main.py 6.5 KB

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