main.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import subprocess
  2. import os
  3. import re
  4. import argparse
  5. from flask import Flask, render_template, redirect, request, url_for
  6. BANDWIDTH_UNITS = [
  7. "bit", # Bits per second
  8. "kbit", # Kilobits per second
  9. "mbit", # Megabits per second
  10. "gbit", # Gigabits per second
  11. "tbit", # Terabits per second
  12. "bps", # Bytes per second
  13. "kbps", # Kilobytes per second
  14. "mbps", # Megabytes per second
  15. "gbps", # Gigabytes per second
  16. "tbps" # Terabytes per second
  17. ]
  18. STANDARD_UNIT = "mbit"
  19. app = Flask(__name__)
  20. pattern = None
  21. dev_list = None
  22. def parse_arguments():
  23. parser = argparse.ArgumentParser(description='TC web GUI')
  24. parser.add_argument('--ip', type=str, required=False,
  25. help='The IP where the server is listening')
  26. parser.add_argument('--port', type=int, required=False,
  27. help='The port where the server is listening')
  28. parser.add_argument('--dev', type=str, nargs='*', required=False,
  29. help='The interfaces to restrict to')
  30. parser.add_argument('--regex', type=str, required=False,
  31. help='A regex to match interfaces')
  32. parser.add_argument('--debug', action='store_true',
  33. help='Run Flask in debug mode')
  34. return parser.parse_args()
  35. @app.route("/")
  36. def main():
  37. rules = get_active_rules()
  38. return render_template('main.html', rules=rules, units=BANDWIDTH_UNITS,
  39. standard_unit=STANDARD_UNIT)
  40. @app.route('/new_rule/<interface>', methods=['POST'])
  41. def new_rule(interface):
  42. delay = request.form['Delay']
  43. delay_variance = request.form['DelayVariance']
  44. loss = request.form['Loss']
  45. loss_correlation = request.form['LossCorrelation']
  46. duplicate = request.form['Duplicate']
  47. reorder = request.form['Reorder']
  48. reorder_correlation = request.form['ReorderCorrelation']
  49. corrupt = request.form['Corrupt']
  50. limit = request.form['Limit']
  51. rate = request.form['Rate']
  52. rate_unit = request.form['rate_unit']
  53. # remove old setup
  54. command = 'tc qdisc del dev %s root netem' % interface
  55. command = command.split(' ')
  56. proc = subprocess.Popen(command)
  57. proc.wait()
  58. # apply new setup
  59. command = 'tc qdisc add dev %s root netem' % interface
  60. if rate != '':
  61. command += ' rate %s%s' % (rate, rate_unit)
  62. if delay != '':
  63. command += ' delay %sms' % delay
  64. if delay_variance != '':
  65. command += ' %sms' % delay_variance
  66. if loss != '':
  67. command += ' loss %s%%' % loss
  68. if loss_correlation != '':
  69. command += ' %s%%' % loss_correlation
  70. if duplicate != '':
  71. command += ' duplicate %s%%' % duplicate
  72. if reorder != '':
  73. command += ' reorder %s%%' % reorder
  74. if reorder_correlation != '':
  75. command += ' %s%%' % reorder_correlation
  76. if corrupt != '':
  77. command += ' corrupt %s%%' % corrupt
  78. if limit != '':
  79. command += ' limit %s' % limit
  80. print(command)
  81. command = command.split(' ')
  82. proc = subprocess.Popen(command)
  83. proc.wait()
  84. return redirect(url_for('main'))
  85. @app.route('/remove_rule/<interface>', methods=['POST'])
  86. def remove_rule(interface):
  87. # remove old setup
  88. command = 'tc qdisc del dev %s root netem' % interface
  89. command = command.split(' ')
  90. proc = subprocess.Popen(command)
  91. proc.wait()
  92. return redirect(url_for('main'))
  93. def get_active_rules():
  94. proc = subprocess.Popen(['tc', 'qdisc'], stdout=subprocess.PIPE)
  95. output = proc.communicate()[0].decode()
  96. lines = output.split('\n')[:-1]
  97. rules = []
  98. dev = set()
  99. for line in lines:
  100. arguments = line.split()
  101. rule = parse_rule(arguments)
  102. if rule['name'] and rule['name'] not in dev:
  103. rules.append(rule)
  104. dev.add(rule['name'])
  105. return rules
  106. def parse_rule(split_rule):
  107. rule = {'name': None,
  108. 'rate': None,
  109. 'delay': None,
  110. 'delayVariance': None,
  111. 'loss': None,
  112. 'lossCorrelation': None,
  113. 'duplicate': None,
  114. 'reorder': None,
  115. 'reorderCorrelation': None,
  116. 'corrupt': None,
  117. 'limit': None}
  118. i = 0
  119. for argument in split_rule:
  120. if argument == 'dev':
  121. # Both regex pattern and dev name can be given
  122. # An interface could match the pattern and/or
  123. # be in the interface list
  124. if pattern is None and dev_list is None:
  125. rule['name'] = split_rule[i + 1]
  126. if pattern:
  127. if pattern.match(split_rule[i + 1]):
  128. rule['name'] = split_rule[i + 1]
  129. if dev_list:
  130. if split_rule[i + 1] in dev_list:
  131. rule['name'] = split_rule[i + 1]
  132. elif argument == 'rate':
  133. rule['rate'] = split_rule[i + 1].split('Mbit')[0]
  134. elif argument == 'delay':
  135. rule['delay'] = split_rule[i + 1]
  136. if len(split_rule) > (i + 2) and 'ms' in split_rule[i + 2]:
  137. rule['delayVariance'] = split_rule[i + 2]
  138. elif argument == 'loss':
  139. rule['loss'] = split_rule[i + 1]
  140. if len(split_rule) > (i + 2) and '%' in split_rule[i + 2]:
  141. rule['lossCorrelation'] = split_rule[i + 2]
  142. elif argument == 'duplicate':
  143. rule['duplicate'] = split_rule[i + 1]
  144. elif argument == 'reorder':
  145. rule['reorder'] = split_rule[i + 1]
  146. if len(split_rule) > (i + 2) and '%' in split_rule[i + 2]:
  147. rule['reorderCorrelation'] = split_rule[i + 2]
  148. elif argument == 'corrupt':
  149. rule['corrupt'] = split_rule[i + 1]
  150. elif argument == 'limit':
  151. rule['limit'] = split_rule[i + 1]
  152. i += 1
  153. return rule
  154. if __name__ == "__main__":
  155. if os.geteuid() != 0:
  156. print("You need to have root privileges to run this script.\n"
  157. "Please try again, this time using 'sudo'. Exiting.")
  158. exit(1)
  159. # TC Variables
  160. args = parse_arguments()
  161. pattern = os.environ.get("TCGUI_REGEX")
  162. if args.regex:
  163. pattern = re.compile(args.regex)
  164. dev_list = os.environ.get("TCGUI_DEV")
  165. if args.dev:
  166. dev_list = args.dev
  167. # Flask Variable
  168. app_args = {}
  169. app_args['host'] = os.environ.get("TCGUI_IP")
  170. app_args['port'] = os.environ.get("TCGUI_PORT")
  171. if args.ip:
  172. app_args['host'] = args.ip
  173. if args.port:
  174. app_args['port'] = args.port
  175. if not args.debug:
  176. app_args['debug'] = False
  177. app.debug = True
  178. app.run(**app_args)