main.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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. rate = request.form['Rate']
  51. rate_unit = request.form['rate_unit']
  52. # remove old setup
  53. command = 'tc qdisc del dev %s root netem' % interface
  54. command = command.split(' ')
  55. proc = subprocess.Popen(command)
  56. proc.wait()
  57. # apply new setup
  58. command = 'tc qdisc add dev %s root netem' % interface
  59. if rate != '':
  60. command += ' rate %s%s' % (rate, rate_unit)
  61. if delay != '':
  62. command += ' delay %sms' % delay
  63. if delay_variance != '':
  64. command += ' %sms' % delay_variance
  65. if loss != '':
  66. command += ' loss %s%%' % loss
  67. if loss_correlation != '':
  68. command += ' %s%%' % loss_correlation
  69. if duplicate != '':
  70. command += ' duplicate %s%%' % duplicate
  71. if reorder != '':
  72. command += ' reorder %s%%' % reorder
  73. if reorder_correlation != '':
  74. command += ' %s%%' % reorder_correlation
  75. if corrupt != '':
  76. command += ' corrupt %s%%' % corrupt
  77. print(command)
  78. command = command.split(' ')
  79. proc = subprocess.Popen(command)
  80. proc.wait()
  81. return redirect(url_for('main'))
  82. @app.route('/remove_rule/<interface>', methods=['POST'])
  83. def remove_rule(interface):
  84. # remove old setup
  85. command = 'tc qdisc del dev %s root netem' % interface
  86. command = command.split(' ')
  87. proc = subprocess.Popen(command)
  88. proc.wait()
  89. return redirect(url_for('main'))
  90. def get_active_rules():
  91. proc = subprocess.Popen(['tc', 'qdisc'], stdout=subprocess.PIPE)
  92. output = proc.communicate()[0].decode()
  93. lines = output.split('\n')[:-1]
  94. rules = []
  95. dev = set()
  96. for line in lines:
  97. arguments = line.split()
  98. rule = parse_rule(arguments)
  99. if rule['name'] and rule['name'] not in dev:
  100. rules.append(rule)
  101. dev.add(rule['name'])
  102. return rules
  103. def parse_rule(split_rule):
  104. rule = {'name': None,
  105. 'rate': None,
  106. 'delay': None,
  107. 'delayVariance': None,
  108. 'loss': None,
  109. 'lossCorrelation': None,
  110. 'duplicate': None,
  111. 'reorder': None,
  112. 'reorderCorrelation': None,
  113. 'corrupt': None}
  114. i = 0
  115. for argument in split_rule:
  116. if argument == 'dev':
  117. # Both regex pattern and dev name can be given
  118. # An interface could match the pattern and/or
  119. # be in the interface list
  120. if pattern is None and dev_list is None:
  121. rule['name'] = split_rule[i + 1]
  122. if pattern:
  123. if pattern.match(split_rule[i + 1]):
  124. rule['name'] = split_rule[i + 1]
  125. if dev_list:
  126. if split_rule[i + 1] in dev_list:
  127. rule['name'] = split_rule[i + 1]
  128. elif argument == 'rate':
  129. rule['rate'] = split_rule[i + 1].split('Mbit')[0]
  130. elif argument == 'delay':
  131. rule['delay'] = split_rule[i + 1]
  132. if len(split_rule) > (i + 2) and 'ms' in split_rule[i + 2]:
  133. rule['delayVariance'] = split_rule[i + 2]
  134. elif argument == 'loss':
  135. rule['loss'] = split_rule[i + 1]
  136. if len(split_rule) > (i + 2) and '%' in split_rule[i + 2]:
  137. rule['lossCorrelation'] = split_rule[i + 2]
  138. elif argument == 'duplicate':
  139. rule['duplicate'] = split_rule[i + 1]
  140. elif argument == 'reorder':
  141. rule['reorder'] = split_rule[i + 1]
  142. if len(split_rule) > (i + 2) and '%' in split_rule[i + 2]:
  143. rule['reorderCorrelation'] = split_rule[i + 2]
  144. elif argument == 'corrupt':
  145. rule['corrupt'] = split_rule[i + 1]
  146. i += 1
  147. return rule
  148. if __name__ == "__main__":
  149. if os.geteuid() != 0:
  150. print("You need to have root privileges to run this script.\n"
  151. "Please try again, this time using 'sudo'. Exiting.")
  152. exit(1)
  153. args = parse_arguments()
  154. if args.regex:
  155. pattern = re.compile(args.regex)
  156. if args.dev:
  157. dev_list = args.dev
  158. app_args = {}
  159. if args.ip:
  160. app_args['host'] = args.ip
  161. if args.port:
  162. app_args['port'] = args.port
  163. if not args.debug:
  164. app_args['debug'] = False
  165. app.debug = True
  166. app.run(**app_args)