main.py 5.5 KB

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