├── .gitignore ├── README.rst ├── __init__.py ├── config ├── topo │ ├── topo_1 │ ├── topo_2 │ ├── topo_3 │ ├── topo_4 │ ├── topo_5 │ └── topo_cong └── xp │ ├── ab │ ├── https │ ├── iperf │ ├── none │ ├── ping │ └── sirimsg ├── core ├── __init__.py ├── experiment.py ├── parameter.py └── topo.py ├── experiments ├── __init__.py ├── ab.py ├── ditg.py ├── epload.py ├── http.py ├── https.py ├── iperf.py ├── iperf_scenario.py ├── msg.py ├── nc.py ├── ncpv.py ├── netperf.py ├── none.py ├── ping.py ├── pquic.py ├── quic.py ├── quic_siri.py ├── send_file.py ├── siri.py ├── siri_http.py ├── siri_msg.py └── vlc.py ├── mininet_builder.py ├── runner.py ├── topos ├── __init__.py ├── multi_interface.py └── multi_interface_multi_client.py └── utils ├── SiriClient.java ├── __init__.py ├── https_server.py ├── msg_client.py ├── msg_server.py ├── server.pem ├── siriClient.jar └── siri_server.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pdf 2 | *.aux 3 | *.backup 4 | *.log 5 | *.bbl 6 | *.out 7 | *.blg 8 | *.pyc 9 | *~ 10 | *.swp 11 | *.pcap 12 | *.xpl 13 | *.patch 14 | __pycache__ -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | What ? 2 | ====== 3 | 4 | Simple tool, based on `mininet `_, to boot a simple network 5 | with n paths and run experiments between two hosts. 6 | 7 | 8 | Usage 9 | ===== 10 | 11 | .. code-block:: console 12 | 13 | ./mpPerf -t topo -x xp 14 | 15 | The format for the topo file and xp file is simple but could be different based 16 | on the type of topo or experiments. Details should follow. 17 | 18 | basic Example 19 | ============= 20 | 21 | 1. Get the CLI 22 | -------------- 23 | 24 | .. code-block:: console 25 | 26 | ./mpPerf -t conf/topo/simple_para 27 | 28 | The content of simple_para is: 29 | 30 | .. code-block:: console 31 | 32 | desc:Simple configuration with two para link 33 | topoType:MultiIf 34 | leftSubnet:10.0. 35 | rightSubnet:10.1. 36 | #path_x:delay,queueSize(may be calc),bw 37 | path_0:10,10,5 38 | path_1:40,40,5 39 | path_2:30,30,2 40 | path_3:20,20,1 41 | 42 | ``topoType`` just specifies that we want to have multiple interfaces, one for 43 | each path. 44 | 45 | Each path is defined by 3 values, delay (one way, int, in ms), queue_size (int, 46 | in packets), and bandwidth (float, in mbit/s). 47 | 48 | Once the configuration is up, you have access to the CLI. You can check route 49 | configuration (policy routing etc.) Just by issuing regular commands preceded 50 | by ``Client`` or ``Server`` 51 | 52 | 2. Simple experiment 53 | -------------------- 54 | 55 | .. code-block:: console 56 | 57 | ./mpPerf -t conf/topo/simple_para -x conf/xp/4_nc 58 | 59 | This command will start the same topology and run the experiment defined by 4_nc 60 | The result for this experiment is a simple pcap file. 61 | 62 | They are other options and experiments, but the documentation is still to be 63 | written. 64 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qdeconinck/minitopo/fac7b51d4ceb1fb59c409becb2ed520cb11421db/__init__.py -------------------------------------------------------------------------------- /config/topo/topo_1: -------------------------------------------------------------------------------- 1 | leftSubnet:10.0. 2 | rightSubnet:10.1. 3 | path_c2r_0:10,10,4 4 | path_c2r_1:40,30,4 5 | topoType:MultiIf 6 | -------------------------------------------------------------------------------- /config/topo/topo_2: -------------------------------------------------------------------------------- 1 | leftSubnet:10.0. 2 | rightSubnet:10.1. 3 | path_c2r_0:100,20,4 4 | path_c2r_1:1,20,4 5 | path_r2s_0:10,20,10 6 | topoType:MultiIf 7 | -------------------------------------------------------------------------------- /config/topo/topo_3: -------------------------------------------------------------------------------- 1 | leftSubnet:10.0. 2 | rightSubnet:10.1. 3 | path_c2r_0:100,20,4 4 | path_r2s_0:10,20,10 5 | topoType:MultiIf 6 | -------------------------------------------------------------------------------- /config/topo/topo_4: -------------------------------------------------------------------------------- 1 | leftSubnet:10.0. 2 | rightSubnet:10.1. 3 | path_c2r_0:100,20,4 4 | path_c2r_1:100,20,4 5 | path_c2r_2:100,20,4 6 | path_r2s_0:10,20,10 7 | path_r2s_1:10,20,10 8 | topoType:MultiIf 9 | -------------------------------------------------------------------------------- /config/topo/topo_5: -------------------------------------------------------------------------------- 1 | leftSubnet:10.0. 2 | rightSubnet:10.1. 3 | path_c2r_0:100,20,4 4 | path_c2r_1:100,20,4 5 | path_r2s_0:10,20,10 6 | path_r2s_1:10,20,10 7 | path_r2s_2:10,20,10 8 | topoType:MultiIf 9 | -------------------------------------------------------------------------------- /config/topo/topo_cong: -------------------------------------------------------------------------------- 1 | leftSubnet:10.0. 2 | rightSubnet:10.1. 3 | path_c2r_0:10,10,4 4 | path_c2r_1:40,30,4 5 | topoType:MultiIfMultiClient 6 | -------------------------------------------------------------------------------- /config/xp/ab: -------------------------------------------------------------------------------- 1 | xpType:ab 2 | -------------------------------------------------------------------------------- /config/xp/https: -------------------------------------------------------------------------------- 1 | xpType:https 2 | clientPcap:yes 3 | kpms:fullmesh 4 | kpmc:fullmesh 5 | rmem:300000 300000 300000 6 | -------------------------------------------------------------------------------- /config/xp/iperf: -------------------------------------------------------------------------------- 1 | #option to dvlp are commented but already there. 2 | #kCommit:aaaa 3 | #pmCommit:bbb 4 | #sysctl:mem,x y z 5 | #sysctl:rmem,x y z 6 | #sysctl:wmem,x y z 7 | xpType:iperf 8 | size:10M 9 | 10 | -------------------------------------------------------------------------------- /config/xp/none: -------------------------------------------------------------------------------- 1 | xpType:none 2 | -------------------------------------------------------------------------------- /config/xp/ping: -------------------------------------------------------------------------------- 1 | xpType:ping 2 | pingCount:16 3 | rmem:87380 87380 87380 4 | -------------------------------------------------------------------------------- /config/xp/sirimsg: -------------------------------------------------------------------------------- 1 | xpType:sirimsg 2 | siriRunTime:10 3 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qdeconinck/minitopo/fac7b51d4ceb1fb59c409becb2ed520cb11421db/core/__init__.py -------------------------------------------------------------------------------- /core/experiment.py: -------------------------------------------------------------------------------- 1 | from .parameter import Parameter 2 | 3 | import logging 4 | 5 | class ExperimentParameter(Parameter): 6 | """ 7 | Handler for experiment parameters stored in configuration files. 8 | The following parameters are common (and thus usable) by all experiments. 9 | If you want to add experiement-specific parameters, you should extend this 10 | class. 11 | 12 | Attribute: 13 | default_parameters Default values for the parameters 14 | """ 15 | RMEM = "rmem" 16 | WMEM = "wmem" 17 | MPTCP_ENABLED = "mptcpEnabled" 18 | SCHED = "sched" 19 | CC = "congctrl" 20 | AUTOCORK = "autocork" 21 | EARLY_RETRANS = "earlyRetrans" 22 | KERNELPM = "kpm" 23 | KERNELPMC = "kpmc" #kernel path manager client / server 24 | KERNELPMS = "kpms" 25 | USERPMC = "upmc" 26 | USERPMS = "upms" #userspace path manager client / server 27 | USERPMC_ARGS = "upmc_args" 28 | USERPMS_ARGS = "upms_args" 29 | CLIENT_PCAP = "clientPcap" 30 | SERVER_PCAP = "serverPcap" 31 | SNAPLEN_PCAP = "snaplen_pcap" 32 | XP_TYPE = "xpType" 33 | PING_COUNT = "pingCount" 34 | PRIO_PATH_0 = "priority_path_0" 35 | PRIO_PATH_1 = "priority_path_1" 36 | BACKUP_PATH_0 = "backup_path_0" 37 | BACKUP_PATH_1 = "backup_path_1" 38 | BUFFER_AUTOTUNING = "bufferAutotuning" 39 | 40 | # Global sysctl keys 41 | SYSCTL_KEY = { 42 | RMEM: "net.ipv4.tcp_rmem", 43 | WMEM: "net.ipv4.tcp_wmem", 44 | MPTCP_ENABLED: "net.mptcp.mptcp_enabled", 45 | KERNELPM: "net.mptcp.mptcp_path_manager", 46 | SCHED: "net.mptcp.mptcp_scheduler", 47 | CC: "net.ipv4.tcp_congestion_control", 48 | AUTOCORK: "net.ipv4.tcp_autocorking", 49 | EARLY_RETRANS: "net.ipv4.tcp_early_retrans", 50 | BUFFER_AUTOTUNING: "net.ipv4.tcp_moderate_rcvbuf", 51 | } 52 | 53 | # sysctl keys specific to client and server, independently 54 | SYSCTL_KEY_CLIENT = { 55 | KERNELPMC: "net.mptcp.mptcp_path_manager", 56 | } 57 | SYSCTL_KEY_SERVER = { 58 | KERNELPMS: "net.mptcp.mptcp_path_manager", 59 | } 60 | 61 | # Default values for unspecified experiment parameters 62 | DEFAULT_PARAMETERS = { 63 | RMEM: "10240 87380 16777216", 64 | WMEM: "4096 16384 4194304", 65 | MPTCP_ENABLED: "1", 66 | KERNELPM: "fullmesh", 67 | KERNELPMC: "fullmesh", 68 | KERNELPMS: "fullmesh", 69 | USERPMC: "fullmesh", 70 | USERPMS: "fullmesh", 71 | USERPMC_ARGS: "", 72 | USERPMS_ARGS: "", 73 | CC: "olia", 74 | SCHED: "default", 75 | AUTOCORK: "1", 76 | EARLY_RETRANS: "3", 77 | BUFFER_AUTOTUNING: "1", 78 | CLIENT_PCAP: "no", 79 | SERVER_PCAP: "no", 80 | SNAPLEN_PCAP: "65535", # Default snapping value of tcpdump 81 | XP_TYPE: "none", 82 | PING_COUNT: "5", 83 | PRIO_PATH_0: "0", 84 | PRIO_PATH_1: "0", 85 | BACKUP_PATH_0: "0", 86 | BACKUP_PATH_1: "0", 87 | } 88 | 89 | def __init__(self, parameter_filename): 90 | super(ExperimentParameter, self).__init__(parameter_filename) 91 | self.default_parameters.update(ExperimentParameter.DEFAULT_PARAMETERS) 92 | 93 | 94 | class Experiment(object): 95 | """ 96 | Base class to instantiate an experiment to perform. 97 | 98 | This class is not instantiable as it. You must define a child class with the 99 | `NAME` attribute. 100 | 101 | By default, an Experiment relies on an instance of ExperimentParameter to 102 | collect the parameters from the experiment configuration file. However, an 103 | experiment may introduce specific parameters in the configuration file. In 104 | such case, the inherinting class must override the `PARAMETER_CLASS` class 105 | variable to point to another class inheriting from ExperimentParameter. 106 | 107 | Attributes: 108 | experiment_parameter Instance of ExperimentParameter 109 | topo Instance of Topo 110 | topo_config Instance of TopoConfig 111 | """ 112 | PARAMETER_CLASS = ExperimentParameter 113 | 114 | IP_BIN = "ip" 115 | PING_OUTPUT = "ping.log" 116 | 117 | def __init__(self, experiment_parameter_filename, topo, topo_config): 118 | """ 119 | Instantiation of this base class only load the experiment parameter 120 | """ 121 | self.experiment_parameter = self.__class__.PARAMETER_CLASS(experiment_parameter_filename) 122 | self.topo = topo 123 | self.topo_config = topo_config 124 | 125 | def load_parameters(self): 126 | """ 127 | Load the parameter of interest from self.experiment_parameter 128 | """ 129 | # Nothing to do in the base class 130 | pass 131 | 132 | def classic_run(self): 133 | """ 134 | Default function to perform the experiment. It consists into three phases: 135 | - A preparation phase through `prepare()` (generating experiment files,...) 136 | - A running phase through `run()` (where the actual experiment takes place) 137 | - A cleaning phase through `clean()` (stopping traffic, removing generated files,...) 138 | """ 139 | self.prepare() 140 | self.run() 141 | self.clean() 142 | 143 | def prepare(self): 144 | """ 145 | Prepare the environment to run the experiment. 146 | Typically, when you inherit from this class, you want to extend this 147 | method, while still calling this parent function. 148 | 149 | TODO: split experiment traffic and protocol configuration 150 | """ 151 | self.setup_sysctl() 152 | self.run_userspace_path_manager() # TODO to move elsewhere 153 | self.put_priority_on_paths() # TODO to move elsewhere 154 | self.run_tcpdump() 155 | self.run_netem_at() 156 | 157 | def put_priority_on_paths(self): 158 | """ 159 | Function only meaningful for MPTCP 160 | """ 161 | priority_path_0 = self.experiment_parameter.get(ExperimentParameter.PRIO_PATH_0) 162 | priority_path_1 = self.experiment_parameter.get(ExperimentParameter.PRIO_PATH_1) 163 | if not priority_path_0 == priority_path_1: 164 | self.topo.command_to(self.topo_config.client, "{} link set dev {} priority {}".format( 165 | Experiment.IP_BIN, self.topo_config.get_client_interface(0), priority_path_0)) 166 | self.topo.command_to(self.topo_config.router, "{} link set dev {} priority {}".format( 167 | Experiment.IP_BIN, self.topo_config.get_router_interface_to_client_switch(0), priority_path_0)) 168 | self.topo.command_to(self.topo_config.client, "{} link set dev {} priority {}".format( 169 | Experiment.IP_BIN, self.topo_config.get_client_interface(1), priority_path_1)) 170 | self.topo.command_to(self.topo_config.router, "{} link set dev {} priority {}".format( 171 | Experiment.IP_BIN, self.topo_config.get_router_interface_to_client_switch(1), priority_path_1)) 172 | 173 | backup_path_0 = self.experiment_parameter.get(ExperimentParameter.BACKUP_PATH_0) 174 | if int(backup_path_0) > 0: 175 | self.topo.command_to(self.topo_config.client, 176 | self.topo_config.interface_backup_command(self.topo_config.get_client_interface(0))) 177 | self.topo.command_to(self.topo_config.router, 178 | self.topo_config.interface_backup_command(self.topo_config.get_router_interface_to_client_switch(0))) 179 | backup_path_1 = self.experiment_parameter.get(ExperimentParameter.BACKUP_PATH_1) 180 | if int(backup_path_1) > 0: 181 | self.topo.command_to(self.topo_config.client, 182 | self.topo_config.interface_backup_command(self.topo_config.get_client_interface(1))) 183 | self.topo.command_to(self.topo_config.router, 184 | self.topo_config.interface_backup_command(self.topo_config.get_router_interface_to_client_switch(1))) 185 | 186 | def run_userspace_path_manager(self): 187 | """ 188 | Function only meaningful to MPTCP with a specific path manager 189 | """ 190 | if self.experiment_parameter.get(ExperimentParameter.KERNELPMC) == "netlink": 191 | logging.info("Running user-space path manager on client") 192 | upmc = self.experiment_parameter.get(ExperimentParameter.USERPMC) 193 | upmca = self.experiment_parameter.get(ExperimentParameter.USERPMC_ARGS) 194 | self.topo.command_to(self.topo_config.client, "{} {} &>{} &".format( 195 | upmc, upmca, "upmc.log")) 196 | if self.experiment_parameter.get(ExperimentParameter.KERNELPMS) == "netlink": 197 | logging.info("Running user-space path manager on server") 198 | upms = self.experiment_parameter.get(ExperimentParameter.USERPMS) 199 | upmsa = self.experiment_parameter.get(ExperimentParameter.USERPMS_ARGS) 200 | self.topo.command_to(self.topo_config.server, "{} {} &>{} &".format( 201 | upms, upmsa, "upms.log")) 202 | 203 | def clean_userspace_path_manager(self): 204 | if self.experiment_parameter.get(ExperimentParameter.KERNELPMC) == "netlink": 205 | logging.info("Cleaning user-space path manager on client") 206 | upmc = self.experiment_parameter.get(ExperimentParameter.USERPMC) 207 | self.topo.command_to(self.topo_config.client, "killall {}".format(upmc)) 208 | if self.experiment_parameter.get(ExperimentParameter.KERNELPMS) == "netlink": 209 | logging.info("Cleaning user-space path manager on server") 210 | upms = self.experiment_parameter.get(ExperimentParameter.USERPMS) 211 | self.topo.command_to(self.topo_config.client, "killall {}".format(upms)) 212 | 213 | def run_netem_at(self): 214 | self.topo_config.run_netem_at() 215 | 216 | def run(self): 217 | """ 218 | Perform the experiment 219 | 220 | This function MUST be overriden by child classes 221 | """ 222 | raise NotImplementedError("Trying to run Experiment") 223 | 224 | def clean(self): 225 | """ 226 | Clean the environment where the experiment took place. 227 | Typically, when you inherit from this class, you want to extend this 228 | method, while still calling this parent function. 229 | """ 230 | self.topo.command_to(self.topo_config.client, "killall tcpdump") 231 | self.topo.command_to(self.topo_config.server, "killall tcpdump") 232 | self.restore_sysctl() 233 | self.clean_userspace_path_manager() 234 | 235 | def setup_sysctl(self): 236 | """ 237 | Record the current sysctls of the host and write the experiment ones 238 | """ 239 | self.save_sysctl() 240 | self.write_sysctl() 241 | 242 | def save_sysctl(self): 243 | """ 244 | Record the current sysctls 245 | """ 246 | self.sysctl_to_restore = {} 247 | self._save_sysctl(ExperimentParameter.SYSCTL_KEY, self.sysctl_to_restore) 248 | self.client_sysctl_to_restore = {} 249 | self._save_sysctl(ExperimentParameter.SYSCTL_KEY_CLIENT, self.client_sysctl_to_restore, 250 | ns=True, who=self.topo_config.client) 251 | self.server_sysctl_to_restore = {} 252 | self._save_sysctl(ExperimentParameter.SYSCTL_KEY_SERVER, self.server_sysctl_to_restore, 253 | ns=True, who=self.topo_config.server) 254 | 255 | def _save_sysctl(self, sysctl_dict, sysctl_to_restore, ns=False, who=None): 256 | for k in sysctl_dict: 257 | sysctl_key = sysctl_dict[k] 258 | cmd = self.read_sysctl_cmd(sysctl_key) 259 | if not ns: 260 | val = self.topo.command_global(cmd) 261 | else: 262 | val = self.topo.command_to(who, cmd) 263 | if val == "Error": 264 | logging.error("unable to get sysctl {}".format(sysctl_key)) 265 | else: 266 | # For Python3 compatibility 267 | if type(val) is bytes: 268 | val = val.decode() 269 | sysctl_to_restore[k] = val.split(" ", 2)[2][:-1] 270 | 271 | def read_sysctl_cmd(self, key): 272 | """ 273 | Return a bash command to read the sysctl key `key` 274 | """ 275 | return "sysctl {}".format(key) 276 | 277 | def cmd_write_sysctl(self, key, value): 278 | """ 279 | Return a bash command to write the sysctl key `key`with value `value` 280 | """ 281 | return '{}="{}"'.format(self.read_sysctl_cmd(key), value) 282 | 283 | def write_sysctl(self): 284 | """ 285 | Write the experiment sysctls 286 | """ 287 | self._write_sysctl(ExperimentParameter.SYSCTL_KEY, self.sysctl_to_restore) 288 | self._write_sysctl(ExperimentParameter.SYSCTL_KEY_CLIENT, self.client_sysctl_to_restore, 289 | ns=True, who=self.topo_config.client) 290 | self._write_sysctl(ExperimentParameter.SYSCTL_KEY_SERVER, self.server_sysctl_to_restore, 291 | ns=True, who=self.topo_config.server) 292 | 293 | def _write_sysctl(self, sysctl_dict, sysctl_to_restore, ns = False, who = None): 294 | for k in sysctl_to_restore: 295 | sysctl_key = sysctl_dict[k] 296 | sysctl_value = self.experiment_parameter.get(k) 297 | cmd = self.cmd_write_sysctl(sysctl_key, sysctl_value) 298 | if not ns: 299 | val = self.topo.command_global(cmd) 300 | else: 301 | val = self.topo.command_to(who, cmd) 302 | if val == "Error": 303 | logging.error("unable to set sysctl {}".format(sysctl_key)) 304 | 305 | def restore_sysctl(self): 306 | """ 307 | Restore back the sysctls that were present before running the experiment 308 | """ 309 | self._restore_sysctl(ExperimentParameter.SYSCTL_KEY, self.sysctl_to_restore) 310 | self._restore_sysctl(ExperimentParameter.SYSCTL_KEY_CLIENT, self.client_sysctl_to_restore, 311 | ns=True, who=self.topo_config.client) 312 | self._restore_sysctl(ExperimentParameter.SYSCTL_KEY_SERVER, self.server_sysctl_to_restore, 313 | ns=True, who=self.topo_config.server) 314 | 315 | def _restore_sysctl(self, sysctl_dict, sysctl_to_restore, ns = False, who = None): 316 | for k in sysctl_to_restore: 317 | sysctl_key = sysctl_dict[k] 318 | sysctl_value = sysctl_to_restore[k] 319 | cmd = self.cmd_write_sysctl(sysctl_key, sysctl_value) 320 | if not ns: 321 | val = self.topo.command_global(cmd) 322 | else: 323 | val = self.topo.command_to(who, cmd) 324 | 325 | if val == "Error": 326 | logging.error("unable to set sysctl {}".format(sysctl_key)) 327 | 328 | def run_tcpdump(self): 329 | client_pcap = self.experiment_parameter.get(ExperimentParameter.CLIENT_PCAP) 330 | server_pcap = self.experiment_parameter.get(ExperimentParameter.SERVER_PCAP) 331 | snaplen_pcap = self.experiment_parameter.get(ExperimentParameter.SNAPLEN_PCAP) 332 | if client_pcap == "yes": 333 | self.topo.command_to(self.topo_config.client, 334 | "tcpdump -i any -s {} -w client.pcap &".format(snaplen_pcap)) 335 | if server_pcap == "yes": 336 | self.topo.command_to(self.topo_config.server, 337 | "tcpdump -i any -s {} -w server.pcap &".format(snaplen_pcap)) 338 | if server_pcap == "yes" or client_pcap == "yes": 339 | logging.info("Activating tcpdump, waiting for it to run") 340 | self.topo.command_to(self.topo_config.client,"sleep 5") 341 | 342 | def ping(self): 343 | self.topo.command_to(self.topo_config.client, 344 | "rm {}".format(Experiment.PING_OUTPUT)) 345 | count = self.experiment_parameter.get(ExperimentParameter.PING_COUNT) 346 | for j in range(0, self.topo_config.server_interface_count()): 347 | for i in range(0, self.topo_config.client_interface_count()): 348 | cmd = self.ping_command(self.topo_config.get_client_ip(i), 349 | self.topo_config.get_server_ip(interface_index=j), n=count) 350 | logging.info(cmd) 351 | self.topo.command_to(self.topo_config.client, cmd) 352 | 353 | def ping_command(self, from_ip, to_ip, n=5): 354 | return "ping -c {} -I {} {} >> {}".format(n, from_ip, to_ip, Experiment.PING_OUTPUT) 355 | 356 | 357 | class RandomFileParameter(ExperimentParameter): 358 | """ 359 | Parameters for the RandomFileExperiment 360 | """ 361 | FILE = "file" # file to fetch; if random, we create a file with random data called random. 362 | RANDOM_SIZE = "file_size" # in KB 363 | 364 | def __init__(self, experiment_parameter_filename): 365 | super(RandomFileParameter, self).__init__(experiment_parameter_filename) 366 | self.default_parameters.update({ 367 | RandomFileParameter.FILE: "random", 368 | RandomFileParameter.RANDOM_SIZE: "1024", 369 | }) 370 | 371 | 372 | class RandomFileExperiment(Experiment): 373 | """ 374 | Enable a experiment to use random files 375 | 376 | This class is not directly instantiable 377 | """ 378 | PARAMETER_CLASS = RandomFileParameter 379 | 380 | def __init__(self, experiment_parameter_filename, topo, topo_config): 381 | super(RandomFileExperiment, self).__init__(experiment_parameter_filename, topo, topo_config) 382 | self.load_parameters() 383 | self.ping() 384 | 385 | def load_parameters(self): 386 | super(RandomFileExperiment, self).load_parameters() 387 | self.file = self.experiment_parameter.get(RandomFileParameter.FILE) 388 | self.random_size = self.experiment_parameter.get(RandomFileParameter.RANDOM_SIZE) 389 | 390 | def prepare(self): 391 | super(RandomFileExperiment, self).prepare() 392 | if self.file == "random": 393 | self.topo.command_to(self.topo_config.client, 394 | "dd if=/dev/urandom of=random bs=1K count={}".format(self.random_size)) 395 | 396 | def clean(self): 397 | super(RandomFileExperiment, self).clean() 398 | if self.file == "random": 399 | self.topo.command_to(self.topo_config.client, "rm random*") 400 | -------------------------------------------------------------------------------- /core/parameter.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | class Parameter(object): 4 | """ 5 | Generic handler for parameters stored in configuration files 6 | 7 | Attributes: 8 | parameters dictionary containing the value for configuration parameters 9 | default_parameters dictionary containung default values for parameters 10 | """ 11 | def __init__(self, parameter_filename): 12 | self.parameters = {} 13 | self.default_parameters = {} 14 | if parameter_filename is None: 15 | logging.warning("No parameter file provided; using default parameters") 16 | else: 17 | logging.info("Extract parameters from file {}".format(parameter_filename)) 18 | self.load_parameter_file(parameter_filename) 19 | 20 | def load_parameter_file(self, parameter_filename): 21 | with open(parameter_filename) as f: 22 | for line in f.readlines(): 23 | # Ignore comments 24 | if line.startswith("#"): 25 | continue 26 | 27 | try: 28 | # Also get rid of trailing characters 29 | key, value = line.strip().split(":") 30 | if key in self.parameters: 31 | if not isinstance(self.parameters[key], list): 32 | self.parameters[key] = [self.parameters[key]] 33 | self.parameters[key].append(value) 34 | else: 35 | self.parameters[key] = value 36 | except ValueError as e: 37 | logging.warning( 38 | "Got error '{}' for line '{}'; ignore it".format(e, line)) 39 | 40 | def get(self, key): 41 | """ 42 | Get the value of the parameter with key `key`. 43 | If not defined by the configuration file, return the default value. 44 | Raise Exception if the parameter has no default value and is absent. 45 | """ 46 | val = self.parameters.get(key) 47 | if val is None: 48 | if key in self.default_parameters: 49 | return self.default_parameters[key] 50 | else: 51 | raise Exception("Parameter not found " + key) 52 | else: 53 | return val 54 | 55 | def __str__(self): 56 | return self.parameters.__str__() 57 | -------------------------------------------------------------------------------- /core/topo.py: -------------------------------------------------------------------------------- 1 | from .parameter import Parameter 2 | 3 | import logging 4 | import math 5 | 6 | 7 | class NetemAt(object): 8 | """ 9 | Class representing a netem command to be run after some time 10 | """ 11 | def __init__(self, at, cmd): 12 | self.at = at 13 | self.cmd = cmd 14 | self.delta = 0 15 | 16 | def __str__(self): 17 | return "netem at {} ({}) will be {}".format(self.at, self.delta, self.cmd) 18 | 19 | 20 | def get_bandwidth_delay_product_divided_by_mtu(delay, bandwidth): 21 | """ 22 | With delay in ms, bandwidth in Mbps 23 | """ 24 | rtt = 2 * float(delay) 25 | bandwidth_delay_product = (float(bandwidth) * 125000.0) * (rtt / 1000.0) 26 | return int(math.ceil(bandwidth_delay_product * 1.0 / 1500.0)) 27 | 28 | 29 | class LinkCharacteristics(object): 30 | """ 31 | Network characteristics associated to a link 32 | 33 | Attributes: 34 | id the identifier of the link 35 | link_type type of the link 36 | delay the one-way delay introduced by the link in ms 37 | queue_size the size of the link buffer, in packets 38 | bandwidth the bandwidth of the link in Mbps 39 | loss the random loss rate in percentage 40 | queuing_delay the maximum time that a packet can stay in the link buffer (computed over queue_size) 41 | netem_at list of NetemAt instances applicable to the link 42 | backup integer indicating if this link is a backup one or not (useful for MPTCP) 43 | """ 44 | def __init__(self, id, link_type, delay, queue_size, bandwidth, loss, backup=0): 45 | self.id = id 46 | self.link_type = link_type 47 | self.delay = delay 48 | self.queue_size = queue_size 49 | self.bandwidth = bandwidth 50 | self.loss = loss 51 | self.queuing_delay = str(self.extract_queuing_delay(queue_size, bandwidth, delay)) 52 | self.netem_at = [] 53 | self.backup = backup 54 | 55 | def bandwidth_delay_product_divided_by_mtu(self): 56 | """ 57 | Get the bandwidth-delay product in terms of packets (hence, dividing by the MTU) 58 | """ 59 | return get_bandwidth_delay_product_divided_by_mtu(self.delay, self.bandwidth) 60 | 61 | def buffer_size(self): 62 | """ 63 | Return the buffer size in bytes 64 | """ 65 | return (1500.0 * self.bandwidth_delay_product_divided_by_mtu()) + \ 66 | (float(self.bandwidth) * 1000.0 * float(self.queuing_delay) / 8) 67 | 68 | def extract_queuing_delay(self, queue_size, bandwidth, delay, mtu=1500): 69 | queuing_delay = (int(queue_size) * int(mtu) * 8.0 * 1000.0) / \ 70 | (float(bandwidth) * 1024 * 1024) 71 | return max(int(queuing_delay), 1) 72 | 73 | def add_netem_at(self, n): 74 | if len(self.netem_at) == 0: 75 | n.delta = n.at 76 | self.netem_at.append(n) 77 | else: 78 | if n.at > self.netem_at[-1].at: 79 | n.delta = n.at - self.netem_at[-1].at 80 | self.netem_at.append(n) 81 | else: 82 | logging.error("{}: not taken into account because not specified in order in the topo param file".format(n)) 83 | 84 | def build_delete_tc_cmd(self, ifname): 85 | return "tc qdisc del dev {} root; tc qdisc del dev {} ingress ".format(ifname, ifname) 86 | 87 | def build_bandwidth_cmd(self, ifname, replace=False): 88 | return "tc qdisc {} dev {} root handle 1:0 tbf rate {}mbit burst 15000 limit {}".format( 89 | "replace" if replace else "add", ifname, self.bandwidth, self.buffer_size()) 90 | 91 | def build_changing_bandwidth_cmd(self, ifname): 92 | return "&& ".join( 93 | ["sleep {} && ({}) ".format( 94 | n.delta, self.build_bandwidth_cmd(ifname, replace=True)) for n in self.netem_at] 95 | + ["true &"] 96 | ) 97 | 98 | def build_netem_cmd(self, ifname, cmd, replace=False): 99 | return "tc qdisc {} dev {} root handle 10: netem {} {}".format( 100 | "replace" if replace else "add", ifname, cmd, "delay {}ms limit 50000".format(self.delay) if not replace else "") 101 | 102 | def build_changing_netem_cmd(self, ifname): 103 | return "&& ".join( 104 | ["sleep {} && {} ".format( 105 | n.delta, self.build_netem_cmd(ifname, n.cmd, replace=True)) for n in self.netem_at] 106 | + ["true &"] 107 | ) 108 | 109 | def as_dict(self): 110 | """ 111 | Notably used by BottleneckLink 112 | """ 113 | return { 114 | "link_id": self.id, 115 | "link_type": self.link_type, 116 | "bw": float(self.bandwidth), 117 | "delay": "{}ms".format(self.delay), 118 | "loss": float(self.loss), 119 | "max_queue_size": int(self.queue_size) 120 | } 121 | 122 | def __str__(self): 123 | return """ 124 | Link type: {} 125 | Link id: {} 126 | Delay: {} 127 | Queue Size: {} 128 | Bandwidth: {} 129 | Loss: {} 130 | Backup: {} 131 | """.format(self.link_type, self.id, self.delay, self.queue_size, self.bandwidth, self.loss, self.backup) + \ 132 | "".join(["\t {} \n".format(n) for n in self.netem_at]) 133 | 134 | 135 | class TopoParameter(Parameter): 136 | LEFT_SUBNET = "leftSubnet" 137 | RIGHT_SUBNET = "rightSubnet" 138 | NETEM_AT = "netemAt_" 139 | CHANGE_NETEM = "changeNetem" 140 | 141 | DEFAULT_PARAMETERS = { 142 | LEFT_SUBNET: "10.1.", 143 | RIGHT_SUBNET: "10.2.", 144 | CHANGE_NETEM: "false", 145 | } 146 | 147 | def __init__(self, parameter_filename): 148 | Parameter.__init__(self, parameter_filename) 149 | self.default_parameters.update(TopoParameter.DEFAULT_PARAMETERS) 150 | self.link_characteristics = [] 151 | self.load_link_characteristics() 152 | self.load_netem_at() 153 | logging.info(self) 154 | 155 | def parse_netem_at(self, key): 156 | """ 157 | Parse key of the form netemAt_{link_type}_{link_id} 158 | 159 | Return link_type, link_id 160 | """ 161 | _, link_type, link_id = key.split("_") 162 | return link_type, int(link_id) 163 | 164 | def load_netem_at(self): 165 | if not self.get(TopoParameter.CHANGE_NETEM) == "yes": 166 | return 167 | for k in sorted(self.parameters): 168 | if k.startswith(TopoParameter.NETEM_AT): 169 | link_type, link_id = self.parse_netem_at(k) 170 | self.load_netem_at_value(link_type, link_id, self.parameters[k]) 171 | 172 | def find_link_characteristic(self, link_type, link_id): 173 | for l in self.link_characteristics: 174 | if l.link_type == link_type and l.id == link_id: 175 | return l 176 | 177 | return ValueError("No link with link_type {} and link_id {}".format(link_type, link_id)) 178 | 179 | def load_netem_at_value(self, link_type, link_id, n): 180 | try: 181 | at, cmd = n.split(",") 182 | na = NetemAt(float(at), cmd) 183 | l = self.find_link_characteristic(link_type, link_id) 184 | l.add_netem_at(na) 185 | 186 | except ValueError as e: 187 | logging.error("Unable to set netem for link {} with command {}: {}".format(link_id, n, e)) 188 | 189 | logging.info(self.link_characteristics[link_id].netem_at) 190 | 191 | def parse_link_id_and_type(self, key): 192 | """ 193 | The key of a path must have the following format: 194 | path_{link_type}_{ID} 195 | 196 | Note that several links can have the same ID, several links can have the same 197 | link_type, but the tuple (link_type, ID) is unique. 198 | """ 199 | _, link_type, link_id = key.split("_") 200 | return link_type, int(link_id) 201 | 202 | def parse_link_characteristics(self, value): 203 | """ 204 | The format of a link characteristic is one of the following: 205 | - "{delay},{queue_size},{bandwidth},{loss_perc},{is_backup}" 206 | - "{delay},{queue_size},{bandwidth},{loss_perc}" 207 | - "{delay},{queue_size},{bandwidth}" 208 | - "{delay},{bandwidth}" 209 | 210 | When not specified, default values are the following: 211 | - queue_size: get_bandwidth_delay_product_divided_by_mtu(delay, bandwidth) 212 | - loss_perc: 0 213 | - is_backup: 0 214 | 215 | Return 216 | delay, bandwidth, queue_size, loss_perc, is_backup 217 | """ 218 | loss_perc, is_backup = 0.0, 0 219 | c = value.split(",") 220 | if len(c) == 2: 221 | delay, bw = float(c[0]), float(c[1]) 222 | return delay, bw, get_bandwidth_delay_product_divided_by_mtu(delay, bw), loss_perc, is_backup 223 | if len(c) == 3: 224 | return float(c[0]), float(c[2]), int(c[1]), loss_perc, is_backup 225 | if len(c) == 4: 226 | return float(c[0]), float(c[2]), int(c[1]), float(c[3]), is_backup 227 | if len(c) == 5: 228 | return float(c[0]), float(c[2]), int(c[1]), float(c[3]), int(c[4]) 229 | 230 | raise ValueError("Invalid link characteristics: {}".format(value)) 231 | 232 | def load_link_characteristics(self): 233 | """ 234 | Load the path characteristics 235 | """ 236 | for k in sorted(self.parameters): 237 | if k.startswith("path"): 238 | try: 239 | link_type, link_id = self.parse_link_id_and_type(k) 240 | delay, bw, queue_size, loss_perc, is_backup = self.parse_link_characteristics( 241 | self.parameters[k]) 242 | except ValueError as e: 243 | logging.error("Ignored path {}: {}".format(k, e)) 244 | else: 245 | path = LinkCharacteristics(link_id, link_type, delay, queue_size, 246 | bw, loss_perc, backup=is_backup) 247 | self.link_characteristics.append(path) 248 | 249 | def __str__(self): 250 | s = "{}".format(super(TopoParameter, self).__str__()) 251 | s += "".join(["{}".format(lc) for lc in self.link_characteristics]) 252 | return s 253 | 254 | 255 | class BottleneckLink(object): 256 | """ 257 | Representation of a bottleneck link having limited bandwidth, a buffer, 258 | experiencing propagation delay and introducing packet losses. 259 | 260 | A bottleneck link has the following actual representation: 261 | 262 | bs0 -- bs1 -- bs2 -- bs3 263 | 264 | Where bs0 (resp. bs3) is the left (resp. right) side of the link, and having 265 | TC commands for the packet flow s0 -> s3 as follows: 266 | - Policing command to implement buffer on ingress of bs1 from bs0 267 | - Shaping command to implement bandwidth on egress of bs1 to bs2 268 | - Netem command to implement delay and loss on egress of bs2 to bs3 269 | """ 270 | BOTTLENECK_SWITCH_NAME_PREFIX = "bs" 271 | 272 | def __init__(self, topo_builder, topo, link_characteristics): 273 | self.link_characteristics = link_characteristics 274 | self.topo = topo 275 | self.bs0 = topo_builder.add_switch(self.get_bs_name(0)) 276 | self.bs1 = topo_builder.add_switch(self.get_bs_name(1)) 277 | self.bs2 = topo_builder.add_switch(self.get_bs_name(2)) 278 | self.bs3 = topo_builder.add_switch(self.get_bs_name(3)) 279 | topo_builder.add_link(self.bs0, self.bs1) 280 | topo_builder.add_link(self.bs1, self.bs2) 281 | topo_builder.add_link(self.bs2, self.bs3) 282 | 283 | def get_bs_name(self, index): 284 | return "{}_{}_{}_{}".format(BottleneckLink.BOTTLENECK_SWITCH_NAME_PREFIX, 285 | self.link_characteristics.link_type, self.link_characteristics.id, index) 286 | 287 | def reinit_variables(self): 288 | # Required to retrieve actual nodes 289 | self.bs0 = self.topo.get_host(self.get_bs_name(0)) 290 | self.bs1 = self.topo.get_host(self.get_bs_name(1)) 291 | self.bs2 = self.topo.get_host(self.get_bs_name(2)) 292 | self.bs3 = self.topo.get_host(self.get_bs_name(3)) 293 | 294 | def configure_bottleneck(self): 295 | bs1_interface_names = self.topo.get_interface_names(self.bs1) 296 | bs2_interface_names = self.topo.get_interface_names(self.bs2) 297 | 298 | # Cleanup tc commands 299 | for bs1_ifname in bs1_interface_names: 300 | clean_cmd = self.link_characteristics.build_delete_tc_cmd(bs1_ifname) 301 | logging.info(clean_cmd) 302 | self.topo.command_to(self.bs1, clean_cmd) 303 | 304 | for bs2_ifname in bs2_interface_names: 305 | clean_cmd = self.link_characteristics.build_delete_tc_cmd(bs2_ifname) 306 | logging.info(clean_cmd) 307 | self.topo.command_to(self.bs2, clean_cmd) 308 | 309 | # Flow bs0 -> bs3 310 | netem_cmd = self.link_characteristics.build_netem_cmd(bs1_interface_names[-1], 311 | "loss {}".format(self.link_characteristics.loss) if float(self.link_characteristics.loss) > 0 else "") 312 | logging.info(netem_cmd) 313 | self.topo.command_to(self.bs1, netem_cmd) 314 | shaping_cmd = self.link_characteristics.build_bandwidth_cmd(bs2_interface_names[-1]) 315 | logging.info(shaping_cmd) 316 | self.topo.command_to(self.bs2, shaping_cmd) 317 | 318 | # Flow bs3 -> bs0 319 | netem_cmd = self.link_characteristics.build_netem_cmd(bs2_interface_names[0], 320 | "loss {}".format(self.link_characteristics.loss) if float(self.link_characteristics.loss) > 0 else "") 321 | logging.info(netem_cmd) 322 | self.topo.command_to(self.bs2, netem_cmd) 323 | shaping_cmd = self.link_characteristics.build_bandwidth_cmd(bs1_interface_names[0]) 324 | logging.info(shaping_cmd) 325 | self.topo.command_to(self.bs1, shaping_cmd) 326 | 327 | def configure_changing_bottleneck(self): 328 | bs1_interface_names = self.topo.get_interface_names(self.bs1) 329 | bs2_interface_names = self.topo.get_interface_names(self.bs2) 330 | # Flow bs0 -> bs3 331 | shaping_cmd = self.link_characteristics.build_changing_bandwidth_cmd(bs1_interface_names[-1]) 332 | logging.info(shaping_cmd) 333 | self.topo.command_to(self.bs1, shaping_cmd) 334 | netem_cmd = self.link_characteristics.build_changing_netem_cmd(bs2_interface_names[-1]) 335 | logging.info(netem_cmd) 336 | self.topo.command_to(self.bs2, netem_cmd) 337 | 338 | # Flow bs3 -> bs0 339 | shaping_cmd = self.link_characteristics.build_changing_bandwidth_cmd(bs2_interface_names[0]) 340 | logging.info(shaping_cmd) 341 | self.topo.command_to(self.bs2, shaping_cmd) 342 | netem_cmd = self.link_characteristics.build_changing_netem_cmd(bs1_interface_names[0]) 343 | logging.info(netem_cmd) 344 | self.topo.command_to(self.bs1, netem_cmd) 345 | 346 | def get_left(self): 347 | return self.bs0 348 | 349 | def get_right(self): 350 | return self.bs3 351 | 352 | 353 | class Topo(object): 354 | """ 355 | Base class to instantiate a topology. 356 | 357 | The network topology has always the following elements: 358 | - a (set of) client(s) 359 | - a (set of) router(s) 360 | - a (set of) server(s) 361 | - a set of bottleneck links 362 | 363 | This class is not instantiable as it. You must define a child class with the 364 | `NAME` attribute. 365 | 366 | Attributes: 367 | topo_builder instance of TopoBuilder 368 | topo_parameter instance of TopoParameter 369 | change_netem boolean indicating if netem must be changed 370 | log_file file descriptor logging commands relative to the topo 371 | """ 372 | MININET_BUILDER = "mininet" 373 | TOPO_ATTR = "topoType" 374 | SWITCH_NAME_PREFIX = "s" 375 | CLIENT_NAME_PREFIX = "Client" 376 | SERVER_NAME_PREFIX = "Server" 377 | ROUTER_NAME_PREFIX = "Router" 378 | CMD_LOG_FILENAME = "command.log" 379 | 380 | def __init__(self, topo_builder, topo_parameter): 381 | self.topo_builder = topo_builder 382 | self.topo_parameter = topo_parameter 383 | self.change_netem = topo_parameter.get(TopoParameter.CHANGE_NETEM).lower() == "yes" 384 | self.log_file = open(Topo.CMD_LOG_FILENAME, 'w') 385 | self.clients = [] 386 | self.routers = [] 387 | self.servers = [] 388 | self.bottleneck_links = [] 389 | 390 | def get_client_name(self, index): 391 | return "{}_{}".format(Topo.CLIENT_NAME_PREFIX, index) 392 | 393 | def get_router_name(self, index): 394 | return "{}_{}".format(Topo.ROUTER_NAME_PREFIX, index) 395 | 396 | def get_server_name(self, index): 397 | return "{}_{}".format(Topo.SERVER_NAME_PREFIX, index) 398 | 399 | def add_client(self): 400 | client = self.add_host(self.get_client_name(self.client_count())) 401 | self.clients.append(client) 402 | return client 403 | 404 | def add_router(self): 405 | router = self.add_host(self.get_router_name(self.router_count())) 406 | self.routers.append(router) 407 | return router 408 | 409 | def add_server(self): 410 | server = self.add_host(self.get_server_name(self.server_count())) 411 | self.servers.append(server) 412 | return server 413 | 414 | def get_link_characteristics(self): 415 | return self.topo_parameter.link_characteristics 416 | 417 | def command_to(self, who, cmd): 418 | self.log_file.write("{} : {}\n".format(who, cmd)) 419 | return self.topo_builder.command_to(who, cmd) 420 | 421 | def command_global(self, cmd): 422 | """ 423 | mainly use for not namespace sysctl. 424 | """ 425 | self.log_file.write("Global : {}\n".format(cmd)) 426 | return self.topo_builder.command_global(cmd) 427 | 428 | def client_count(self): 429 | return len(self.clients) 430 | 431 | def get_client(self, index): 432 | return self.clients[index] 433 | 434 | def get_router(self, index): 435 | return self.routers[index] 436 | 437 | def get_server(self, index): 438 | return self.servers[index] 439 | 440 | def router_count(self): 441 | return len(self.routers) 442 | 443 | def server_count(self): 444 | return len(self.servers) 445 | 446 | def bottleneck_link_count(self): 447 | return len(self.bottleneck_links) 448 | 449 | def get_host(self, who): 450 | return self.topo_builder.get_host(who) 451 | 452 | def get_interface_names(self, who): 453 | return self.topo_builder.get_interface_names(who) 454 | 455 | def add_host(self, host): 456 | return self.topo_builder.add_host(host) 457 | 458 | def add_switch(self, switch): 459 | return self.topo_builder.add_switch(switch) 460 | 461 | def add_link(self, from_a, to_b, **kwargs): 462 | self.topo_builder.add_link(from_a, to_b, **kwargs) 463 | 464 | def add_bottleneck_link(self, from_a, to_b, link_characteristics=None, bottleneck_link=None): 465 | """ 466 | If bottleneck_link is None, create a bottleneck link with parameters kwargs, 467 | otherwise just connect it to from_a and to_b and returns the bottleneck_link 468 | """ 469 | if bottleneck_link is None: 470 | bottleneck_link = BottleneckLink(self.topo_builder, self, link_characteristics) 471 | self.bottleneck_links.append(bottleneck_link) 472 | 473 | self.topo_builder.add_link(from_a, bottleneck_link.get_left()) 474 | self.topo_builder.add_link(bottleneck_link.get_right(), to_b) 475 | return bottleneck_link 476 | 477 | def reinit_variables(self): 478 | # Because we create nodes before starting mininet 479 | self.clients = [self.get_host(self.get_client_name(i)) for i in range(len(self.clients))] 480 | self.routers = [self.get_host(self.get_router_name(i)) for i in range(len(self.routers))] 481 | self.servers = [self.get_host(self.get_server_name(i)) for i in range(len(self.servers))] 482 | for b in self.bottleneck_links: 483 | b.reinit_variables() 484 | 485 | def get_cli(self): 486 | self.topo_builder.get_cli() 487 | 488 | def start_network(self): 489 | self.topo_builder.start_network() 490 | 491 | def close_log_file(self): 492 | self.log_file.close() 493 | 494 | def stop_network(self): 495 | self.topo_builder.stop_network() 496 | 497 | 498 | class TopoConfig(object): 499 | """ 500 | Base class to instantiate a topology. 501 | 502 | This class is not instantiable as it. You must define a child class with the 503 | `NAME` attribute. 504 | """ 505 | def __init__(self, topo, param): 506 | self.topo = topo 507 | self.param = param 508 | 509 | def configure_network(self): 510 | self.topo.reinit_variables() 511 | self.disable_tso() 512 | logging.debug("Configure network in TopoConfig") 513 | self.configure_interfaces() 514 | self.configure_routing() 515 | 516 | def disable_tso(self): 517 | """ 518 | Disable TSO, GSO and GRO on all interfaces 519 | """ 520 | logging.info("Disable TSO, GSO and GRO on all interfaces of all nodes") 521 | for node in [self.topo.get_host(n) for n in self.topo.topo_builder.net]: 522 | for intf in self.topo.get_interface_names(node): 523 | logging.debug("Disable TSO, GSO and GRO on interface {}".format(intf)) 524 | cmd = "ethtool -K {} tso off; ethtool -K {} gso off; ethtool -K {} gro off".format(intf, intf, intf) 525 | logging.debug(cmd) 526 | self.topo.command_to(node, cmd) 527 | 528 | def run_netem_at(self): 529 | """ 530 | Prepare netem commands to be run after some delay 531 | """ 532 | if not self.topo.change_netem: 533 | # Just rely on defaults of TCLink 534 | logging.info("No need to change netem") 535 | return 536 | 537 | logging.info("Will change netem config on the fly") 538 | for b in self.topo.bottleneck_links: 539 | b.configure_changing_bottleneck() 540 | 541 | def configure_interfaces(self): 542 | """ 543 | Function to inherit to configure the interfaces of the topology 544 | """ 545 | for b in self.topo.bottleneck_links: 546 | b.configure_bottleneck() 547 | 548 | def configure_routing(self): 549 | """ 550 | Function to override to configure the routing of the topology 551 | """ 552 | pass 553 | 554 | def client_interface_count(self): 555 | """ 556 | Return the number of client's interfaces, without lo 557 | """ 558 | raise NotImplementedError() 559 | 560 | def server_interface_count(self): 561 | """ 562 | Return the number of server's interfaces, without lo 563 | """ 564 | raise NotImplementedError() 565 | 566 | def get_client_interface(self, client_index, interface_index): 567 | """ 568 | Return the interface with index `interface_index` of the client with index `client_index` 569 | """ 570 | raise NotImplementedError() 571 | 572 | def get_server_interface(self, server_index, interface_index): 573 | """ 574 | Return the interface with index `interface_index` of the server with index `server_index` 575 | """ 576 | raise NotImplementedError() 577 | 578 | def get_router_interface_to_client_switch(self, index): 579 | """ 580 | Return the router's interface to client's switch with index `index` 581 | """ 582 | raise NotImplementedError() 583 | 584 | def get_router_interface_to_server_switch(self, index): 585 | """ 586 | Return the router's interface to server's switch with index `index` 587 | """ 588 | raise NotImplementedError() 589 | 590 | def interface_backup_command(self, interface_name): 591 | return "ip link set dev {} multipath backup ".format( 592 | interface_name) 593 | 594 | def interface_up_command(self, interface_name, ip, subnet): 595 | return "ifconfig {} {} netmask {}".format(interface_name, ip, subnet) 596 | 597 | def add_table_route_command(self, from_ip, id): 598 | return "ip rule add from {} table {}".format(from_ip, id + 1) 599 | 600 | def add_link_scope_route_command(self, network, interface_name, id): 601 | return "ip route add {} dev {} scope link table {}".format( 602 | network, interface_name, id + 1) 603 | 604 | def add_table_default_route_command(self, via, id): 605 | return "ip route add default via {} table {}".format(via, id + 1) 606 | 607 | def add_global_default_route_command(self, via, interface_name): 608 | return "ip route add default scope global nexthop via {} dev {}".format(via, interface_name) 609 | 610 | def arp_command(self, ip, mac): 611 | return "arp -s {} {}".format(ip, mac) 612 | 613 | def add_simple_default_route_command(self, via): 614 | return "ip route add default via {}".format(via) 615 | -------------------------------------------------------------------------------- /experiments/__init__.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | import pkgutil 3 | import os 4 | 5 | from core.experiment import Experiment 6 | 7 | pkg_dir = os.path.dirname(__file__) 8 | for (module_loader, name, ispkg) in pkgutil.iter_modules([pkg_dir]): 9 | importlib.import_module('.' + name, __package__) 10 | 11 | # Track indirect inheritance 12 | EXPERIMENTS = {} 13 | 14 | def _get_all_subclasses(BaseClass): 15 | for cls in BaseClass.__subclasses__(): 16 | if hasattr(cls, "NAME"): 17 | EXPERIMENTS[cls.NAME] = cls 18 | 19 | _get_all_subclasses(cls) 20 | 21 | _get_all_subclasses(Experiment) -------------------------------------------------------------------------------- /experiments/ab.py: -------------------------------------------------------------------------------- 1 | from core.experiment import RandomFileExperiment, RandomFileParameter, ExperimentParameter 2 | import os 3 | 4 | 5 | class ABParameter(RandomFileParameter): 6 | CONCURRENT_REQUESTS = "abConccurentRequests" 7 | TIME_LIMIT = "abTimelimit" 8 | 9 | def __init__(self, experiment_parameter_filename): 10 | super(ABParameter, self).__init__(experiment_parameter_filename) 11 | self.default_parameters.update({ 12 | ABParameter.CONCURRENT_REQUESTS: "50", 13 | ABParameter.TIME_LIMIT: "20", 14 | }) 15 | 16 | 17 | class AB(RandomFileExperiment): 18 | NAME = "ab" 19 | PARAMETER_CLASS = ABParameter 20 | 21 | SERVER_LOG = "ab_server.log" 22 | CLIENT_LOG = "ab_client.log" 23 | AB_BIN = "ab" 24 | PING_OUTPUT = "ping.log" 25 | 26 | def __init__(self, experiment_parameter_filename, topo, topo_config): 27 | super(AB, self).__init__(experiment_parameter_filename, topo, topo_config) 28 | 29 | def load_parameters(self): 30 | super(AB, self).load_parameters() 31 | self.concurrent_requests = self.experiment_parameter.get(ABParameter.CONCURRENT_REQUESTS) 32 | self.time_limit = self.experiment_parameter.get(ABParameter.TIME_LIMIT) 33 | 34 | def prepare(self): 35 | super(AB, self).prepare() 36 | self.topo.command_to(self.topo_config.client, "rm " + \ 37 | AB.CLIENT_LOG ) 38 | self.topo.command_to(self.topo_config.server, "rm " + \ 39 | AB.SERVER_LOG ) 40 | 41 | def get_ab_server_cmd(self): 42 | s = "python {}/../utils/http_server.py &> {} 2>&1 &".format( 43 | os.path.dirname(os.path.abspath(__file__)), AB.SERVER_LOG) 44 | print(s) 45 | return s 46 | 47 | def get_ab_client_cmd(self): 48 | s = "{} -c {} -t {} http://{}/{} &> {}".format(AB.AB_BIN, self.concurrent_requests, 49 | self.time_limit, self.topo_config.get_server_ip(), self.file, AB.CLIENT_LOG) 50 | print(s) 51 | return s 52 | 53 | def clean(self): 54 | super(AB, self).clean() 55 | 56 | def run(self): 57 | cmd = self.get_ab_server_cmd() 58 | self.topo.command_to(self.topo_config.server, cmd) 59 | print("Wait for the HTTP server to be up, this can take quite a while...") 60 | self.topo.command_to(self.topo_config.client, "sleep 15") 61 | cmd = self.get_ab_client_cmd() 62 | self.topo.command_to(self.topo_config.client, cmd) 63 | self.topo.command_to(self.topo_config.client, "sleep 2") 64 | -------------------------------------------------------------------------------- /experiments/ditg.py: -------------------------------------------------------------------------------- 1 | from core.experiment import Experiment, ExperimentParameter 2 | import os 3 | 4 | 5 | class DITGParameter(ExperimentParameter): 6 | KBYTES = "ditgKBytes" 7 | CONSTANT_PACKET_SIZE = "ditgConstantPacketSize" 8 | MEAN_POISSON_PACKETS_SEC = "ditgMeanPoissonPacketsSec" 9 | CONSTANT_PACKETS_SEC = "ditgConstantPacketsSec" 10 | BURSTS_ON_PACKETS_SEC = "ditgBurstsOnPacketsSec" 11 | BURSTS_OFF_PACKETS_SEC = "ditgBurstsOffPacketsSec" 12 | 13 | def __init__(self, experiment_parameter_filename): 14 | super(DITGParameter, self).__init__(experiment_parameter_filename) 15 | self.default_parameters.update({ 16 | DITGParameter.KBYTES: "10000", 17 | DITGParameter.CONSTANT_PACKET_SIZE: "1428", 18 | DITGParameter.MEAN_POISSON_PACKETS_SEC: "0", 19 | DITGParameter.CONSTANT_PACKETS_SEC: "0", 20 | DITGParameter.BURSTS_ON_PACKETS_SEC: "0", 21 | DITGParameter.BURSTS_OFF_PACKETS_SEC: "0", 22 | }) 23 | 24 | 25 | class DITG(Experiment): 26 | NAME = "ditg" 27 | PARAMETER_CLASS = DITGParameter 28 | 29 | DITG_LOG = "ditg.log" 30 | DITG_SERVER_LOG = "ditg_server.log" 31 | ITGDEC_BIN = "/home/mininet/D-ITG-2.8.1-r1023/bin/ITGDec" 32 | ITGRECV_BIN = "/home/mininet/D-ITG-2.8.1-r1023/bin/ITGRecv" 33 | ITGSEND_BIN = "/home/mininet/D-ITG-2.8.1-r1023/bin/ITGSend" 34 | DITG_TEMP_LOG = "snd_log_file" 35 | DITG_SERVER_TEMP_LOG = "recv_log_file" 36 | PING_OUTPUT = "ping.log" 37 | 38 | 39 | def __init__(self, experiment_parameter_filename, topo, topo_config): 40 | super(DITG, self).__init__(experiment_parameter_filename, topo, topo_config) 41 | self.load_parameters() 42 | self.ping() 43 | 44 | def load_parameters(self): 45 | self.kbytes = self.experiment_parameter.get(DITGParameter.KBYTES) 46 | self.constant_packet_size = self.experiment_parameter.get(DITGParameter.CONSTANT_PACKET_SIZE) 47 | self.mean_poisson_packets_sec = self.experiment_parameter.get(DITGParameter.MEAN_POISSON_PACKETS_SEC) 48 | self.constant_packets_sec = self.experiment_parameter.get(DITGParameter.CONSTANT_PACKETS_SEC) 49 | self.bursts_on_packets_sec = self.experiment_parameter.get(DITGParameter.BURSTS_ON_PACKETS_SEC) 50 | self.bursts_off_packets_sec = self.experiment_parameter.get(DITGParameter.BURSTS_OFF_PACKETS_SEC) 51 | 52 | def prepare(self): 53 | super(DITG, self).prepare() 54 | self.topo.command_to(self.topo_config.client, "rm " + DITG.DITG_LOG) 55 | self.topo.command_to(self.topo_config.server, "rm " + DITG.DITG_SERVER_LOG) 56 | self.topo.command_to(self.topo_config.client, "rm " + DITG.DITG_TEMP_LOG) 57 | 58 | def get_client_cmd(self): 59 | s = DITG.ITGSEND_BIN + " -a " + self.topo_config.get_server_ip() + \ 60 | " -T TCP -k " + self.kbytes + " -l " + DITG.DITG_TEMP_LOG 61 | 62 | if self.constant_packet_size != "0": 63 | s += " -c " + self.constant_packet_size 64 | elif self.mean_poisson_packets_sec != "0": 65 | s += " -O " + self.mean_poisson_packets_sec 66 | elif self.constant_packets_sec != "0": 67 | s += " -C " + self.constant_packets_sec 68 | elif self.bursts_on_packets_sec != "0" and self.bursts_off_packets_sec != "0": 69 | s += " -B C " + self.bursts_on_packets_sec + " C " + self.bursts_off_packets_sec 70 | 71 | s += " && " + DITG.ITGDEC_BIN + " " + DITG.DITG_TEMP_LOG + " &> " + DITG.DITG_LOG 72 | print(s) 73 | return s 74 | 75 | def get_server_cmd(self): 76 | s = DITG.ITGRECV_BIN + " -l " + DITG.DITG_SERVER_TEMP_LOG + " &" 77 | print(s) 78 | return s 79 | 80 | def clean(self): 81 | super(DITG, self).clean() 82 | 83 | def run(self): 84 | cmd = self.get_server_cmd() 85 | self.topo.command_to(self.topo_config.server, cmd) 86 | 87 | self.topo.command_to(self.topo_config.client, "sleep 2") 88 | cmd = self.get_client_cmd() 89 | self.topo.command_to(self.topo_config.client, cmd) 90 | self.topo.command_to(self.topo_config.server, "pkill -9 -f ITGRecv") 91 | self.topo.command_to(self.topo_config.server, DITG.ITGDEC_BIN + " " + DITG.DITG_SERVER_TEMP_LOG + " &> " + DITG.DITG_SERVER_LOG) 92 | self.topo.command_to(self.topo_config.client, "sleep 2") 93 | -------------------------------------------------------------------------------- /experiments/epload.py: -------------------------------------------------------------------------------- 1 | from core.experiment import Experiment, ExperimentParameter 2 | import os 3 | 4 | class EploadParameter(ExperimentParameter): 5 | TEST_DIR = "test_dir" 6 | 7 | def __init__(self, experiment_parameter_filename): 8 | super(EploadParameter, self).__init__(experiment_parameter_filename) 9 | self.default_parameters.update({ 10 | TEST_DIR: "/bla/bla/bla", 11 | }) 12 | 13 | 14 | class Epload(Experiment): 15 | NAME = "epload" 16 | PARAMETER_CLASS = EploadParameter 17 | 18 | SERVER_LOG = "http_server.log" 19 | EPLOAD_LOG = "epload.log" 20 | NODE_BIN = "/usr/local/nodejs/bin/node" 21 | EPLOAD_EMULATOR="/home/mininet/epload/epload/emulator/run.js" 22 | PING_OUTPUT = "ping.log" 23 | 24 | def __init__(self, experiment_parameter_filename, topo, topo_config): 25 | super(Epload, self).__init__(experiment_parameter_filename, topo, topo_config) 26 | self.load_parameters() 27 | self.ping() 28 | 29 | def load_parameters(self): 30 | self.test_dir = self.experiment_parameter.get(EploadParameter.TEST_DIR) 31 | 32 | def prepare(self): 33 | super(Epload, self).prepare() 34 | self.topo.command_to(self.topo_config.client, "rm " + \ 35 | Epload.EPLOAD_LOG ) 36 | self.topo.command_to(self.topo_config.server, "rm " + \ 37 | Epload.SERVER_LOG ) 38 | 39 | def get_http_server_cmd(self): 40 | s = "/etc/init.d/apache2 restart &>" + Epload.SERVER_LOG + " &" 41 | print(s) 42 | return s 43 | 44 | def getKillHTTPCmd(self): 45 | s = "ps aux | grep SimpleHTTP | head -1 | tr -s ' ' | cut -d ' ' -f 2 | xargs kill" 46 | print(s) 47 | return s 48 | 49 | def getEploadClientCmd(self): 50 | s = Epload.NODE_BIN + " " + Epload.EPLOAD_EMULATOR + \ 51 | " http " + \ 52 | self.test_dir + " &>" + Epload.EPLOAD_LOG 53 | print(s) 54 | return s 55 | 56 | def getSubHostCmd(self): 57 | s = "for f in `ls " + self.test_dir + "/*`; do " + \ 58 | " sed -i 's/@host@/" + self.topo_config.get_server_ip() + "/' " + \ 59 | "$f; done" 60 | print(s) 61 | return s 62 | 63 | def getSubBackHostCmd(self): 64 | s = "for f in `ls " + self.test_dir + "/*`; do " + \ 65 | " sed -i 's/" + self.topo_config.get_server_ip() + "/@host@/' " + \ 66 | "$f; done" 67 | print(s) 68 | return s 69 | 70 | def clean(self): 71 | super(Epload, self).clean() 72 | 73 | def run(self): 74 | cmd = self.get_http_server_cmd() 75 | self.topo.command_to(self.topo_config.server, cmd) 76 | self.topo.command_to(self.topo_config.client, "sleep 2") 77 | 78 | cmd = self.getSubHostCmd() 79 | self.topo.command_to(self.topo_config.client, cmd) 80 | cmd = self.getEploadClientCmd() 81 | self.topo.command_to(self.topo_config.client, cmd) 82 | cmd = self.getSubBackHostCmd() 83 | self.topo.command_to(self.topo_config.client, cmd) 84 | 85 | self.topo.command_to(self.topo_config.client, "sleep 2") 86 | cmd = self.getKillHTTPCmd() 87 | self.topo.command_to(self.topo_config.server, cmd) 88 | -------------------------------------------------------------------------------- /experiments/http.py: -------------------------------------------------------------------------------- 1 | from core.experiment import ExperimentParameter, RandomFileExperiment, RandomFileParameter 2 | import logging 3 | import os 4 | 5 | class HTTP(RandomFileExperiment): 6 | NAME = "http" 7 | 8 | SERVER_LOG = "http_server.log" 9 | CLIENT_LOG = "http_client.log" 10 | WGET_BIN = "wget" 11 | PING_OUTPUT = "ping.log" 12 | 13 | def __init__(self, experiment_parameter_filename, topo, topo_config): 14 | # Just rely on RandomFileExperiment 15 | super(HTTP, self).__init__(experiment_parameter_filename, topo, topo_config) 16 | 17 | def load_parameters(self): 18 | # Just rely on RandomFileExperiment 19 | super(HTTP, self).load_parameters() 20 | 21 | def prepare(self): 22 | super(HTTP, self).prepare() 23 | self.topo.command_to(self.topo_config.client, "rm " + \ 24 | HTTP.CLIENT_LOG ) 25 | self.topo.command_to(self.topo_config.server, "rm " + \ 26 | HTTP.SERVER_LOG ) 27 | 28 | def get_http_server_cmd(self): 29 | s = "python3 -m http.server 80 &> {}&".format(HTTP.SERVER_LOG) 30 | logging.info(s) 31 | return s 32 | 33 | def get_http_client_cmd(self): 34 | s = "(time {} http://{}/{} --no-check-certificate) &> {}".format(HTTP.WGET_BIN, 35 | self.topo_config.get_server_ip(), self.file, HTTP.CLIENT_LOG) 36 | logging.info(s) 37 | return s 38 | 39 | def clean(self): 40 | super(HTTP, self).clean() 41 | 42 | def run(self): 43 | cmd = self.get_http_server_cmd() 44 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_before") 45 | self.topo.command_to(self.topo_config.server, cmd) 46 | 47 | self.topo.command_to(self.topo_config.client, "sleep 2") 48 | cmd = self.get_http_client_cmd() 49 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_before") 50 | self.topo.command_to(self.topo_config.client, cmd) 51 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_after") 52 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_after") 53 | self.topo.command_to(self.topo_config.client, "sleep 2") 54 | -------------------------------------------------------------------------------- /experiments/https.py: -------------------------------------------------------------------------------- 1 | from core.experiment import ExperimentParameter, RandomFileExperiment, RandomFileParameter 2 | import os 3 | 4 | class HTTPS(RandomFileExperiment): 5 | NAME = "https" 6 | 7 | SERVER_LOG = "https_server.log" 8 | CLIENT_LOG = "https_client.log" 9 | WGET_BIN = "wget" 10 | PING_OUTPUT = "ping.log" 11 | 12 | def __init__(self, experiment_parameter_filename, topo, topo_config): 13 | # Just rely on RandomFileExperiment 14 | super(HTTPS, self).__init__(experiment_parameter_filename, topo, topo_config) 15 | 16 | def load_parameters(self): 17 | # Just rely on RandomFileExperiment 18 | super(HTTPS, self).load_parameters() 19 | 20 | def prepare(self): 21 | super(HTTPS, self).prepare() 22 | self.topo.command_to(self.topo_config.client, "rm " + \ 23 | HTTPS.CLIENT_LOG ) 24 | self.topo.command_to(self.topo_config.server, "rm " + \ 25 | HTTPS.SERVER_LOG ) 26 | 27 | def getHTTPSServerCmd(self): 28 | s = "python {}/../utils/https_server.py {}/../utils/server.pem &> {}&".format(os.path.dirname(os.path.abspath(__file__)), 29 | os.path.dirname(os.path.abspath(__file__)), HTTPS.SERVER_LOG) 30 | print(s) 31 | return s 32 | 33 | def getHTTPSClientCmd(self): 34 | s = "(time " + HTTPS.WGET_BIN + " https://" + self.topo_config.get_server_ip() + \ 35 | "/" + self.file + " --no-check-certificate) &>" + HTTPS.CLIENT_LOG 36 | print(s) 37 | return s 38 | 39 | def clean(self): 40 | super(HTTPS, self).clean() 41 | 42 | def run(self): 43 | cmd = self.getHTTPSServerCmd() 44 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_before") 45 | self.topo.command_to(self.topo_config.server, cmd) 46 | 47 | print("Waiting for the server to run") 48 | self.topo.command_to(self.topo_config.client, "sleep 2") 49 | cmd = self.getHTTPSClientCmd() 50 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_before") 51 | self.topo.command_to(self.topo_config.client, cmd) 52 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_after") 53 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_after") 54 | self.topo.command_to(self.topo_config.server, "pkill -f https_server.py") 55 | self.topo.command_to(self.topo_config.client, "sleep 2") 56 | -------------------------------------------------------------------------------- /experiments/iperf.py: -------------------------------------------------------------------------------- 1 | from core.experiment import Experiment, ExperimentParameter 2 | import logging 3 | import os 4 | 5 | 6 | class IPerfParameter(ExperimentParameter): 7 | TIME = "iperfTime" 8 | PARALLEL = "iperfParallel" 9 | 10 | def __init__(self, experiment_parameter_filename): 11 | super(IPerfParameter, self).__init__(experiment_parameter_filename) 12 | self.default_parameters.update({ 13 | IPerfParameter.TIME: "10", 14 | IPerfParameter.PARALLEL: "1", 15 | }) 16 | 17 | class IPerf(Experiment): 18 | NAME = "iperf" 19 | PARAMETER_CLASS = IPerfParameter 20 | 21 | IPERF_LOG = "iperf.log" 22 | SERVER_LOG = "server.log" 23 | IPERF_BIN = "iperf" 24 | PING_OUTPUT = "ping.log" 25 | 26 | def __init__(self, experiment_parameter_filename, topo, topo_config): 27 | super(IPerf, self).__init__(experiment_parameter_filename, topo, topo_config) 28 | self.load_parameters() 29 | self.ping() 30 | 31 | def load_parameters(self): 32 | self.time = self.experiment_parameter.get(IPerfParameter.TIME) 33 | self.parallel = self.experiment_parameter.get(IPerfParameter.PARALLEL) 34 | 35 | def prepare(self): 36 | super(IPerf, self).prepare() 37 | self.topo.command_to(self.topo_config.client, "rm {}".format(IPerf.IPERF_LOG)) 38 | self.topo.command_to(self.topo_config.server, "rm {}".format(IPerf.SERVER_LOG)) 39 | 40 | def get_client_cmd(self): 41 | s = "{} -c {} -t {} -P {} -i 1 &>{}".format(IPerf.IPERF_BIN, 42 | self.topo_config.get_server_ip(), self.time, self.parallel, IPerf.IPERF_LOG) 43 | logging.info(s) 44 | return s 45 | 46 | def get_server_cmd(self): 47 | s = "{} -s &> {} &".format(IPerf.IPERF_BIN, IPerf.SERVER_LOG) 48 | logging.info(s) 49 | return s 50 | 51 | def clean(self): 52 | super(IPerf, self).clean() 53 | 54 | def run(self): 55 | cmd = self.get_server_cmd() 56 | self.topo.command_to(self.topo_config.server, cmd) 57 | 58 | self.topo.command_to(self.topo_config.client, "sleep 2") 59 | cmd = self.get_client_cmd() 60 | self.topo.command_to(self.topo_config.client, cmd) 61 | self.topo.command_to(self.topo_config.client, "sleep 2") 62 | -------------------------------------------------------------------------------- /experiments/iperf_scenario.py: -------------------------------------------------------------------------------- 1 | from core.experiment import Experiment, ExperimentParameter 2 | from topos.multi_interface_multi_client import MultiInterfaceMultiClientTopo 3 | import logging 4 | import os 5 | 6 | 7 | class IPerfScenarioParameter(ExperimentParameter): 8 | FM_SUBFLOWS = "iperfScenarioFMSublows" 9 | 10 | def __init__(self, experiment_parameter_filename): 11 | super(IPerfScenarioParameter, self).__init__(experiment_parameter_filename) 12 | self.default_parameters.update({ 13 | IPerfScenarioParameter.FM_SUBFLOWS: "1", 14 | }) 15 | 16 | 17 | class IPerfScenario(Experiment): 18 | NAME = "iperfScenario" 19 | PARAMETER_CLASS = IPerfScenarioParameter 20 | 21 | IPERF_LOG = "iperf.log" 22 | SERVER_LOG = "server.log" 23 | IPERF_BIN = "iperf" 24 | PING_OUTPUT = "ping.log" 25 | 26 | def __init__(self, experiment_parameter_filename, topo, topo_config): 27 | super(IPerfScenario, self).__init__(experiment_parameter_filename, topo, topo_config) 28 | self.load_parameters() 29 | self.ping() 30 | 31 | def load_parameters(self): 32 | super(IPerfScenario, self).load_parameters() 33 | self.fm_subflows = self.experiment_parameter.get(IPerfScenarioParameter.FM_SUBFLOWS) 34 | 35 | def prepare(self): 36 | super(IPerfScenario, self).prepare() 37 | self.topo.command_to(self.topo_config.client, "rm {}".format(IPerfScenario.IPERF_LOG)) 38 | self.topo.command_to(self.topo_config.server, "rm {}".format(IPerfScenario.SERVER_LOG)) 39 | 40 | if not isinstance(self.topo, MultiInterfaceMultiClientTopo): 41 | raise Exception("IPerfScenario only runs with MultiInterfaceMultiClientTopo") 42 | 43 | def get_client_iperf_cmd(self, server_ip, time, client_id): 44 | s = "{} -c {} -t {} -P 1 -i 5 &>{}{}".format(IPerfScenario.IPERF_BIN, server_ip, time, IPerfScenario.IPERF_LOG, client_id) 45 | logging.info(s) 46 | return s 47 | 48 | def get_server_cmd(self, server_id=0): 49 | s = "{} -s &> {}{} &".format(IPerfScenario.IPERF_BIN, IPerfScenario.SERVER_LOG, server_id) 50 | logging.info(s) 51 | return s 52 | 53 | def clean(self): 54 | super(IPerfScenario, self).clean() 55 | 56 | def run(self): 57 | 58 | self.topo.command_to(self.topo_config.router, "tcpdump -i any -w router.pcap &") 59 | # First run servers 60 | for l, s in enumerate(self.topo_config.servers): 61 | self.topo.command_to(s, self.get_server_cmd(server_id=l)) 62 | 63 | # And set nb of subflows for fullmesh 64 | self.topo.command_to(self.topo_config.client, "echo {} > /sys/module/mptcp_fullmesh/parameters/num_subflows".format(self.fm_subflows)) 65 | 66 | self.topo.command_to(self.topo_config.client, "sleep 2") 67 | 68 | # We run as follow. 69 | logging.info("This experiment last about 1 minute. Please wait...") 70 | cmd = "sleep 10 && {} &".format(self.get_client_iperf_cmd(self.topo_config.get_server_ip(interface_index=1), 20, 1)) 71 | self.topo.command_to(self.topo_config.clients[1], cmd) 72 | cmd = "sleep 20 && {} &".format(self.get_client_iperf_cmd(self.topo_config.get_server_ip(interface_index=2), 20, 2)) 73 | self.topo.command_to(self.topo_config.clients[2], cmd) 74 | cmd = "{} &".format(self.get_client_iperf_cmd(self.topo_config.get_server_ip(), 50, 0)) 75 | self.topo.command_to(self.topo_config.client, cmd) 76 | 77 | self.topo.command_to(self.topo_config.client, "sleep 2") 78 | 79 | # This is hacky 80 | self.topo.command_global("sysctl -w net.mptcp.mptcp_enabled=0") 81 | self.topo.command_global("sysctl -w net.ipv4.tcp_congestion_control=reno") 82 | 83 | self.topo.command_to(self.topo_config.client, "sleep 50") 84 | 85 | self.topo.command_to(self.topo_config.client, "echo 1 > /sys/module/mptcp_fullmesh/parameters/num_subflows") 86 | -------------------------------------------------------------------------------- /experiments/msg.py: -------------------------------------------------------------------------------- 1 | from core.experiment import Experiment, ExperimentParameter 2 | import logging 3 | import os 4 | 5 | 6 | class MsgParameter(ExperimentParameter): 7 | CLIENT_SLEEP = "msgClientSleep" 8 | SERVER_SLEEP = "msgServerSleep" 9 | NB_REQUESTS = "msgNbRequests" 10 | BYTES = "msgBytes" 11 | 12 | def __init__(self, experiment_parameter_filename): 13 | super(MsgParameter, self).__init__(experiment_parameter_filename) 14 | self.default_parameters.update({ 15 | MsgParameter.CLIENT_SLEEP: "5.0", 16 | MsgParameter.SERVER_SLEEP: "5.0", 17 | MsgParameter.NB_REQUESTS: "5", 18 | MsgParameter.BYTES: "1200", 19 | }) 20 | 21 | class Msg(Experiment): 22 | NAME = "msg" 23 | PARAMETER_CLASS = MsgParameter 24 | 25 | SERVER_LOG = "msg_server.log" 26 | CLIENT_LOG = "msg_client.log" 27 | CLIENT_ERR = "msg_client.err" 28 | PING_OUTPUT = "ping.log" 29 | 30 | def __init__(self, experiment_parameter_filename, topo, topo_config): 31 | super(Msg, self).__init__(experiment_parameter_filename, topo, topo_config) 32 | self.load_parameters() 33 | self.ping() 34 | 35 | def load_parameters(self): 36 | self.client_sleep = self.experiment_parameter.get(MsgParameter.CLIENT_SLEEP) 37 | self.server_sleep = self.experiment_parameter.get(MsgParameter.SERVER_SLEEP) 38 | self.nb_requests = self.experiment_parameter.get(MsgParameter.NB_REQUESTS) 39 | self.bytes = self.experiment_parameter.get(MsgParameter.BYTES) 40 | 41 | def prepare(self): 42 | super(Msg, self).prepare() 43 | self.topo.command_to(self.topo_config.client, "rm " + \ 44 | Msg.CLIENT_LOG) 45 | self.topo.command_to(self.topo_config.server, "rm " + \ 46 | Msg.SERVER_LOG) 47 | 48 | def get_msg_server_cmd(self): 49 | s = "python {}/../utils/msg_server.py --sleep {} --bytes {} &> {}&".format( 50 | os.path.dirname(os.path.abspath(__file__)), self.server_sleep, self.bytes, 51 | Msg.SERVER_LOG) 52 | logging.info(s) 53 | return s 54 | 55 | def get_msg_client_cmd(self, daemon=False): 56 | s = "python {}/../utils/msg_client.py --sleep {} --nb {} --bytes {} > {} 2> {} {}".format( 57 | os.path.dirname(os.path.abspath(__file__)), self.client_sleep, self.nb_requests, 58 | self.bytes, Msg.CLIENT_LOG, Msg.CLIENT_ERR, "&" if daemon else "") 59 | logging.info(s) 60 | return s 61 | 62 | def clean(self): 63 | super(Msg, self).clean() 64 | 65 | def run(self): 66 | cmd = self.get_msg_server_cmd() 67 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_before") 68 | self.topo.command_to(self.topo_config.server, cmd) 69 | 70 | self.topo.command_to(self.topo_config.client, "sleep 2") 71 | cmd = self.get_msg_client_cmd() 72 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_before") 73 | self.topo.command_to(self.topo_config.client, cmd) 74 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_after") 75 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_after") 76 | self.topo.command_to(self.topo_config.server, "pkill -f msg_server.py") 77 | self.topo.command_to(self.topo_config.client, "sleep 2") 78 | -------------------------------------------------------------------------------- /experiments/nc.py: -------------------------------------------------------------------------------- 1 | from core.experiment import Experiment, ExperimentParameter 2 | 3 | """ 4 | Should be the mother of ExperimentNCPV, shame on me, should rewrite 5 | ExperimentNCPV as daughter class of this one. 6 | """ 7 | 8 | class NCParameter(ExperimentParameter): 9 | DD_IBS = "ddIBS" 10 | DD_OBS = "ddIBS" 11 | DD_COUNT = "ddCount" 12 | SERVER_PORT = "ncServerPort" 13 | CLIENT_PORT = "ncClientPort" 14 | 15 | def __init__(self, experiment_parameter_filename): 16 | super(NCParameter, self).__init__(experiment_parameter_filename) 17 | self.default_parameters.update({ 18 | NCParameter.DD_IBS: "1k", 19 | NCParameter.DD_OBS: "1k", 20 | NCParameter.DD_COUNT: "5000", #5k * 1k = 5m 21 | NCParameter.SERVER_PORT: "33666", 22 | NCParameter.CLIENT_PORT: "33555", 23 | }) 24 | 25 | 26 | class NC(Experiment): 27 | NAME = "nc" 28 | PARAMETER_CLASS = NCParameter 29 | 30 | SERVER_NC_LOG = "netcat_server" 31 | CLIENT_NC_LOG = "netcat_client" 32 | NC_BIN = "netcat" 33 | 34 | def __init__(self, experiment_parameter_filename, topo, topo_config): 35 | super(NC, self).__init__(experiment_parameter_filename, topo, topo_config) 36 | self.load_parameters() 37 | 38 | def load_parameters(self): 39 | self.ddibs = self.experiment_parameter.get(NCParameter.DD_IBS) 40 | self.ddobs = self.experiment_parameter.get(NCParameter.DD_OBS) 41 | self.ddcount = self.experiment_parameter.get(NCParameter.DD_COUNT) 42 | self.ncServerPort = self.experiment_parameter.get(NCParameter.SERVER_PORT) 43 | self.ncClientPort = [] 44 | for k in sorted(self.experiment_parameter.paramDic): 45 | if k.startswith(NCParameter.CLIENT_PORT): 46 | port = self.experiment_parameter.paramDic[k] 47 | self.ncClientPort.append(port) 48 | if len(self.ncClientPort) == 0: 49 | d = self.experiment_parameter.get(NCParameter.CLIENT_PORT) 50 | self.ncClientPort.append(d) 51 | 52 | def prepare(self): 53 | super(NC, self).prepare() 54 | self.topo.command_to(self.topo_config.client, "rm " + \ 55 | NC.CLIENT_NC_LOG ) 56 | self.topo.command_to(self.topo_config.server, "rm " + \ 57 | NC.SERVER_NC_LOG ) 58 | 59 | def getNCServerCmd(self, id): 60 | s = "dd if=/dev/urandom ibs=" + self.ddibs + \ 61 | " obs=" + self.ddobs + \ 62 | " count=" + self.ddcount + \ 63 | " | " + \ 64 | NC.NC_BIN + \ 65 | " -l " + self.ncServerPort + \ 66 | " &>" + NC.SERVER_NC_LOG + \ 67 | "_" + str(id) + ".log" 68 | print(s) 69 | return s 70 | 71 | def getNCClientCmd(self, id): 72 | s = NC.NC_BIN + " " + \ 73 | " -p " + self.ncClientPort[id] + " " + \ 74 | self.topo_config.get_server_ip() + " " + \ 75 | self.ncServerPort + " " + \ 76 | "&>" + NC.CLIENT_NC_LOG + \ 77 | "_" + str(id) + ".log" 78 | print(s) 79 | return s 80 | 81 | def clean(self): 82 | super(NC, self).clean() 83 | self.topo.command_to(self.topo_config.server, "killall netcat") 84 | 85 | def run(self): 86 | for i in range(0, len(self.ncClientPort)): 87 | cmd = self.getNCServerCmd(i) 88 | self.topo_config.server.sendCmd(cmd) 89 | 90 | cmd = self.getNCClientCmd(i) 91 | self.topo.command_to(self.topo_config.client, cmd) 92 | 93 | self.topo_config.server.waitOutput() 94 | 95 | self.topo.command_to(self.topo_config.client, "sleep 1") 96 | 97 | -------------------------------------------------------------------------------- /experiments/ncpv.py: -------------------------------------------------------------------------------- 1 | from .nc import NC, NCParameter, ExperimentParameter 2 | 3 | 4 | class NCPVParameter(NCParameter): 5 | RATE_LIMIT= "pvRateLimit" 6 | G = "pvG" #patched version of pv 7 | Z = "pvZ" 8 | CHANGE_PV = "changePv" 9 | CHANGE_PV_AT = "changePvAt" 10 | 11 | def __init__(self, experiment_parameter_filename): 12 | super(NCPVParameter, self).__init__(experiment_parameter_filename) 13 | self.default_parameters.update({ 14 | NCPVParameter.RATE_LIMIT: "400k", 15 | NCPVParameter.G: "10000", 16 | NCPVParameter.Z: "10000", 17 | NCPVParameter.CHANGE_PV: "no", 18 | }) 19 | 20 | 21 | class MpPvAt(object): 22 | def __init__(self, at, cmd): 23 | self.at = at 24 | self.cmd = cmd 25 | self.delta = 0 26 | 27 | def __str__(self): 28 | return "Pv... at " + str(self.at) + "(" + str(self.delta) + \ 29 | ") will be " + self.cmd 30 | 31 | 32 | class NCPV(NC): 33 | """ 34 | NC PV : NetCat and Pipe Viewer 35 | """ 36 | NAME = "ncpv" 37 | PARAMETER_CLASS = NCPVParameter 38 | 39 | SERVER_NC_LOG = "netcat_server" 40 | CLIENT_NC_LOG = "netcat_client" 41 | NC_BIN = "/usr/local/bin/nc" 42 | PV_BIN = "/usr/local/bin/pv" 43 | PING_OUTPUT = "ping.log" 44 | 45 | def __init__(self, experiment_parameter_filename, topo, topo_config): 46 | super(NCPV, self).__init__(experiment_parameter_filename, topo, topo_config) 47 | self.load_parameters() 48 | self.ping() 49 | 50 | def ping(self): 51 | self.topo.command_to(self.topo_config.client, "rm " + \ 52 | NCPV.PING_OUTPUT ) 53 | count = self.experiment_parameter.get(ExperimentParameter.PING_COUNT) 54 | for i in range(0, self.topo_config.client_interface_count()): 55 | cmd = self.ping_command(self.topo_config.get_client_ip(i), 56 | self.topo_config.get_server_ip(), n = count) 57 | self.topo.command_to(self.topo_config.client, cmd) 58 | 59 | def ping_command(self, fromIP, toIP, n=5): 60 | s = "ping -c " + str(n) + " -I " + fromIP + " " + toIP + \ 61 | " >> " + NCPV.PING_OUTPUT 62 | print(s) 63 | return s 64 | 65 | def load_parameters(self): 66 | super(NCPV, self).load_parameters() 67 | self.pvg = self.experiment_parameter.get(ExperimentParameter.PVG) 68 | self.pvz = self.experiment_parameter.get(ExperimentParameter.PVZ) 69 | self.pvRateLimit = self.experiment_parameter.get(ExperimentParameter.PVRATELIMIT) 70 | self.loadPvAt() 71 | 72 | def loadPvAt(self): 73 | self.changePvAt = [] 74 | self.changePv = self.experiment_parameter.get(ExperimentParameter.CHANGEPV) 75 | if self.changePv != "yes": 76 | print("Don't change pv rate...") 77 | return 78 | changePvAt = self.experiment_parameter.get(ExperimentParameter.CHANGEPVAT) 79 | if not isinstance(changePvAt, list): 80 | changePvAt = [changePvAt] 81 | for p in changePvAt: 82 | tab = p.split(",") 83 | if len(tab)==2: 84 | o = MpPvAt(float(tab[0]), tab[1]) 85 | self.addPvAt(o) 86 | else: 87 | print("pv wrong line : " + p) 88 | 89 | def addPvAt(self, p): 90 | if len(self.changePvAt) == 0 : 91 | p.delta = p.at 92 | else: 93 | if p.at > self.changePvAt[-1].at: 94 | p.delta = p.at - self.changePvAt[-1].at 95 | else: 96 | print("Do not take into account " + p.__str__() + \ 97 | "because ooo !") 98 | return 99 | 100 | self.changePvAt.append(p) 101 | 102 | def getPvChangeCmd(self): 103 | cmd = "" 104 | for p in self.changePvAt: 105 | cmd = cmd + "sleep " + str(p.delta) 106 | cmd = cmd + " && " 107 | cmd = cmd + NCPV.PV_BIN + " -R " + self.pvPid 108 | cmd = cmd + " " + p.cmd + " && " 109 | cmd = cmd + " true &" 110 | return cmd 111 | 112 | def prepare(self): 113 | super(NCPV, self).prepare() 114 | 115 | def getNCServerCmd(self, id): 116 | s = NC.NC_BIN + " -d " + \ 117 | " -l " + self.ncServerPort + \ 118 | " 1>/dev/null 2>" + NC.SERVER_NC_LOG + \ 119 | "_" + str(id) + ".log &" 120 | print(s) 121 | return s 122 | 123 | def getNCClientCmd(self, id): 124 | s = "dd if=/dev/urandom ibs=" + self.ddibs + \ 125 | " obs=" + self.ddobs + \ 126 | " count=" + self.ddcount + \ 127 | " | " + NC.PV_BIN + \ 128 | " -g " + self.pvg + " -z " + self.pvz + \ 129 | " -q --rate-limit " + self.pvRateLimit + \ 130 | " | " + NC.NC_BIN + " " + \ 131 | " -p " + self.ncClientPort[id] + " " + \ 132 | self.topo_config.get_server_ip() + " " + \ 133 | self.ncServerPort + " " + \ 134 | "&>" + NCPV.CLIENT_NC_LOG + \ 135 | "_" + str(id) + ".log" 136 | print(s) 137 | return s 138 | 139 | def getPvPidCmd(self): 140 | s = "pgrep -n pv" 141 | return s 142 | 143 | def clean(self): 144 | super(NCPV, self).clean() 145 | 146 | def run(self): 147 | for i in range(0, len(self.ncClientPort)): 148 | cmd = self.getNCServerCmd(i) 149 | self.topo.command_to(self.topo_config.server, cmd) 150 | 151 | cmd = self.getNCClientCmd(i) 152 | self.topo_config.client.sendCmd(cmd) 153 | 154 | cmd = self.getPvPidCmd() 155 | self.pvPid = None 156 | while self.pvPid == None or self.pvPid == "": 157 | self.pvPid = self.topo.command_to(self.topo_config.server, cmd)[:-1] 158 | print("guessing pv pid ... :" + str(self.pvPid)) 159 | 160 | cmd = self.getPvChangeCmd() 161 | print(cmd) 162 | self.topo.command_to(self.topo_config.server, cmd) 163 | 164 | 165 | self.topo_config.client.waitOutput() 166 | 167 | self.topo.command_to(self.topo_config.client, "sleep 1") 168 | 169 | -------------------------------------------------------------------------------- /experiments/netperf.py: -------------------------------------------------------------------------------- 1 | from core.experiment import Experiment, ExperimentParameter 2 | import os 3 | 4 | 5 | class NetperfParameter(ExperimentParameter): 6 | TESTLEN = "netperfTestlen" 7 | TESTNAME = "netperfTestname" 8 | REQRES_SIZE = "netperfReqresSize" 9 | 10 | def __init__(self, experiment_parameter_filename): 11 | super(NetperfParameter, self).__init__(experiment_parameter_filename) 12 | self.default_parameters.update({ 13 | NetperfParameter.TESTLEN: "10", 14 | NetperfParameter.TESTNAME: "TCP_RR", 15 | NetperfParameter.REQRES_SIZE: "2K,256", 16 | }) 17 | 18 | 19 | class Netperf(Experiment): 20 | NAME = "netperf" 21 | PARAMETER_CLASS = NetperfParameter 22 | 23 | NETPERF_LOG = "netperf.log" 24 | NETSERVER_LOG = "netserver.log" 25 | NETPERF_BIN = "netperf" 26 | NETSERVER_BIN = "netserver" 27 | PING_OUTPUT = "ping.log" 28 | 29 | def __init__(self, experiment_parameter_filename, topo, topo_config): 30 | super(Netperf, self).__init__(experiment_parameter_filename, topo, topo_config) 31 | self.load_parameters() 32 | self.ping() 33 | 34 | def ping(self): 35 | self.topo.command_to(self.topo_config.client, "rm " + \ 36 | Netperf.PING_OUTPUT) 37 | count = self.experiment_parameter.get(ExperimentParameter.PING_COUNT) 38 | for i in range(0, self.topo_config.client_interface_count()): 39 | cmd = self.ping_command(self.topo_config.get_client_ip(i), 40 | self.topo_config.get_server_ip(), n = count) 41 | self.topo.command_to(self.topo_config.client, cmd) 42 | 43 | def ping_command(self, fromIP, toIP, n=5): 44 | s = "ping -c " + str(n) + " -I " + fromIP + " " + toIP + \ 45 | " >> " + Netperf.PING_OUTPUT 46 | print(s) 47 | return s 48 | 49 | def load_parameters(self): 50 | self.testlen = self.experiment_parameter.get(NetperfParameter.TESTLEN) 51 | self.testname = self.experiment_parameter.get(NetperfParameter.TESTNAME) 52 | self.reqres_size = self.experiment_parameter.get(NetperfParameter.REQRES_SIZE) 53 | 54 | def prepare(self): 55 | super(Netperf, self).prepare() 56 | self.topo.command_to(self.topo_config.client, "rm " + 57 | Netperf.NETPERF_LOG) 58 | self.topo.command_to(self.topo_config.server, "rm " + 59 | Netperf.NETSERVER_LOG) 60 | 61 | def get_client_cmd(self): 62 | s = "{} -H {} -l {} -t {} -- -r {} &> {}".format(Netperf.NETPERF_BIN, 63 | self.topo_config.get_server_ip(), self.testlen, self.testname, self.reqres_size, 64 | Netperf.NETPERF_LOG) 65 | print(s) 66 | return s 67 | 68 | def get_server_cmd(self): 69 | s = "sudo {} &> {} &".format(Netperf.NETSERVER_BIN, Netperf.NETSERVER_LOG) 70 | print(s) 71 | return s 72 | 73 | def clean(self): 74 | super(Netperf, self).clean() 75 | 76 | def run(self): 77 | cmd = self.get_server_cmd() 78 | self.topo.command_to(self.topo_config.server, cmd) 79 | 80 | self.topo.command_to(self.topo_config.client, "sleep 2") 81 | cmd = self.get_client_cmd() 82 | self.topo.command_to(self.topo_config.client, cmd) 83 | self.topo.command_to(self.topo_config.client, "sleep 2") 84 | -------------------------------------------------------------------------------- /experiments/none.py: -------------------------------------------------------------------------------- 1 | from core.experiment import Experiment, ExperimentParameter 2 | 3 | class NoneExperiment(Experiment): 4 | NAME = "none" 5 | 6 | def __init__(self, experiment_parameter_filename, topo, topo_config): 7 | super(NoneExperiment, self).__init__(experiment_parameter_filename, topo, topo_config) 8 | 9 | def prepare(self): 10 | Experiment.prepare(self) 11 | 12 | def clean(self): 13 | Experiment.clean(self) 14 | 15 | def run(self): 16 | self.topo.get_cli() 17 | -------------------------------------------------------------------------------- /experiments/ping.py: -------------------------------------------------------------------------------- 1 | from core.experiment import Experiment, ExperimentParameter 2 | 3 | class Ping(Experiment): 4 | NAME = "ping" 5 | 6 | PING_OUTPUT = "ping.log" 7 | 8 | def __init__(self, experiment_parameter_filename, topo, topo_config): 9 | super(Ping, self).__init__(experiment_parameter_filename, topo, topo_config) 10 | 11 | def prepare(self): 12 | super(Ping, self).prepare() 13 | 14 | def clean(self): 15 | super(Ping, self).clean() 16 | 17 | def run(self): 18 | self.topo.command_to(self.topo_config.client, "rm " + \ 19 | Ping.PING_OUTPUT ) 20 | count = self.experiment_parameter.get(ExperimentParameter.PING_COUNT) 21 | for i in range(0, self.topo_config.client_interface_count()): 22 | cmd = self.ping_command(self.topo_config.get_client_ip(i), 23 | self.topo_config.get_server_ip(), n = count) 24 | self.topo.command_to(self.topo_config.client, cmd) 25 | 26 | def ping_command(self, fromIP, toIP, n=5): 27 | s = "ping -c " + str(n) + " -I " + fromIP + " " + toIP + \ 28 | " >> " + Ping.PING_OUTPUT 29 | print(s) 30 | return s 31 | -------------------------------------------------------------------------------- /experiments/pquic.py: -------------------------------------------------------------------------------- 1 | from core.experiment import Experiment, ExperimentParameter 2 | import logging 3 | import os 4 | 5 | 6 | class PQUICParameter(ExperimentParameter): 7 | PLUGINS = "pquicPlugins" 8 | CLIENT_PLUGINS = "pquicClientPlugins" 9 | SERVER_PLUGINS = "pquicServerPlugins" 10 | SIZE = "pquicSize" 11 | 12 | def __init__(self, experiment_parameter_filename): 13 | super(PQUICParameter, self).__init__(experiment_parameter_filename) 14 | self.default_parameters.update({ 15 | PQUICParameter.PLUGINS: "", 16 | PQUICParameter.CLIENT_PLUGINS: "", 17 | PQUICParameter.SERVER_PLUGINS: "", 18 | PQUICParameter.SIZE: 10240000, 19 | }) 20 | 21 | 22 | class PQUIC(Experiment): 23 | NAME = "pquic" 24 | PARAMETER_CLASS = PQUICParameter 25 | 26 | BIN = "~/pquic/picoquicdemo" 27 | CERT_FILE = "~/pquic/certs/cert.pem" 28 | KEY_FILE = "~/pquic/certs/key.pem" 29 | SERVER_LOG = "pquic_server.log" 30 | CLIENT_LOG = "pquic_client.log" 31 | 32 | def __init__(self, experiment_parameter_filename, topo, topo_config): 33 | super(PQUIC, self).__init__(experiment_parameter_filename, topo, topo_config) 34 | self.load_parameters() 35 | self.ping() 36 | 37 | def load_parameters(self): 38 | super(PQUIC, self).load_parameters() 39 | self.plugins = self.experiment_parameter.get(PQUICParameter.PLUGINS) 40 | self.client_plugins = self.experiment_parameter.get(PQUICParameter.CLIENT_PLUGINS) 41 | self.server_plugins = self.experiment_parameter.get(PQUICParameter.SERVER_PLUGINS) 42 | self.size = int(self.experiment_parameter.get(PQUICParameter.SIZE)) 43 | 44 | def prepare(self): 45 | super(PQUIC, self).prepare() 46 | self.topo.command_to(self.topo_config.client, "rm {}".format(PQUIC.CLIENT_LOG)) 47 | self.topo.command_to(self.topo_config.server, "rm {}".format(PQUIC.SERVER_LOG)) 48 | 49 | def get_plugin_cmd(self, client=False): 50 | device_plugins = self.client_plugins if client else self.server_plugins 51 | device_plugins = self.plugins if len(device_plugins) == 0 else device_plugins 52 | if len(device_plugins) == 0: 53 | return "" 54 | 55 | plugins = device_plugins.split(",") 56 | return " ".join([" -P {} ".format(p) for p in plugins]) 57 | 58 | def get_pquic_server_cmd(self): 59 | s = "{} {} -c {} -k {} &> {} &".format(PQUIC.BIN, self.get_plugin_cmd(), 60 | PQUIC.CERT_FILE, PQUIC.KEY_FILE, PQUIC.SERVER_LOG) 61 | logging.info(s) 62 | return s 63 | 64 | def get_pquic_client_cmd(self): 65 | s = "{} {} -4 -G {} {} 4443 &> {}".format(PQUIC.BIN, self.get_plugin_cmd(client=True), self.size, 66 | self.topo_config.get_server_ip(), PQUIC.CLIENT_LOG) 67 | logging.info(s) 68 | return s 69 | 70 | def clean(self): 71 | super(PQUIC, self).clean() 72 | 73 | def run(self): 74 | cmd = self.get_pquic_server_cmd() 75 | self.topo.command_to(self.topo_config.server, cmd) 76 | 77 | self.topo.command_to(self.topo_config.client, "sleep 2") 78 | 79 | cmd = self.get_pquic_client_cmd() 80 | self.topo.command_to(self.topo_config.client, cmd) 81 | 82 | self.topo.command_to(self.topo_config.client, "sleep 2") 83 | -------------------------------------------------------------------------------- /experiments/quic.py: -------------------------------------------------------------------------------- 1 | from core.experiment import RandomFileExperiment, RandomFileParameter, ExperimentParameter 2 | from topos.multi_interface_multi_client import MultiInterfaceMultiClientConfig 3 | import os 4 | 5 | 6 | class QUICParameter(RandomFileParameter): 7 | MULTIPATH = "quicMultipath" 8 | 9 | def __init__(self, experiment_parameter_filename): 10 | super(QUICParameter, self).__init__(experiment_parameter_filename) 11 | self.default_parameters.update({ 12 | QUICParameter.MULTIPATH: "0", 13 | }) 14 | 15 | 16 | class QUIC(RandomFileExperiment): 17 | NAME = "quic" 18 | PARAMETER_CLASS = QUICParameter 19 | 20 | GO_BIN = "/usr/local/go/bin/go" 21 | WGET = "~/git/wget/src/wget" 22 | SERVER_LOG = "quic_server.log" 23 | CLIENT_LOG = "quic_client.log" 24 | CLIENT_GO_FILE = "~/go/src/github.com/lucas-clemente/quic-go/example/client_benchmarker_cached/main.go" 25 | SERVER_GO_FILE = "~/go/src/github.com/lucas-clemente/quic-go/example/main.go" 26 | CERTPATH = "~/go/src/github.com/lucas-clemente/quic-go/example/" 27 | PING_OUTPUT = "ping.log" 28 | 29 | def __init__(self, experiment_parameter_filename, topo, topo_config): 30 | # Just rely on RandomFileExperiment 31 | super(QUIC, self).__init__(experiment_parameter_filename, topo, topo_config) 32 | 33 | def ping(self): 34 | self.topo.command_to(self.topo_config.client, "rm " + \ 35 | QUIC.PING_OUTPUT ) 36 | count = self.experiment_parameter.get(ExperimentParameter.PING_COUNT) 37 | for i in range(0, self.topo_config.client_interface_count()): 38 | cmd = self.ping_command(self.topo_config.get_client_ip(i), 39 | self.topo_config.get_server_ip(), n = count) 40 | self.topo.command_to(self.topo_config.client, cmd) 41 | 42 | def ping_command(self, fromIP, toIP, n=5): 43 | s = "ping -c " + str(n) + " -I " + fromIP + " " + toIP + \ 44 | " >> " + QUIC.PING_OUTPUT 45 | print(s) 46 | return s 47 | 48 | def load_parameters(self): 49 | super(QUIC, self).load_parameters() 50 | self.multipath = self.experiment_parameter.get(QUICParameter.MULTIPATH) 51 | 52 | def prepare(self): 53 | super(QUIC, self).prepare() 54 | self.topo.command_to(self.topo_config.client, "rm " + \ 55 | QUIC.CLIENT_LOG ) 56 | self.topo.command_to(self.topo_config.server, "rm " + \ 57 | QUIC.SERVER_LOG ) 58 | 59 | def getQUICServerCmd(self): 60 | s = QUIC.GO_BIN + " run " + QUIC.SERVER_GO_FILE 61 | s += " -www . -certpath " + QUIC.CERTPATH + " &>" 62 | s += QUIC.SERVER_LOG + " &" 63 | print(s) 64 | return s 65 | 66 | def getQUICClientCmd(self): 67 | s = QUIC.GO_BIN + " run " + QUIC.CLIENT_GO_FILE 68 | if int(self.multipath) > 0: 69 | s += " -m" 70 | s += " https://" + self.topo_config.get_server_ip() + ":6121/random &>" + QUIC.CLIENT_LOG 71 | print(s) 72 | return s 73 | 74 | def getCongServerCmd(self, congID): 75 | s = "python " + os.path.dirname(os.path.abspath(__file__)) + \ 76 | "/../utils/https_server.py &> https_server" + str(congID) + ".log &" 77 | print(s) 78 | return s 79 | 80 | def getCongClientCmd(self, congID): 81 | s = "(time " + QUIC.WGET + " https://" + self.topo_config.getCongServerIP(congID) +\ 82 | "/" + self.file + " --no-check-certificate --disable-mptcp) &> https_client" + str(congID) + ".log &" 83 | print(s) 84 | return s 85 | 86 | def clean(self): 87 | super(QUIC, self).clean() 88 | 89 | def run(self): 90 | cmd = self.getQUICServerCmd() 91 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_before") 92 | self.topo.command_to(self.topo_config.server, cmd) 93 | 94 | if isinstance(self.topo_config, MultiInterfaceMultiClientConfig): 95 | i = 0 96 | for cs in self.topo_config.cong_servers: 97 | cmd = self.getCongServerCmd(i) 98 | self.topo.command_to(cs, cmd) 99 | i = i + 1 100 | 101 | self.topo.command_to(self.topo_config.client, "sleep 2") 102 | 103 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_before") 104 | # First run congestion clients, then the main one 105 | if isinstance(self.topo_config, MultiInterfaceMultiClientConfig): 106 | i = 0 107 | for cc in self.topo_config.cong_clients: 108 | cmd = self.getCongClientCmd(i) 109 | self.topo.command_to(cc, cmd) 110 | i = i + 1 111 | 112 | cmd = self.getQUICClientCmd() 113 | self.topo.command_to(self.topo_config.client, cmd) 114 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_after") 115 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_after") 116 | # Wait for congestion traffic to end 117 | if isinstance(self.topo_config, MultiInterfaceMultiClientConfig): 118 | for cc in self.topo_config.cong_clients: 119 | self.topo.command_to(cc, "while pkill -f wget -0; do sleep 0.5; done") 120 | 121 | self.topo.command_to(self.topo_config.server, "pkill -f " + QUIC.SERVER_GO_FILE) 122 | if isinstance(self.topo_config, MultiInterfaceMultiClientConfig): 123 | for cs in self.topo_config.cong_servers: 124 | self.topo.command_to(cs, "pkill -f https_server.py") 125 | 126 | self.topo.command_to(self.topo_config.client, "sleep 2") 127 | # Need to delete the go-build directory in tmp; could lead to no more space left error 128 | self.topo.command_to(self.topo_config.client, "rm -r /tmp/go-build*") 129 | # Remove cache data 130 | self.topo.command_to(self.topo_config.client, "rm cache_*") 131 | -------------------------------------------------------------------------------- /experiments/quic_siri.py: -------------------------------------------------------------------------------- 1 | from core.experiment import Experiment, ExperimentParameter 2 | import os 3 | 4 | 5 | class QUICSiriParameter(ExperimentParameter): 6 | MULTIPATH = "quicMultipath" 7 | RUN_TIME = "quicSiriRunTime" 8 | 9 | def __init__(self, experiment_parameter_filename): 10 | super(QUICSiriParameter, self).__init__(experiment_parameter_filename) 11 | self.default_parameters.update({ 12 | QUICSiriParameter.MULTIPATH: "0", 13 | }) 14 | 15 | 16 | class QUICSiri(Experiment): 17 | NAME = "quicsiri" 18 | PARAMETER_CLASS = QUICSiriParameter 19 | 20 | GO_BIN = "/usr/local/go/bin/go" 21 | SERVER_LOG = "quic_server.log" 22 | CLIENT_LOG = "quic_client.log" 23 | CLIENT_GO_FILE = "~/go/src/github.com/lucas-clemente/quic-go/example/siri/client/siri.go" 24 | SERVER_GO_FILE = "~/go/src/github.com/lucas-clemente/quic-go/example/siri/siri.go" 25 | PING_OUTPUT = "ping.log" 26 | 27 | def __init__(self, experiment_parameter_filename, topo, topo_config): 28 | super(QUICSiri, self).__init__(experiment_parameter_filename, topo, topo_config) 29 | self.load_parameters() 30 | self.ping() 31 | 32 | def ping(self): 33 | self.topo.command_to(self.topo_config.client, "rm " + \ 34 | QUICSiri.PING_OUTPUT ) 35 | count = self.experiment_parameter.get(ExperimentParameter.PING_COUNT) 36 | for i in range(0, self.topo_config.client_interface_count()): 37 | cmd = self.ping_command(self.topo_config.get_client_ip(i), 38 | self.topo_config.get_server_ip(), n = count) 39 | self.topo.command_to(self.topo_config.client, cmd) 40 | 41 | def ping_command(self, fromIP, toIP, n=5): 42 | s = "ping -c " + str(n) + " -I " + fromIP + " " + toIP + \ 43 | " >> " + QUICSiri.PING_OUTPUT 44 | print(s) 45 | return s 46 | 47 | def load_parameters(self): 48 | self.run_time = self.experiment_parameter.get(QUICSiriParameter.RUN_TIME) 49 | self.multipath = self.experiment_parameter.get(QUICSiriParameter.MULTIPATH) 50 | 51 | def prepare(self): 52 | super(QUICSiri, self).prepare() 53 | self.topo.command_to(self.topo_config.client, "rm " + \ 54 | QUICSiri.CLIENT_LOG ) 55 | self.topo.command_to(self.topo_config.server, "rm " + \ 56 | QUICSiri.SERVER_LOG ) 57 | 58 | def get_quic_siri_server_cmd(self): 59 | s = "{} run {} -addr 0.0.0.0:8080 &> {} &".format(QUICSiri.GO_BIN, 60 | QUICSiri.SERVER_GO_FILE, QUICSiri.SERVER_LOG) 61 | print(s) 62 | return s 63 | 64 | def get_quic_siri_client_cmd(self): 65 | s = "{} run {} -addr {}:8080 -runTime {}s {} &> {}".format(QUICSiri.GO_BIN, 66 | QUICSiri.CLIENT_GO_FILE, self.topo_config.get_server_ip(), self.run_time, 67 | "-m" if int(self.multipath) > 0 else "", QUICSiri.CLIENT_LOG) 68 | print(s) 69 | return s 70 | 71 | def clean(self): 72 | super(QUICSiri, self).clean() 73 | 74 | def run(self): 75 | cmd = self.get_quic_siri_server_cmd() 76 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_before") 77 | self.topo.command_to(self.topo_config.server, cmd) 78 | 79 | self.topo.command_to(self.topo_config.client, "sleep 2") 80 | cmd = self.get_quic_siri_client_cmd() 81 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_before") 82 | self.topo.command_to(self.topo_config.client, cmd) 83 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_after") 84 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_after") 85 | self.topo.command_to(self.topo_config.server, "pkill -f " + QUICSiri.SERVER_GO_FILE) 86 | self.topo.command_to(self.topo_config.client, "sleep 2") 87 | # Need to delete the go-build directory in tmp; could lead to no more space left error 88 | self.topo.command_to(self.topo_config.client, "rm -r /tmp/go-build*") 89 | -------------------------------------------------------------------------------- /experiments/send_file.py: -------------------------------------------------------------------------------- 1 | from core.experiment import RandomFileExperiment, RandomFileParameter, ExperimentParameter 2 | import os 3 | 4 | class SendFile(RandomFileExperiment): 5 | NAME = "sendfile" 6 | 7 | SERVER_LOG = "sendfile_server.log" 8 | CLIENT_LOG = "sendfile_client.log" 9 | WGET_BIN = "./client" 10 | PING_OUTPUT = "ping.log" 11 | 12 | def __init__(self, experiment_parameter_filename, topo, topo_config): 13 | # Just rely on RandomFileExperiment 14 | super(SendFile, self).__init__(experiment_parameter_filename, topo, topo_config) 15 | 16 | def ping(self): 17 | self.topo.command_to(self.topo_config.client, "rm " + \ 18 | SendFile.PING_OUTPUT ) 19 | count = self.experiment_parameter.get(ExperimentParameter.PING_COUNT) 20 | for i in range(0, self.topo_config.client_interface_count()): 21 | cmd = self.ping_command(self.topo_config.get_client_ip(i), 22 | self.topo_config.get_server_ip(), n = count) 23 | self.topo.command_to(self.topo_config.client, cmd) 24 | 25 | def ping_command(self, fromIP, toIP, n=5): 26 | s = "ping -c " + str(n) + " -I " + fromIP + " " + toIP + \ 27 | " >> " + SendFile.PING_OUTPUT 28 | print(s) 29 | return s 30 | 31 | def load_parameters(self): 32 | super(SendFile, self).load_parameters() 33 | 34 | def prepare(self): 35 | super(SendFile, self).prepare() 36 | self.topo.command_to(self.topo_config.client, "rm " + \ 37 | SendFile.CLIENT_LOG ) 38 | self.topo.command_to(self.topo_config.server, "rm " + \ 39 | SendFile.SERVER_LOG ) 40 | 41 | def getSendFileServerCmd(self): 42 | s = "./server &>" + SendFile.SERVER_LOG + "&" 43 | print(s) 44 | return s 45 | 46 | def getSendFileClientCmd(self): 47 | s = SendFile.WGET_BIN + " " + self.topo_config.get_server_ip() + " &>" + SendFile.CLIENT_LOG 48 | print(s) 49 | return s 50 | 51 | def clean(self): 52 | super(SendFile, self).clean() 53 | 54 | def run(self): 55 | cmd = self.getSendFileServerCmd() 56 | self.topo.command_to(self.topo_config.server, cmd) 57 | 58 | self.topo.command_to(self.topo_config.client, "sleep 0.1") 59 | cmd = self.getSendFileClientCmd() 60 | self.topo.command_to(self.topo_config.client, cmd) 61 | self.topo.command_to(self.topo_config.client, "sleep 2") 62 | -------------------------------------------------------------------------------- /experiments/siri.py: -------------------------------------------------------------------------------- 1 | from core.experiment import Experiment, ExperimentParameter 2 | import os 3 | 4 | 5 | class SiriParameter(ExperimentParameter): 6 | RUN_TIME = "siriRunTime" 7 | QUERY_SIZE = "siriQuerySize" 8 | RESPONSE_SIZE = "siriResponseSize" 9 | DELAY_QUERY_RESPONSE = "siriDelayQueryResponse" 10 | MIN_PAYLOAD_SIZE = "siriMinPayloadSize" 11 | MAX_PAYLOAD_SIZE = "siriMaxPayloadSize" 12 | INTERVAL_TIME_MS = "siriIntervalTimeMs" 13 | BUFFER_SIZE = "siriBufferSize" 14 | BURST_SIZE = "siriBurstSize" 15 | INTERVAL_BURST_TIME_MS = "siriIntervalBurstTimeMs" 16 | 17 | def __init__(self, experiment_parameter_filename): 18 | super(SiriParameter, self).__init__(experiment_parameter_filename) 19 | self.default_parameters.update({ 20 | SiriParameter.QUERY_SIZE: "2500", 21 | SiriParameter.RESPONSE_SIZE: "750", 22 | SiriParameter.DELAY_QUERY_RESPONSE: "0", 23 | SiriParameter.MIN_PAYLOAD_SIZE: "85", 24 | SiriParameter.MAX_PAYLOAD_SIZE: "500", 25 | SiriParameter.INTERVAL_TIME_MS: "333", 26 | SiriParameter.BUFFER_SIZE: "9", 27 | SiriParameter.BURST_SIZE: "0", 28 | SiriParameter.INTERVAL_BURST_TIME_MS: "0", 29 | }) 30 | 31 | 32 | class Siri(Experiment): 33 | NAME = "siri" 34 | PARAMETER_CLASS = SiriParameter 35 | 36 | SERVER_LOG = "siri_server.log" 37 | CLIENT_LOG = "siri_client.log" 38 | CLIENT_ERR = "siri_client.err" 39 | JAVA_BIN = "java" 40 | PING_OUTPUT = "ping.log" 41 | 42 | def __init__(self, experiment_parameter_filename, topo, topo_config): 43 | super(Siri, self).__init__(experiment_parameter_filename, topo, topo_config) 44 | self.load_parameters() 45 | self.ping() 46 | 47 | def ping(self): 48 | self.topo.command_to(self.topo_config.client, "rm " + \ 49 | Siri.PING_OUTPUT ) 50 | count = self.experiment_parameter.get(ExperimentParameter.PING_COUNT) 51 | for i in range(0, self.topo_config.client_interface_count()): 52 | cmd = self.ping_command(self.topo_config.get_client_ip(i), 53 | self.topo_config.get_server_ip(), n = count) 54 | self.topo.command_to(self.topo_config.client, cmd) 55 | 56 | def ping_command(self, fromIP, toIP, n=5): 57 | s = "ping -c " + str(n) + " -I " + fromIP + " " + toIP + \ 58 | " >> " + Siri.PING_OUTPUT 59 | print(s) 60 | return s 61 | 62 | def load_parameters(self): 63 | self.run_time = self.experiment_parameter.get(SiriParameter.RUN_TIME) 64 | self.query_size = self.experiment_parameter.get(SiriParameter.QUERY_SIZE) 65 | self.response_size = self.experiment_parameter.get(SiriParameter.RESPONSE_SIZE) 66 | self.delay_query_response = self.experiment_parameter.get(SiriParameter.DELAY_QUERY_RESPONSE) 67 | self.min_payload_size = self.experiment_parameter.get(SiriParameter.MIN_PAYLOAD_SIZE) 68 | self.max_payload_size = self.experiment_parameter.get(SiriParameter.MAX_PAYLOAD_SIZE) 69 | self.interval_time_ms = self.experiment_parameter.get(SiriParameter.INTERVAL_TIME_MS) 70 | self.buffer_size = self.experiment_parameter.get(SiriParameter.BUFFER_SIZE) 71 | self.burst_size = self.experiment_parameter.get(SiriParameter.BURST_SIZE) 72 | self.interval_burst_time_ms = self.experiment_parameter.get(SiriParameter.INTERVAL_BURST_TIME_MS) 73 | 74 | def prepare(self): 75 | super(Siri, self).prepare() 76 | self.topo.command_to(self.topo_config.client, "rm " + \ 77 | Siri.CLIENT_LOG) 78 | self.topo.command_to(self.topo_config.server, "rm " + \ 79 | Siri.SERVER_LOG) 80 | 81 | def get_siri_server_cmd(self): 82 | s = "python3 {}/../utils/siri_server.py &> {}&".format( 83 | os.path.dirname(os.path.abspath(__file__)), Siri.SERVER_LOG) 84 | print(s) 85 | return s 86 | 87 | def get_siri_client_cmd(self): 88 | s = "{} -jar {}/../utils/siriClient.jar {} 8080 {} {} {} {} {} {} {} {} {} {} > {} 2> {}".format( 89 | Siri.JAVA_BIN, os.path.dirname(os.path.abspath(__file__)), self.topo_config.get_server_ip(), 90 | self.run_time, self.query_size, self.response_size, self.delay_query_response, 91 | self.min_payload_size, self.max_payload_size, self.interval_time_ms, self.buffer_size, 92 | self.burst_size, self.interval_burst_time_ms, Siri.CLIENT_LOG, Siri.CLIENT_ERR) 93 | print(s) 94 | return s 95 | 96 | def clean(self): 97 | super(Siri, self).clean() 98 | 99 | def run(self): 100 | cmd = self.get_siri_server_cmd() 101 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_before") 102 | self.topo.command_to(self.topo_config.server, cmd) 103 | 104 | self.topo.command_to(self.topo_config.client, "sleep 2") 105 | cmd = self.get_siri_client_cmd() 106 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_before") 107 | self.topo.command_to(self.topo_config.client, cmd) 108 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_after") 109 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_after") 110 | self.topo.command_to(self.topo_config.server, "pkill -f siri_server.py") 111 | self.topo.command_to(self.topo_config.client, "sleep 2") 112 | -------------------------------------------------------------------------------- /experiments/siri_http.py: -------------------------------------------------------------------------------- 1 | from core.experiment import ExperimentParameter, RandomFileExperiment, RandomFileParameter 2 | from .siri import Siri 3 | import os 4 | 5 | class SiriHTTP(Siri, RandomFileExperiment): 6 | NAME = "sirihttp" 7 | 8 | HTTP_SERVER_LOG = "http_server.log" 9 | HTTP_CLIENT_LOG = "http_client.log" 10 | WGET_BIN = "wget" 11 | SERVER_LOG = "siri_server.log" 12 | CLIENT_LOG = "siri_client.log" 13 | CLIENT_ERR = "siri_client.err" 14 | JAVA_BIN = "java" 15 | PING_OUTPUT = "ping.log" 16 | 17 | def __init__(self, experiment_parameter_filename, topo, topo_config): 18 | # Just rely on RandomFileExperiment 19 | super(SiriHTTP, self).__init__(experiment_parameter_filename, topo, topo_config) 20 | 21 | def ping(self): 22 | self.topo.command_to(self.topo_config.client, "rm " + \ 23 | SiriHTTP.PING_OUTPUT ) 24 | count = self.experiment_parameter.get(ExperimentParameter.PING_COUNT) 25 | for i in range(0, self.topo_config.client_interface_count()): 26 | cmd = self.ping_command(self.topo_config.get_client_ip(i), 27 | self.topo_config.get_server_ip(), n = count) 28 | self.topo.command_to(self.topo_config.client, cmd) 29 | 30 | def ping_command(self, fromIP, toIP, n=5): 31 | s = "ping -c " + str(n) + " -I " + fromIP + " " + toIP + \ 32 | " >> " + SiriHTTP.PING_OUTPUT 33 | print(s) 34 | return s 35 | 36 | def load_parameters(self): 37 | # Start collecting parameters of RandomFileExperiment and Siri 38 | super(SiriHTTP, self).load_parameters() 39 | 40 | def prepare(self): 41 | super(SiriHTTP, self).prepare() 42 | self.topo.command_to(self.topo_config.client, "rm " + \ 43 | SiriHTTP.CLIENT_LOG) 44 | self.topo.command_to(self.topo_config.server, "rm " + \ 45 | SiriHTTP.SERVER_LOG) 46 | self.topo.command_to(self.topo_config.client, "rm " + \ 47 | SiriHTTP.HTTP_CLIENT_LOG) 48 | self.topo.command_to(self.topo_config.server, "rm " + \ 49 | SiriHTTP.HTTP_SERVER_LOG) 50 | 51 | def get_http_server_cmd(self): 52 | s = "/etc/init.d/apache2 restart &>" + SiriHTTP.SERVER_LOG + "&" 53 | print(s) 54 | return s 55 | 56 | def get_http_client_cmd(self): 57 | s = SiriHTTP.WGET_BIN + " http://" + self.topo_config.get_server_ip() + \ 58 | "/" + self.file + " --no-check-certificate" 59 | print(s) 60 | return s 61 | 62 | def clean(self): 63 | super(SiriHTTP, self).clean() 64 | 65 | def run(self): 66 | cmd = self.get_siri_server_cmd() 67 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_before") 68 | self.topo.command_to(self.topo_config.server, cmd) 69 | cmd = self.get_http_server_cmd() 70 | self.topo.command_to(self.topo_config.server, cmd) 71 | 72 | self.topo.command_to(self.topo_config.client, "sleep 2") 73 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_before") 74 | cmd = self.get_http_client_cmd() 75 | self.topo.command_to(self.topo_config.client, "for i in {1..200}; do " + cmd + "; done &") 76 | cmd = self.get_siri_client_cmd() 77 | self.topo.command_to(self.topo_config.client, cmd) 78 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_after") 79 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_after") 80 | self.topo.command_to(self.topo_config.server, "pkill -f siri_server.py") 81 | self.topo.command_to(self.topo_config.client, "sleep 2") 82 | -------------------------------------------------------------------------------- /experiments/siri_msg.py: -------------------------------------------------------------------------------- 1 | from core.experiment import ExperimentParameter 2 | from .siri import Siri, SiriParameter 3 | from .msg import Msg, MsgParameter 4 | import os 5 | 6 | 7 | class SiriMsgParameter(SiriParameter, MsgParameter): 8 | """ 9 | This class is needed because Python has no way to know what we prefer over Siri or 10 | Msg parameters. So explicitly state that we want both. 11 | """ 12 | pass 13 | 14 | 15 | class SiriMsg(Siri, Msg): 16 | NAME = "sirimsg" 17 | PARAMETER_CLASS = SiriMsgParameter 18 | 19 | MSG_SERVER_LOG = "msg_server.log" 20 | MSG_CLIENT_LOG = "msg_client.log" 21 | MSG_CLIENT_ERR = "msg_client.err" 22 | SERVER_LOG = "siri_server.log" 23 | CLIENT_LOG = "siri_client.log" 24 | CLIENT_ERR = "siri_client.err" 25 | JAVA_BIN = "java" 26 | PING_OUTPUT = "ping.log" 27 | 28 | def __init__(self, experiment_parameter_filename, topo, topo_config): 29 | super(SiriMsg, self).__init__(experiment_parameter_filename, topo, topo_config) 30 | 31 | def load_parameters(self): 32 | # Fetch both Msg and Siri parameters 33 | Siri.load_parameters(self) 34 | Msg.load_parameters(self) 35 | 36 | def prepare(self): 37 | # Should be the combination of Siri and Msg 38 | Siri.prepare(self) 39 | Msg.prepare(self) 40 | 41 | def clean(self): 42 | # Should be the combination of Siri and Msg 43 | Siri.clean(self) 44 | Msg.clean(self) 45 | 46 | def run(self): 47 | cmd = self.get_siri_server_cmd() 48 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_before") 49 | self.topo.command_to(self.topo_config.server, cmd) 50 | cmd = self.get_msg_server_cmd() 51 | self.topo.command_to(self.topo_config.server, cmd) 52 | 53 | self.topo.command_to(self.topo_config.client, "sleep 2") 54 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_before") 55 | cmd = self.get_msg_client_cmd(daemon=True) 56 | self.topo.command_to(self.topo_config.client, cmd) 57 | cmd = self.get_siri_client_cmd() 58 | self.topo.command_to(self.topo_config.client, cmd) 59 | self.topo.command_to(self.topo_config.server, "netstat -sn > netstat_server_after") 60 | self.topo.command_to(self.topo_config.client, "netstat -sn > netstat_client_after") 61 | self.topo.command_to(self.topo_config.server, "pkill -f siri_server.py") 62 | self.topo.command_to(self.topo_config.server, "pkill -f msg_server.py") 63 | self.topo.command_to(self.topo_config.server, "pkill -f msg_client.py") 64 | self.topo.command_to(self.topo_config.client, "sleep 2") 65 | -------------------------------------------------------------------------------- /experiments/vlc.py: -------------------------------------------------------------------------------- 1 | from core.experiment import Experiment, ExperimentParameter 2 | import os 3 | 4 | 5 | class VLCParameter(ExperimentParameter): 6 | FILE = "vlcFile" 7 | TIME = "vlcTime" 8 | 9 | def __init__(self, experiment_parameter_filename): 10 | super(VLCParameter, self).__init__(experiment_parameter_filename) 11 | self.default_parameters.update({ 12 | VLCParameter.FILE: "bunny_ibmff_360.mpd", 13 | VLCParameter.TIME: "0", 14 | }) 15 | 16 | class VLC(Experiment): 17 | NAME = "vlc" 18 | 19 | SERVER_LOG = "vlc_server.log" 20 | CLIENT_LOG = "vlc_client.log" 21 | VLC_BIN = "/home/mininet/vlc/vlc" 22 | PING_OUTPUT = "ping.log" 23 | 24 | def __init__(self, experiment_parameter_filename, topo, topo_config): 25 | super(VLC, self).__init__(experiment_parameter_filename, topo, topo_config) 26 | self.load_parameters() 27 | self.ping() 28 | 29 | def ping(self): 30 | self.topo.command_to(self.topo_config.client, "rm " + \ 31 | VLC.PING_OUTPUT ) 32 | count = self.experiment_parameter.get(ExperimentParameter.PING_COUNT) 33 | for i in range(0, self.topo_config.client_interface_count()): 34 | cmd = self.ping_command(self.topo_config.get_client_ip(i), 35 | self.topo_config.get_server_ip(), n = count) 36 | self.topo.command_to(self.topo_config.client, cmd) 37 | 38 | def ping_command(self, fromIP, toIP, n=5): 39 | s = "ping -c " + str(n) + " -I " + fromIP + " " + toIP + \ 40 | " >> " + VLC.PING_OUTPUT 41 | print(s) 42 | return s 43 | 44 | def load_parameters(self): 45 | self.file = self.experiment_parameter.get(VLCParameter.FILE) 46 | self.time = self.experiment_parameter.get(VLCParameter.TIME) 47 | 48 | def prepare(self): 49 | super(VLC, self).prepare() 50 | self.topo.command_to(self.topo_config.client, "rm " + \ 51 | VLC.CLIENT_LOG ) 52 | self.topo.command_to(self.topo_config.client, "Xvfb :66 &") 53 | self.topo.command_to(self.topo_config.server, "rm " + \ 54 | VLC.SERVER_LOG ) 55 | 56 | def get_vlc_server_cmd(self): 57 | s = "/etc/init.d/apache2 restart &> {}".format(VLC.SERVER_LOG) 58 | print(s) 59 | return s 60 | 61 | def get_vlc_client_cmd(self): 62 | s = "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/mininet/usr/lib/ && sudo ldconfig && \ 63 | {} -I dummy --x11-display :66 --adaptive-logic 3 --no-loop --play-and-exit \ 64 | http://{}/{} 2>&1 | grep -E '(Neb|halp|bandwidth|late|Buffering|buffering)' > {} {}".format( 65 | VLC.VLC_BIN, self.topo_config.get_server_ip(), self.file, VLC.CLIENT_LOG, 66 | "&" if self.time != "0" else "") 67 | print(s) 68 | return s 69 | 70 | def clean(self): 71 | super(VLC, self).clean(self) 72 | self.topo.command_to(self.topo_config.client, "pkill Xvfb") 73 | 74 | def run(self): 75 | cmd = self.get_vlc_server_cmd() 76 | self.topo.command_to(self.topo_config.server, cmd) 77 | 78 | self.topo.command_to(self.topo_config.client, "sleep 1") 79 | cmd = self.get_vlc_client_cmd() 80 | self.topo.command_to(self.topo_config.client, cmd) 81 | 82 | if self.time != "0" : 83 | self.topo.command_to(self.topo_config.client, "sleep " + self.time) 84 | self.topo.command_to(self.topo_config.client, "pkill -9 -f vlc") 85 | 86 | self.topo.command_to(self.topo_config.client, "sleep 2") 87 | -------------------------------------------------------------------------------- /mininet_builder.py: -------------------------------------------------------------------------------- 1 | from mininet.topo import Topo 2 | from mininet.net import Mininet 3 | from mininet.link import TCLink 4 | from mininet.node import OVSBridge 5 | from mininet.cli import CLI 6 | from subprocess import Popen, PIPE 7 | 8 | import logging 9 | 10 | class MininetBuilder(Topo): 11 | def __init__(self): 12 | Topo.__init__( self ) 13 | self.net = None 14 | 15 | def command_to(self, who, cmd): 16 | """ 17 | Launch command `cmd` to the specific name space of `who` 18 | """ 19 | return who.cmd(cmd) 20 | 21 | def command_global(self, cmd): 22 | """ 23 | Launch command `cmd` over the global system, i.e., not specific to a name space 24 | """ 25 | p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) 26 | stdout, stderr = p.communicate() 27 | if stderr: 28 | logging.error("Got error when running cmd: {}".format(cmd)) 29 | return "Error" 30 | return stdout 31 | 32 | def start_network(self): 33 | """ 34 | Start the network using Mininet 35 | 36 | Note that the use of OVSBridge avoid facing issues with OVS controllers. 37 | """ 38 | self.net = Mininet(topo=self, link=TCLink, switch=OVSBridge, controller=None) 39 | self.net.start() 40 | 41 | def get_cli(self): 42 | """ 43 | Get the Mininet command line interface 44 | """ 45 | if self.net is None: 46 | logging.error("Cannot get the CLI") 47 | else: 48 | CLI(self.net) 49 | 50 | def get_host(self, who): 51 | if self.net is None: 52 | logging.error("Network not available...") 53 | raise Exception("Network not ready") 54 | else: 55 | return self.net.getNodeByName(who) 56 | 57 | def get_interface_names(self, who): 58 | # NOTE: bs1.intfNames()[0] is lo... 59 | return [i for i in who.intfNames() if i != "lo"] 60 | 61 | def add_host(self, host): 62 | return self.addHost(host) 63 | 64 | def add_switch(self, switch): 65 | return self.addSwitch(switch) 66 | 67 | def add_link(self, from_a, to_b, **kwargs): 68 | return self.addLink(from_a, to_b, **kwargs) 69 | 70 | def stop_network(self): 71 | if self.net is None: 72 | logging.warning("Unable to stop the network: net is None") 73 | else: 74 | self.net.stop() 75 | -------------------------------------------------------------------------------- /runner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from core.experiment import Experiment, ExperimentParameter, ExperimentParameter 4 | from core.topo import Topo, TopoParameter 5 | 6 | from mininet_builder import MininetBuilder 7 | from mininet.clean import cleanup 8 | 9 | from experiments import EXPERIMENTS 10 | from topos import TOPO_CONFIGS, TOPOS 11 | 12 | import logging 13 | import os 14 | import subprocess 15 | import traceback 16 | 17 | def get_git_revision_short_hash(): 18 | # Because we might run Minitopo from elsewhere. 19 | curr_dir = os.getcwd() 20 | ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) 21 | os.chdir(ROOT_DIR) 22 | ret = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).decode("unicode_escape").strip() 23 | os.chdir(curr_dir) 24 | return ret 25 | 26 | class Runner(object): 27 | """ 28 | Run an experiment described by `experiment_parameter_file` in the topology 29 | described by `topo_parameter_file` in the network environment built by 30 | `builder_type`. 31 | 32 | All the operations are done when calling the constructor. 33 | """ 34 | def __init__(self, builder_type, topo_parameter_file, experiment_parameter_file): 35 | logging.info("Minitopo version {}".format(get_git_revision_short_hash())) 36 | self.topo_parameter = TopoParameter(topo_parameter_file) 37 | self.set_builder(builder_type) 38 | self.apply_topo() 39 | self.apply_topo_config() 40 | self.start_topo() 41 | self.run_experiment(experiment_parameter_file) 42 | self.stop_topo() 43 | 44 | def set_builder(self, builder_type): 45 | """ 46 | Currently the only builder type supported is Mininet... 47 | """ 48 | if builder_type == Topo.MININET_BUILDER: 49 | self.topo_builder = MininetBuilder() 50 | else: 51 | raise Exception("I can not find the builder {}".format(builder_type)) 52 | 53 | def apply_topo(self): 54 | """ 55 | Matches the name of the topo and find the corresponding Topo class. 56 | """ 57 | t = self.topo_parameter.get(Topo.TOPO_ATTR) 58 | if t in TOPOS: 59 | self.topo = TOPOS[t](self.topo_builder, self.topo_parameter) 60 | else: 61 | raise Exception("Unknown topo: {}".format(t)) 62 | 63 | logging.info("Using topo {}".format(self.topo)) 64 | 65 | def apply_topo_config(self): 66 | """ 67 | Match the name of the topo and find the corresponding TopoConfig class. 68 | """ 69 | t = self.topo_parameter.get(Topo.TOPO_ATTR) 70 | if t in TOPO_CONFIGS: 71 | self.topo_config = TOPO_CONFIGS[t](self.topo, self.topo_parameter) 72 | else: 73 | raise Exception("Unknown topo config: {}".format(t)) 74 | 75 | logging.info("Using topo config {}".format(self.topo_config)) 76 | 77 | def start_topo(self): 78 | """ 79 | Initialize the topology with its configuration 80 | """ 81 | self.topo.start_network() 82 | self.topo_config.configure_network() 83 | 84 | def run_experiment(self, experiment_parameter_file): 85 | """ 86 | Match the name of the experiement and launch it 87 | """ 88 | # Well, we need to load twice the experiment parameters, is it really annoying? 89 | xp = ExperimentParameter(experiment_parameter_file).get(ExperimentParameter.XP_TYPE) 90 | if xp in EXPERIMENTS: 91 | exp = EXPERIMENTS[xp](experiment_parameter_file, self.topo, self.topo_config) 92 | exp.classic_run() 93 | else: 94 | raise Exception("Unknown experiment {}".format(xp)) 95 | 96 | def stop_topo(self): 97 | """ 98 | Stop the topology 99 | """ 100 | self.topo.stop_network() 101 | 102 | 103 | if __name__ == '__main__': 104 | import argparse 105 | 106 | parser = argparse.ArgumentParser( 107 | description="Minitopo, a wrapper of Mininet to run multipath experiments") 108 | 109 | parser.add_argument("--topo_param_file", "-t", required=True, 110 | help="path to the topo parameter file") 111 | parser.add_argument("--experiment_param_file", "-x", 112 | help="path to the experiment parameter file") 113 | 114 | args = parser.parse_args() 115 | 116 | logging.basicConfig(format="%(asctime)-15s [%(levelname)s] %(funcName)s: %(message)s", level=logging.INFO) 117 | 118 | # XXX Currently, there is no alternate topo builder... 119 | try: 120 | Runner(Topo.MININET_BUILDER, args.topo_param_file, args.experiment_param_file) 121 | except Exception as e: 122 | logging.fatal("A fatal error occurred: {}".format(e)) 123 | traceback.print_exc() 124 | finally: 125 | # Always cleanup Mininet 126 | logging.info("cleanup mininet") 127 | cleanup() -------------------------------------------------------------------------------- /topos/__init__.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | import pkgutil 3 | import os 4 | 5 | from core.topo import Topo, TopoConfig 6 | 7 | pkg_dir = os.path.dirname(__file__) 8 | for (module_loader, name, ispkg) in pkgutil.iter_modules([pkg_dir]): 9 | importlib.import_module('.' + name, __package__) 10 | 11 | # Track indirect inheritance 12 | TOPO_CONFIGS = {} 13 | TOPOS = {} 14 | 15 | def _get_all_subclasses(BaseClass, dico): 16 | for cls in BaseClass.__subclasses__(): 17 | if hasattr(cls, "NAME"): 18 | dico[cls.NAME] = cls 19 | 20 | _get_all_subclasses(cls, dico) 21 | 22 | _get_all_subclasses(TopoConfig, TOPO_CONFIGS) 23 | _get_all_subclasses(Topo, TOPOS) -------------------------------------------------------------------------------- /topos/multi_interface.py: -------------------------------------------------------------------------------- 1 | from core.topo import Topo, TopoConfig, TopoParameter 2 | import logging 3 | 4 | 5 | class MultiInterfaceTopo(Topo): 6 | NAME = "MultiIf" 7 | 8 | def __init__(self, topo_builder, parameterFile): 9 | logging.info("Initializing MultiInterfaceTopo...") 10 | super(MultiInterfaceTopo, self).__init__(topo_builder, parameterFile) 11 | self.client = self.add_client() 12 | self.server = self.add_server() 13 | self.router = self.add_router() 14 | self.c2r_links = [] 15 | self.r2s_links = [] 16 | 17 | # Add client - router links 18 | for l in self.get_client_to_router_links(): 19 | self.c2r_links.append(self.add_bottleneck_link(self.client, self.router, link_characteristics=l)) 20 | 21 | # Special case: if there is no specified link between router and server, directly connect them! 22 | if len(self.get_router_to_server_links()) > 0: 23 | for l in self.get_router_to_server_links(): 24 | self.r2s_links.append(self.add_bottleneck_link(self.router, self.server, link_characteristics=l)) 25 | else: 26 | self.add_link(self.router, self.server) 27 | 28 | 29 | def get_client_to_router_links(self): 30 | return [l for l in self.topo_parameter.link_characteristics if l.link_type == "c2r"] 31 | 32 | def get_router_to_server_links(self): 33 | return [l for l in self.topo_parameter.link_characteristics if l.link_type == "r2s"] 34 | 35 | def __str__(self): 36 | s = "Simple multiple interface topology \n" 37 | i = 0 38 | nc = len(self.get_client_to_router_links()) 39 | ns = len(self.get_router_to_server_links()) 40 | m = max(nc, ns) 41 | skipped = 0 42 | for i in range(0, m): 43 | if i == m // 2: 44 | if m % 2 == 0: 45 | s = s + "c r s\n" 46 | s = s + " \-sw---bl---sw-/ \-sw---bl---sw-/\n" 47 | else: 48 | s = s + "c--sw---bl---sw--r--sw---bl---sw--s\n" 49 | else: 50 | if i < m // 2: 51 | if (nc == m and ns + skipped == m) or (ns == m and nc + skipped == m): 52 | s = s + " /-sw---bl---sw-\ /-sw---bl---sw-\ \n" 53 | elif nc == m: 54 | s = s + " /-sw---bl---sw-\ \n" 55 | skipped += 1 56 | else: 57 | s = s + " /-sw---bl---sw-\ \n" 58 | skipped += 1 59 | else: 60 | if (nc == m and ns + skipped == m) or (ns == m and nc + skipped == m): 61 | s = s + " \-sw---bl---sw-/ \-sw---bl---sw-/ \n" 62 | elif nc == m: 63 | s = s + " \-sw---bl---sw-/ \n" 64 | skipped += 1 65 | else: 66 | s = s + " \-sw---bl---sw-/ \n" 67 | skipped += 1 68 | 69 | return s 70 | 71 | 72 | class MultiInterfaceConfig(TopoConfig): 73 | NAME = "MultiIf" 74 | 75 | def __init__(self, topo, param): 76 | super(MultiInterfaceConfig, self).__init__(topo, param) 77 | 78 | def configure_routing(self): 79 | for i, _ in enumerate(self.topo.c2r_links): 80 | cmd = self.add_table_route_command(self.get_client_ip(i), i) 81 | self.topo.command_to(self.client, cmd) 82 | 83 | cmd = self.add_link_scope_route_command( 84 | self.get_client_subnet(i), 85 | self.get_client_interface(0, i), i) 86 | self.topo.command_to(self.client, cmd) 87 | 88 | cmd = self.add_table_default_route_command(self.get_router_ip_to_client_switch(i), 89 | i) 90 | self.topo.command_to(self.client, cmd) 91 | 92 | for i, _ in enumerate(self.topo.r2s_links): 93 | cmd = self.add_table_route_command(self.get_server_ip(i), i) 94 | self.topo.command_to(self.server, cmd) 95 | 96 | cmd = self.add_link_scope_route_command( 97 | self.get_server_subnet(i), 98 | self.get_server_interface(0, i), i) 99 | self.topo.command_to(self.server, cmd) 100 | 101 | cmd = self.add_table_default_route_command(self.get_router_ip_to_server_switch(i), 102 | i) 103 | self.topo.command_to(self.server, cmd) 104 | 105 | cmd = self.add_global_default_route_command(self.get_router_ip_to_client_switch(0), 106 | self.get_client_interface(0, 0)) 107 | self.topo.command_to(self.client, cmd) 108 | 109 | cmd = self.add_simple_default_route_command(self.get_router_ip_to_server_switch(0)) 110 | self.topo.command_to(self.server, cmd) 111 | 112 | 113 | def configure_interfaces(self): 114 | logging.info("Configure interfaces using MultiInterfaceConfig...") 115 | super(MultiInterfaceConfig, self).configure_interfaces() 116 | self.client = self.topo.get_client(0) 117 | self.server = self.topo.get_server(0) 118 | self.router = self.topo.get_router(0) 119 | netmask = "255.255.255.0" 120 | 121 | for i, _ in enumerate(self.topo.c2r_links): 122 | cmd = self.interface_up_command(self.get_client_interface(0, i), self.get_client_ip(i), netmask) 123 | self.topo.command_to(self.client, cmd) 124 | client_interface_mac = self.client.intf(self.get_client_interface(0, i)).MAC() 125 | self.topo.command_to(self.router, "arp -s {} {}".format(self.get_client_ip(i), client_interface_mac)) 126 | 127 | if self.topo.get_client_to_router_links()[i].backup: 128 | cmd = self.interface_backup_command(self.get_client_interface(0, i)) 129 | self.topo.command_to(self.client, cmd) 130 | 131 | for i, _ in enumerate(self.topo.c2r_links): 132 | cmd = self.interface_up_command(self.get_router_interface_to_client_switch(i), 133 | self.get_router_ip_to_client_switch(i), netmask) 134 | self.topo.command_to(self.router, cmd) 135 | router_interface_mac = self.router.intf(self.get_router_interface_to_client_switch(i)).MAC() 136 | self.topo.command_to(self.client, "arp -s {} {}".format( 137 | self.get_router_ip_to_client_switch(i), router_interface_mac)) 138 | 139 | if len(self.topo.r2s_links) == 0: 140 | # Case no server param is specified 141 | cmd = self.interface_up_command(self.get_router_interface_to_server_switch(0), 142 | self.get_router_ip_to_server_switch(0), netmask) 143 | self.topo.command_to(self.router, cmd) 144 | router_interface_mac = self.router.intf(self.get_router_interface_to_server_switch(0)).MAC() 145 | self.topo.command_to(self.server, "arp -s {} {}".format( 146 | self.get_router_ip_to_server_switch(0), router_interface_mac)) 147 | 148 | cmd = self.interface_up_command(self.get_server_interface(0, 0), self.get_server_ip(0), netmask) 149 | self.topo.command_to(self.server, cmd) 150 | server_interface_mac = self.server.intf(self.get_server_interface(0, 0)).MAC() 151 | self.topo.command_to(self.router, "arp -s {} {}".format( 152 | self.get_server_ip(0), server_interface_mac)) 153 | 154 | for i, _ in enumerate(self.topo.r2s_links): 155 | cmd = self.interface_up_command(self.get_router_interface_to_server_switch(i), 156 | self.get_router_ip_to_server_switch(i), netmask) 157 | self.topo.command_to(self.router, cmd) 158 | router_interface_mac = self.router.intf(self.get_router_interface_to_server_switch(i)).MAC() 159 | self.topo.command_to(self.server, "arp -s {} {}".format( 160 | self.get_router_ip_to_server_switch(i), router_interface_mac)) 161 | 162 | for i, _ in enumerate(self.topo.r2s_links): 163 | cmd = self.interface_up_command(self.get_server_interface(0, i), self.get_server_ip(i), netmask) 164 | self.topo.command_to(self.server, cmd) 165 | server_interface_mac = self.server.intf(self.get_server_interface(0, i)).MAC() 166 | self.topo.command_to(self.router, "arp -s {} {}".format( 167 | self.get_server_ip(i), server_interface_mac)) 168 | 169 | def get_client_ip(self, interface_index): 170 | return "{}{}.1".format(self.param.get(TopoParameter.LEFT_SUBNET), interface_index) 171 | 172 | def get_client_subnet(self, interface_index): 173 | return "{}{}.0/24".format(self.param.get(TopoParameter.LEFT_SUBNET), interface_index) 174 | 175 | def get_router_ip_to_client_switch(self, switch_index): 176 | return "{}{}.2".format(self.param.get(TopoParameter.LEFT_SUBNET), switch_index) 177 | 178 | def get_router_ip_to_server_switch(self, switch_index): 179 | return "{}{}.2".format(self.param.get(TopoParameter.RIGHT_SUBNET), switch_index) 180 | 181 | def get_server_ip(self, interface_index=0): 182 | return "{}{}.1".format(self.param.get(TopoParameter.RIGHT_SUBNET), interface_index) 183 | 184 | def get_server_subnet(self, interface_index): 185 | return "{}{}.0/24".format(self.param.get(TopoParameter.RIGHT_SUBNET), interface_index) 186 | 187 | def client_interface_count(self): 188 | return max(len(self.topo.c2r_links), 1) 189 | 190 | def server_interface_count(self): 191 | return max(len(self.topo.r2s_links), 1) 192 | 193 | def get_router_interface_to_server_switch(self, switch_index): 194 | return self.get_router_interface_to_client_switch(len(self.topo.c2r_links) + switch_index) 195 | 196 | def get_client_interface(self, client_index, interface_index): 197 | return "{}-eth{}".format(self.topo.get_client_name(client_index), interface_index) 198 | 199 | def get_router_interface_to_client_switch(self, interface_index): 200 | return "{}-eth{}".format(self.topo.get_router_name(0), interface_index) 201 | 202 | def get_server_interface(self, server_index, interface_index): 203 | return "{}-eth{}".format(self.topo.get_server_name(server_index), interface_index) -------------------------------------------------------------------------------- /topos/multi_interface_multi_client.py: -------------------------------------------------------------------------------- 1 | from core.topo import TopoParameter 2 | from .multi_interface import MultiInterfaceTopo, MultiInterfaceConfig 3 | import logging 4 | 5 | 6 | class MultiInterfaceMultiClientTopo(MultiInterfaceTopo): 7 | NAME = "MultiIfMultiClient" 8 | 9 | def __init__(self, topo_builder, parameterFile): 10 | logging.info("Initializing MultiInterfaceMultiClientTopo...") 11 | super(MultiInterfaceMultiClientTopo, self).__init__(topo_builder, parameterFile) 12 | 13 | # For each client-router, add a client, a bottleneck link, and a server 14 | for bl in self.c2r_links: 15 | client = self.add_client() 16 | self.add_server() 17 | self.add_link(client, bl.get_left()) 18 | 19 | # And connect the router to all servers 20 | for s in self.servers[1:]: 21 | self.add_link(self.router, s) 22 | 23 | def __str__(self): 24 | s = "Multiple interface topology with several clients and servers\n" 25 | i = 0 26 | nc = len(self.get_client_to_router_links()) 27 | for i in range(0, nc): 28 | if i == nc // 2: 29 | s = s + "c- r--s\n" 30 | s = s + "c-\sw---bl---sw-/ \-s\n" 31 | else: 32 | s = s + "c-/sw---bl---sw-\ /-s\n" 33 | 34 | return s 35 | 36 | 37 | class MultiInterfaceMultiClientConfig(MultiInterfaceConfig): 38 | NAME = "MultiIfMultiClient" 39 | 40 | def __init__(self, topo, param): 41 | super(MultiInterfaceMultiClientConfig, self).__init__(topo, param) 42 | 43 | def configure_routing(self): 44 | super(MultiInterfaceMultiClientConfig, self).configure_routing() 45 | for i, _ in enumerate(self.topo.c2r_links): 46 | # Routing for the congestion client 47 | cmd = self.add_global_default_route_command(self.get_router_ip_to_client_switch(i), 48 | self.get_client_interface(i+1, 0)) 49 | self.topo.command_to(self.clients[i+1], cmd) 50 | 51 | for i, s in enumerate(self.topo.servers): 52 | # Routing for the congestion server 53 | cmd = self.add_simple_default_route_command(self.get_router_ip_to_server_switch(i)) 54 | self.topo.command_to(s, cmd) 55 | 56 | def configure_interfaces(self): 57 | logging.info("Configure interfaces using MultiInterfaceMultiClientConfig...") 58 | super(MultiInterfaceMultiClientConfig, self).configure_interfaces() 59 | self.clients = [self.topo.get_client(i) for i in range(0, self.topo.client_count())] 60 | self.servers = [self.topo.get_server(i) for i in range(0, self.topo.server_count())] 61 | netmask = "255.255.255.0" 62 | 63 | for i, _ in enumerate(self.topo.c2r_links): 64 | # Congestion client 65 | cmd = self.interface_up_command(self.get_client_interface(i + 1, 0), self.get_client_ip(i, congestion_client=True), netmask) 66 | self.topo.command_to(self.clients[i+1], cmd) 67 | client_interface_mac = self.clients[i+1].intf(self.get_client_interface(i + 1, 0)).MAC() 68 | self.topo.command_to(self.router, "arp -s {} {}".format(self.get_client_ip(i, congestion_client=True), client_interface_mac)) 69 | 70 | router_interface_mac = self.router.intf(self.get_router_interface_to_client_switch(i)).MAC() 71 | # Congestion client 72 | self.topo.command_to(self.clients[i+1], "arp -s {} {}".format( 73 | self.get_router_ip_to_client_switch(i), router_interface_mac)) 74 | 75 | for i, s in enumerate(self.servers): 76 | cmd = self.interface_up_command(self.get_router_interface_to_server_switch(i), 77 | self.get_router_ip_to_server_switch(i), netmask) 78 | self.topo.command_to(self.router, cmd) 79 | router_interface_mac = self.router.intf(self.get_router_interface_to_server_switch(i)).MAC() 80 | self.topo.command_to(s, "arp -s {} {}".format( 81 | self.get_router_ip_to_server_switch(i), router_interface_mac)) 82 | cmd = self.interface_up_command(self.get_server_interface(i, 0), self.get_server_ip(interface_index=i), netmask) 83 | self.topo.command_to(s, cmd) 84 | server_interface_mac = s.intf(self.get_server_interface(i, 0)).MAC() 85 | self.topo.command_to(self.router, "arp -s {} {}".format( 86 | self.get_server_ip(interface_index=i), server_interface_mac)) 87 | 88 | def get_client_ip(self, interface_index, congestion_client=False): 89 | return "{}{}.{}".format(self.param.get(TopoParameter.LEFT_SUBNET), interface_index, "10" if congestion_client else "1") 90 | 91 | def server_interface_count(self): 92 | return max(len(self.servers), 1) -------------------------------------------------------------------------------- /utils/SiriClient.java: -------------------------------------------------------------------------------- 1 | import java.io.*; 2 | import java.net.InetAddress; 3 | import java.net.NetworkInterface; 4 | import java.net.Socket; 5 | import java.net.SocketException; 6 | import java.nio.charset.StandardCharsets; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import java.util.Random; 11 | import java.util.Collections; 12 | 13 | public class SiriClient { 14 | 15 | private String serverMessage; 16 | 17 | /* Client parameters */ 18 | public final String SERVERIP; //your computer IP address 19 | public final int SERVERPORT; 20 | public final int RUN_TIME; // In seconds 21 | public final int QUERY_SIZE; 22 | public final int RESPONSE_SIZE; 23 | public final int DELAY_QUERY_RESPONSE; 24 | public final int MIN_PAYLOAD_SIZE; 25 | public final int MAX_PAYLOAD_SIZE; 26 | public final int INTERVAL_TIME_MS; 27 | public final int BUFFER_SIZE; 28 | public final int BURST_SIZE; 29 | public final int INTERVAL_BURST_TIME_MS; 30 | 31 | private boolean mRun = false; 32 | private int messageId = 0; 33 | private static final int MAX_ID = 100; 34 | 35 | private Random random; 36 | private long[] sentTime; 37 | private List delayTime; 38 | private String mac; 39 | private int counter; 40 | private int missed; 41 | PrintWriter out; 42 | BufferedReader in; 43 | OutputStream outputStream; 44 | OutputStreamWriter osw; 45 | Socket socket; 46 | private int pktCounter; 47 | 48 | /** 49 | * Constructor of the class. OnMessagedReceived listens for the messages received from server 50 | */ 51 | public SiriClient(String serverIp, int serverPort, int runTime, int querySize, int responseSize, 52 | int delayQueryResponse, int minPayloadSize, int maxPayloadSize, int intervalTimeMs, 53 | int bufferSize, int burstSize, int intervalBurstTimeMs) { 54 | random = new Random(); 55 | sentTime = new long[MAX_ID]; 56 | delayTime = new ArrayList<>(); 57 | counter = 0; 58 | missed = 0; 59 | pktCounter = 0; 60 | /* Client parameters */ 61 | SERVERIP = serverIp; 62 | SERVERPORT = serverPort; 63 | RUN_TIME = runTime; 64 | QUERY_SIZE = querySize; 65 | RESPONSE_SIZE = responseSize; 66 | DELAY_QUERY_RESPONSE = delayQueryResponse; 67 | MIN_PAYLOAD_SIZE = minPayloadSize; 68 | MAX_PAYLOAD_SIZE = maxPayloadSize; 69 | INTERVAL_TIME_MS = intervalTimeMs; 70 | BUFFER_SIZE = bufferSize; 71 | BURST_SIZE = burstSize; 72 | INTERVAL_BURST_TIME_MS = intervalBurstTimeMs; 73 | } 74 | 75 | public SiriClient(String serverIp, int serverPort, int runTime) { 76 | this(serverIp, serverPort, runTime, 2500, 750, 0, 85, 500, 333, 9, 0, 0); 77 | } 78 | 79 | protected String getStringWithLengthAndFilledWithCharacter(int length, char charToFill) { 80 | if (length > 0) { 81 | char[] array = new char[length]; 82 | Arrays.fill(array, charToFill); 83 | return new String(array); 84 | } 85 | return ""; 86 | } 87 | 88 | /** 89 | * Return a random number in [@MIN_PAYLOAD_SIZE, @MAX_PAYLOAD_SIZE] 90 | * It manages 3 cases: 91 | * 1) remainToSend in [@MIN_PAYLOAD_SIZE, @MAX_PAYLOAD_SIZE]: return remainToSend 92 | * 2) remainToSend - @MAX_PAYLOAD_SIZE < MIN_PAYLOAD_SIZE: return random value in 93 | * [@MIN_PAYLOAD_SIZE, @MAX_PAYLOAD_SIZE - @MIN_PAYLOAD_SIZE] 94 | * 3) else, return random value in [@MIN_PAYLOAD_SIZE, @MAX_PAYLOAD_SIZE] 95 | * @param remainToSend number of remaining bytes to send >= MIN_PAYLOAD_SIZE 96 | */ 97 | private int sizeOfNextPacket(int remainToSend) { 98 | if (remainToSend < MIN_PAYLOAD_SIZE) throw new AssertionError(); 99 | 100 | // Case 1) 101 | if (remainToSend <= MAX_PAYLOAD_SIZE && remainToSend >= MIN_PAYLOAD_SIZE) { 102 | return remainToSend; 103 | } 104 | 105 | int randomPart; 106 | 107 | // Case 2) 108 | if (remainToSend - MAX_PAYLOAD_SIZE < MIN_PAYLOAD_SIZE) { 109 | randomPart = random.nextInt(MAX_PAYLOAD_SIZE - 2 * MIN_PAYLOAD_SIZE + 1); 110 | } 111 | // Case 3) 112 | else { 113 | randomPart = random.nextInt(MAX_PAYLOAD_SIZE - MIN_PAYLOAD_SIZE + 1); 114 | } 115 | 116 | return MIN_PAYLOAD_SIZE + randomPart; 117 | } 118 | 119 | /** 120 | * Get a random value following a Poisson distribution of mean lambda 121 | * @param lambda mean of the Poisson distribution 122 | * @return random value following a Poisson distribution of mean lambda 123 | */ 124 | public static int getPoisson(double lambda) { 125 | double L = Math.exp(-lambda); 126 | double p = 1.0; 127 | int k = 0; 128 | 129 | do { 130 | k++; 131 | p *= Math.random(); 132 | } while (p > L); 133 | 134 | return k - 1; 135 | } 136 | 137 | /** 138 | * Sends the message entered by client to the server 139 | */ 140 | public void sendMessage() { 141 | // if (out != null && !out.checkError() && osw != null) { 142 | if (socket != null && !socket.isClosed()) { 143 | int remainToBeSent = QUERY_SIZE; 144 | // If the server has a DB, use it, otherwise set to 0 145 | //int delaysToSend = delayTime.size(); 146 | int delaysToSend = 0; 147 | StringBuffer sb = new StringBuffer(); 148 | // for (int i = 0; i < delaysToSend; i++) { 149 | // sb.append(delayTime.get(0) + "&"); 150 | // delayTime.remove(delayTime.get(0)); 151 | // } 152 | sentTime[messageId] = System.currentTimeMillis(); 153 | String startString = messageId + "&" + QUERY_SIZE + "&" + RESPONSE_SIZE + "&" + 154 | DELAY_QUERY_RESPONSE + "&" + delaysToSend + "&" + sentTime[messageId] + 155 | "&" + mac + "&" + sb.toString(); 156 | messageId = (messageId + 1) % MAX_ID; 157 | int bytesToSend = Math.max(startString.length(), sizeOfNextPacket(remainToBeSent)); 158 | // System.err.println("BytesToSend: " + bytesToSend); 159 | byte[] toSend; 160 | try { 161 | //osw.write(startString + getStringWithLengthAndFilledWithCharacter(bytesToSend - startString.length(), '0')); 162 | //osw.flush(); 163 | toSend = (startString + getStringWithLengthAndFilledWithCharacter(bytesToSend - startString.length(), '0')).getBytes(StandardCharsets.US_ASCII); 164 | outputStream.write(toSend); 165 | outputStream.flush(); 166 | } catch (IOException e) { 167 | System.err.println("ERROR: OUTPUT STREAM ERROR"); 168 | } 169 | // out.println(startString + getStringWithLengthAndFilledWithCharacter(bytesToSend - startString.length() - 1, '0')); 170 | // System.err.println("Sent " + startString + getStringWithLengthAndFilledWithCharacter(bytesToSend - startString.length() - 1, '0')); 171 | // out.flush(); 172 | remainToBeSent -= bytesToSend; 173 | synchronized (this) { 174 | counter += bytesToSend; 175 | } 176 | 177 | try { 178 | while(remainToBeSent > 0) { 179 | bytesToSend = sizeOfNextPacket(remainToBeSent); 180 | //out.println(getStringWithLengthAndFilledWithCharacter(bytesToSend - 1, '0')); 181 | //out.flush(); 182 | 183 | if (remainToBeSent == bytesToSend) { 184 | //osw.write(getStringWithLengthAndFilledWithCharacter(bytesToSend - 1, '0') + "\n"); 185 | toSend = (getStringWithLengthAndFilledWithCharacter(bytesToSend - 1, '0') + "\n").getBytes(StandardCharsets.US_ASCII); 186 | } else { 187 | //osw.write(getStringWithLengthAndFilledWithCharacter(bytesToSend, '0')); 188 | toSend = getStringWithLengthAndFilledWithCharacter(bytesToSend, '0').getBytes(StandardCharsets.US_ASCII); 189 | } 190 | //osw.flush(); 191 | outputStream.write(toSend); 192 | outputStream.flush(); 193 | 194 | remainToBeSent -= bytesToSend; 195 | synchronized (this) { 196 | counter += bytesToSend; 197 | } 198 | } 199 | } catch (IOException e) { 200 | System.err.println("ERROR: OUTPUT STREAM ERROR"); 201 | e.printStackTrace(); 202 | } 203 | } 204 | } 205 | 206 | public void stopClient(){ 207 | mRun = false; 208 | if (socket != null) { 209 | try { 210 | socket.close(); 211 | } catch (IOException e) { 212 | e.printStackTrace(); 213 | } 214 | } 215 | } 216 | 217 | public static String getIPAddress(boolean useIPv4) { 218 | try { 219 | List interfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); 220 | for (NetworkInterface intf : interfaces) { 221 | List addrs = Collections.list(intf.getInetAddresses()); 222 | for (InetAddress addr : addrs) { 223 | if (!addr.isLoopbackAddress()) { 224 | String sAddr = addr.getHostAddress(); 225 | //boolean isIPv4 = InetAddressUtils.isIPv4Address(sAddr); 226 | boolean isIPv4 = sAddr.indexOf(':')<0; 227 | 228 | if (useIPv4) { 229 | if (isIPv4) 230 | return sAddr; 231 | } else { 232 | if (!isIPv4) { 233 | int delim = sAddr.indexOf('%'); // drop ip6 zone suffix 234 | return delim<0 ? sAddr.toUpperCase() : sAddr.substring(0, delim).toUpperCase(); 235 | } 236 | } 237 | } 238 | } 239 | } 240 | } catch (Exception ex) { } // for now eat exceptions 241 | return ""; 242 | } 243 | 244 | public void run(String macWifi) { 245 | 246 | mRun = true; 247 | mac = macWifi; 248 | 249 | try { 250 | //here you must put your computer's IP address. 251 | InetAddress serverAddr = InetAddress.getByName(SERVERIP); 252 | System.err.println("Me: " + getIPAddress(true)); 253 | 254 | System.err.println("TCP Client: Connecting..."); 255 | 256 | //create a socket to make the connection with the server 257 | socket = new Socket(serverAddr, SERVERPORT); 258 | // Needed to emulate any traffic 259 | socket.setTcpNoDelay(true); 260 | 261 | new Thread(new Runnable() { 262 | public void run() { 263 | final long startTime = System.currentTimeMillis(); 264 | while (socket != null && !socket.isClosed()) { 265 | try { 266 | if (BURST_SIZE > 0 && pktCounter == BURST_SIZE) { 267 | Thread.sleep(INTERVAL_BURST_TIME_MS); 268 | pktCounter = 0; 269 | } else { 270 | Thread.sleep(INTERVAL_TIME_MS); //* getPoisson(3)); 271 | } 272 | if ((System.currentTimeMillis() - startTime) >= RUN_TIME * 1000) { 273 | stopClient(); 274 | } else if (!socket.isClosed() && counter <= QUERY_SIZE * BUFFER_SIZE) { 275 | sendMessage(); 276 | pktCounter++; 277 | } else if (!socket.isClosed()) { 278 | missed++; 279 | } 280 | } catch (InterruptedException e) { 281 | e.printStackTrace(); 282 | } 283 | } 284 | 285 | 286 | } 287 | }).start(); 288 | 289 | 290 | try { 291 | 292 | //send the message to the server 293 | out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true); 294 | outputStream = socket.getOutputStream(); 295 | osw = new OutputStreamWriter(socket.getOutputStream()); 296 | 297 | System.err.println("TCP Client: Done."); 298 | 299 | //receive the message which the server sends back 300 | in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 301 | 302 | //in this while the client listens for the messages sent by the server 303 | while (mRun) { 304 | serverMessage = in.readLine(); 305 | long receivedTime = System.currentTimeMillis(); 306 | // System.err.println("SERVER MESSAGE: " + ((serverMessage == null) ? "" : serverMessage)); 307 | if (serverMessage != null) { 308 | int id = Integer.parseInt(serverMessage.split("&")[0]); 309 | // System.err.println("ELAPSED TIME: " + (receivedTime - sentTime[id])); 310 | delayTime.add(receivedTime - sentTime[id]); 311 | synchronized (this) { 312 | counter -= QUERY_SIZE; 313 | } 314 | 315 | } 316 | serverMessage = null; 317 | 318 | } 319 | 320 | //System.err.println("RESPONSE FROM SERVER: Received Message: '" + serverMessage + "'"); 321 | 322 | } catch (SocketException e) { 323 | System.err.println("TCP: Socket closed"); 324 | } catch (Exception e) { 325 | System.err.println("TCP: Error " + e); 326 | 327 | } finally { 328 | //the socket must be closed. It is not possible to reconnect to this socket 329 | // after it is closed, which means a new socket instance has to be created. 330 | socket.close(); 331 | } 332 | 333 | } catch (Exception e) { 334 | 335 | System.err.println("TCP C: Error" + e); 336 | 337 | } 338 | 339 | } 340 | 341 | public static void usage() { 342 | System.out.println("Usage: siriClient serverIP serverPort runTime [querySize responseSize delayQueryResponse " 343 | + "minPayloadSize maxPayloadSize intervalTimeMs bufferSize burstSize intervalBurstTimeMs]"); 344 | } 345 | 346 | public static void main(String[] args) { 347 | if (args.length != 3 && args.length != 12) { 348 | usage(); 349 | System.exit(1); 350 | } 351 | String serverIp = args[0]; 352 | int serverPort = Integer.parseInt(args[1]); 353 | int runTime = Integer.parseInt(args[2]); 354 | SiriClient siriClient; 355 | 356 | if (args.length == 12) { 357 | int querySize = Integer.parseInt(args[3]); 358 | int responseSize = Integer.parseInt(args[4]); 359 | int delayQueryResponse = Integer.parseInt(args[5]); 360 | int minPayloadSize = Integer.parseInt(args[6]); 361 | int maxPayloadSize = Integer.parseInt(args[7]); 362 | int intervalTimeMs = Integer.parseInt(args[8]); 363 | int bufferSize = Integer.parseInt(args[9]); 364 | int burstSize = Integer.parseInt(args[10]); 365 | int intervalBurstTimeMs = Integer.parseInt(args[11]); 366 | siriClient = new SiriClient(serverIp, serverPort, runTime, querySize, responseSize, delayQueryResponse, 367 | minPayloadSize, maxPayloadSize, intervalTimeMs, bufferSize, burstSize, intervalBurstTimeMs); 368 | } else { 369 | siriClient = new SiriClient(serverIp, serverPort, runTime); 370 | } 371 | 372 | String mac = "00:00:00:00:00:00"; 373 | siriClient.run(mac); 374 | System.out.println("missed: " + siriClient.missed); 375 | for (long delay: siriClient.delayTime) { 376 | System.out.println(delay); 377 | } 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qdeconinck/minitopo/fac7b51d4ceb1fb59c409becb2ed520cb11421db/utils/__init__.py -------------------------------------------------------------------------------- /utils/https_server.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From : 3 | http://code.activestate.com/recipes/442473-simple-http-server-supporting-ssl-secure-communica/ 4 | 5 | SimpleSecureHTTPServer.py - simple HTTP server supporting SSL. 6 | 7 | - replace fpem with the location of your .pem server file. 8 | - the default port is 443. 9 | 10 | usage: python SimpleSecureHTTPServer.py 11 | ''' 12 | 13 | import sys 14 | if sys.version_info[0] == 3: 15 | # Python 3 16 | import http.server, ssl 17 | server_address = ('', 443) 18 | httpd = http.server.HTTPServer(server_address, http.server.SimpleHTTPRequestHandler) 19 | httpd.socket = ssl.wrap_socket(httpd.socket, 20 | server_side=True, 21 | certfile=sys.argv[1], 22 | ssl_version=ssl.PROTOCOL_TLS) 23 | print("Serving HTTPS on 0.0.0.0 port 443...") 24 | httpd.serve_forever() 25 | else: 26 | # Python2 27 | import BaseHTTPServer, SimpleHTTPServer 28 | import ssl 29 | import os 30 | 31 | httpd = BaseHTTPServer.HTTPServer(('', 443), SimpleHTTPServer.SimpleHTTPRequestHandler) 32 | httpd.socket = ssl.wrap_socket(httpd.socket, certfile=sys.argv[1], server_side=True) 33 | 34 | if len(sys.argv) > 1: 35 | os.chdir(sys.argv[1]) 36 | 37 | print("Serving HTTPS on 0.0.0.0 port 443...") 38 | httpd.serve_forever() -------------------------------------------------------------------------------- /utils/msg_client.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import datetime 3 | import random 4 | import socket 5 | import string 6 | import time 7 | 8 | BUFFER_SIZE = 2048 9 | ENCODING = 'ascii' 10 | 11 | threads = {} 12 | to_join = [] 13 | 14 | parser = argparse.ArgumentParser(description="Msg client") 15 | parser.add_argument("-s", "--sleep", type=float, help="sleep time between two sendings", default=5.0) 16 | parser.add_argument("-n", "--nb", type=int, help="number of requests done", default=5) 17 | parser.add_argument("-B", "--bulk", help="if set, don't wait for reply to send another packet", action="store_true") 18 | parser.add_argument("-b", "--bytes", type=int, help="number of bytes to send and receive", default=1200) 19 | 20 | args = parser.parse_args() 21 | 22 | 23 | def string_generator(size=6, chars=string.ascii_uppercase + string.digits): 24 | return ''.join(random.choice(chars) for _ in range(size)) 25 | 26 | 27 | # Create a TCP/IP socket 28 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 29 | # Handle reusing the same 5-tuple if the previous one is still in TIME_WAIT 30 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 31 | sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 32 | sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK, 0) 33 | 34 | # Bind the socket to the port 35 | server_address = ('10.1.0.1', 8000) 36 | print("Try to connect to %s port %s" % server_address) 37 | sock.connect(server_address) 38 | 39 | delays = [] 40 | 41 | try: 42 | for i in range(args.nb): 43 | time.sleep(args.sleep) 44 | request = string_generator(size=args.bytes, chars=string.digits) 45 | start_time = datetime.datetime.now() 46 | sock.sendall(request.encode(ENCODING)) 47 | 48 | if args.bulk: 49 | continue 50 | 51 | buffer_data = "" 52 | while len(buffer_data) < args.bytes: 53 | data = sock.recv(BUFFER_SIZE).decode(ENCODING) 54 | 55 | if len(data) == 0: 56 | # Connection closed at remote; close the connection 57 | break 58 | 59 | buffer_data += data 60 | 61 | if len(buffer_data) == args.bytes: 62 | stop_time = datetime.datetime.now() 63 | delays.append(stop_time - start_time) 64 | else: 65 | print("An issue occured...") 66 | break 67 | finally: 68 | # Clean up the connection 69 | print("Closing connection") 70 | sock.close() 71 | for delay in delays: 72 | print(delay.total_seconds()) 73 | -------------------------------------------------------------------------------- /utils/msg_server.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import datetime 3 | import random 4 | import socket 5 | import string 6 | import threading 7 | import time 8 | 9 | BUFFER_SIZE = 2048 10 | ENCODING = 'ascii' 11 | 12 | threads = {} 13 | to_join = [] 14 | 15 | parser = argparse.ArgumentParser(description="Msg server") 16 | parser.add_argument("-s", "--sleep", type=float, help="sleep time between reception and sending", default=5.0) 17 | parser.add_argument("-b", "--bytes", type=int, help="number of bytes to send and receive", default=1200) 18 | 19 | args = parser.parse_args() 20 | 21 | 22 | def string_generator(size=6, chars=string.ascii_uppercase + string.digits): 23 | return ''.join(random.choice(chars) for _ in range(size)) 24 | 25 | 26 | class HandleClientConnectionThread(threading.Thread): 27 | """ Handle requests given by the client """ 28 | 29 | def __init__(self, connection, client_address, id, msg_size): 30 | threading.Thread.__init__(self) 31 | self.connection = connection 32 | self.client_address = client_address 33 | self.id = id 34 | self.msg_size = msg_size 35 | self.delays = [] 36 | 37 | def run(self): 38 | try: 39 | print(self.id, ": Connection from", self.client_address) 40 | start_time = None 41 | buffer_data = "" 42 | 43 | # Receive the data and retransmit it 44 | while True: 45 | data = self.connection.recv(BUFFER_SIZE).decode(ENCODING) 46 | 47 | if len(data) == 0: 48 | # Connection closed at remote; close the connection 49 | break 50 | 51 | buffer_data += data 52 | 53 | if len(buffer_data) >= self.msg_size: 54 | stop_time = datetime.datetime.now() 55 | if start_time: 56 | self.delays.append(stop_time - start_time) 57 | time.sleep(args.sleep) 58 | response = string_generator(size=self.msg_size, chars=string.digits) 59 | start_time = datetime.datetime.now() 60 | self.connection.sendall(response.encode(ENCODING)) 61 | buffer_data = buffer_data[self.msg_size:] 62 | 63 | finally: 64 | # Clean up the connection 65 | print(self.id, ": Closing connection") 66 | self.connection.close() 67 | print(self.delays) 68 | to_join.append(self.id) 69 | 70 | 71 | def join_finished_threads(): 72 | """ Join threads whose connection is closed """ 73 | while len(to_join) > 0: 74 | thread_to_join_id = to_join.pop() 75 | threads[thread_to_join_id].join() 76 | del threads[thread_to_join_id] 77 | 78 | # Create a TCP/IP socket 79 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 80 | # Handle reusing the same 5-tuple if the previous one is still in TIME_WAIT 81 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 82 | sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 83 | sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK, 0) 84 | 85 | # Bind the socket to the port 86 | server_address = ('0.0.0.0', 8000) 87 | print("Stating the server on %s port %s" % server_address) 88 | sock.bind(server_address) 89 | 90 | # Listen for incoming connections 91 | sock.listen(4) 92 | 93 | # Connection identifier 94 | conn_id = 0 95 | 96 | while True: 97 | # Wait for a connection 98 | connection, client_address = sock.accept() 99 | thread = HandleClientConnectionThread(connection, client_address, conn_id, args.bytes) 100 | threads[conn_id] = thread 101 | conn_id += 1 102 | thread.start() 103 | join_finished_threads() 104 | -------------------------------------------------------------------------------- /utils/server.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDJ6Y4ZII7l77iN 3 | HaUbJjYN/Fw5d7LuSXsgaStpSb1Y5T7EP93RdHjQi9LtwBJ4pkRH5KmQsm5IBkLv 4 | gOksbVmss6qXPxuwrMjTa+TZVQZnDKcHqNhEivBCmN13jUGzPfmVPXXtLjPUfP0y 5 | 7xeIF8k53QZJxhmr4hBL3kKSVcG+Gq6229cEqRGQJmyquNNSrDN1diZlXIHqRyg1 6 | 8QBKnOD5ETN1zLW5W2d6mL6M71Vm9z2pziVfihWhmxuH4yO83LIswMq8pLZEraSh 7 | Fu85BoOix9w+TOK1S0M+U24OsRyAmbINIWbxhx11KwLXCTf/LLfEU1nXTdz8HYjC 8 | QzPu9qtJAgMBAAECggEADECxD9NK+KcgcufOoiQieZzL1+zsncs1vpTDPqNr6x4W 9 | PgCGLHS99CHYDfdu54VndVlp9M7vJE3E+BXBkKGeJH3Op1j8DC+gDDxq6clgFxbM 10 | eAmF/jrUM6ZlIiEfUIo9QBI3usnn+UgQcWvS6L5QxsMj44wy+JxCUuhM7+ZmWGvY 11 | ctYkhIoe+5mbAn3onIA/4YzFUi8FNL2UhYiBrnO6l6avdgRS7hSZtsR2FQxQifKH 12 | MEy4LsXnVnH6RRcx86iCXtAXqKaq/7PJj9geCmZ7w6oaa2VWUizUl89Pj2ZQvnLH 13 | Z14TTgYkgTXn7ungAKgDeZVbhR6P06gXoDNbXc1fkQKBgQDx5q+yYedJA4GOIe5Y 14 | XmdEFe/n2YsoIW/tJPBuFJKcB1FGf/dwgHoHbJwbM/WA2sSY7nFlb74Yqwuorcen 15 | 4Jn1BaGexhj9DMJOMRV9xU3ddouWx3ywpWT0R7cKtc+nPSgYH7wlOEJ+sBwLw1El 16 | VJfI4IjJxXi2E+0aSPyPFjjhvQKBgQDVrjYZAVcGK2hB+tws6UVcbdZDw1jjncp3 17 | 9Aiq/wqnFZYDcdLkYiv+US2RrWdqMPslzhmUpfVvdIMsGtVyR1fJO9G0QEr9XNEv 18 | td4s/kbEi8TsePQA6itDh1I1ujdiCDCIWEiRmcMjSP2j7CTMqWwMuBhAOFIZDahm 19 | NytACGRafQKBgFnpG27bEuNBiVrx46w20n0tBjGP2zg8TWTAcRkJToDt+1iP4cGQ 20 | D0tJJDC8PEj7h00seztvsCFtGfVFOkt8oNzAjhT8nncX0fTMK6fGuS1SjYmqdf8W 21 | SpK9QRya/Sa1BX0J9p2C7rw16wa4PyX37j68rjsIydgrSdqWPEFWyielAoGAdWfg 22 | L4MN9sTY5w/X9BFD3BI5nUfzSjVDrv712EaD6uQwZbofvv6132lpGVbmsHEzPUVt 23 | xsAdB91DyXMA/mZ2tInaoiiFS4q5IXbTGXOpHIsTaz7WCr6fgN4UbJLhpUqMqA2h 24 | 6eZLUtLjEjNI0O7yAFcSdA6+BSf89BSx/d/ei90CgYAFkslWND9L6/hW6YnOZwnO 25 | zE155TbA+iN5Sq5FuqfZIRRXU5piN0nLiLfsDmCMitOM2IMDxYKYr6W/E8SEG6Fj 26 | 8WIQ7stv8ivmvvMBn9VhSeFY2/1n1bX7X/XzImtkfzmQmmEsYrCwsLUm5SW4rFLZ 27 | 6FIaEOzK+Anx3WhH2tLGqA== 28 | -----END PRIVATE KEY----- 29 | -----BEGIN CERTIFICATE----- 30 | MIIDTzCCAjegAwIBAgIJAKy2DVlL66fKMA0GCSqGSIb3DQEBCwUAMD4xCzAJBgNV 31 | BAYTAkJFMQwwCgYDVQQIDANMTE4xITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg 32 | UHR5IEx0ZDAeFw0xNTA4MTMxOTM4MDVaFw0xNjA4MTIxOTM4MDVaMD4xCzAJBgNV 33 | BAYTAkJFMQwwCgYDVQQIDANMTE4xITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg 34 | UHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMnpjhkgjuXv 35 | uI0dpRsmNg38XDl3su5JeyBpK2lJvVjlPsQ/3dF0eNCL0u3AEnimREfkqZCybkgG 36 | Qu+A6SxtWayzqpc/G7CsyNNr5NlVBmcMpweo2ESK8EKY3XeNQbM9+ZU9de0uM9R8 37 | /TLvF4gXyTndBknGGaviEEveQpJVwb4arrbb1wSpEZAmbKq401KsM3V2JmVcgepH 38 | KDXxAEqc4PkRM3XMtblbZ3qYvozvVWb3PanOJV+KFaGbG4fjI7zcsizAyryktkSt 39 | pKEW7zkGg6LH3D5M4rVLQz5Tbg6xHICZsg0hZvGHHXUrAtcJN/8st8RTWddN3Pwd 40 | iMJDM+72q0kCAwEAAaNQME4wHQYDVR0OBBYEFJl1YmlCXxlJIur6n4XUx+vJ+LxY 41 | MB8GA1UdIwQYMBaAFJl1YmlCXxlJIur6n4XUx+vJ+LxYMAwGA1UdEwQFMAMBAf8w 42 | DQYJKoZIhvcNAQELBQADggEBAFqNf6XzHI5tEdZPZC9CB+WjxtSroH4z31XOSKQG 43 | aHqZKozx51mD911nqsOD286yCslpoA7ZI9YDC/juanVUDA/vmv8WgpjlLqfD4fK/ 44 | V1lvXLb1x4mhyXXV8++1+EcktgGk4G8Z/5Bs8zR5UF2mIXBK/FwcDJt/esWunEse 45 | 7KrvtHb9ESZ4mEDfQ6bcYdVg/l+3h7B/4kpP+fC/c7LA632/Z6U+/jYHhj2C2Uhv 46 | 6KicgQ6ANB5CkvvJj3wD0eabqlhTL4oXrrpITXHB5MgBMAQWEvh2Dr6FKYe9Te/H 47 | 4183fX7arMCQGmzexoreS9mntq55wLPMHoQzHIxh+A/PXmk= 48 | -----END CERTIFICATE----- 49 | -------------------------------------------------------------------------------- /utils/siriClient.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qdeconinck/minitopo/fac7b51d4ceb1fb59c409becb2ed520cb11421db/utils/siriClient.jar -------------------------------------------------------------------------------- /utils/siri_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import sys 3 | import threading 4 | import time 5 | 6 | BUFFER_SIZE = 512 7 | ENCODING = 'ascii' 8 | 9 | MAX_SECURITY_HEADER_SIZE = 128 10 | MIN_FIELDS_NB = 8 11 | 12 | threads = {} 13 | to_join = [] 14 | delay_results = {} 15 | time_sent = {} 16 | mac = {} 17 | 18 | 19 | class HandleClientConnectionThread(threading.Thread): 20 | """ Handle requests given by the client """ 21 | 22 | def __init__(self, connection, client_address, id): 23 | threading.Thread.__init__(self) 24 | self.connection = connection 25 | self.client_address = client_address 26 | self.id = id 27 | 28 | def run(self): 29 | try: 30 | print(self.id, ": Connection from", self.client_address) 31 | 32 | # Some useful variables 33 | first_data = True 34 | expected_req_size = 0 35 | res_size = 0 36 | waiting_time = 0 37 | expected_delay_results = 0 38 | buffer_data = "" 39 | next_buffer_data = "" 40 | message_sizes = [] 41 | message_id = 0 42 | 43 | # Initialize logging variables 44 | delay_results[self.id] = [] 45 | time_sent[self.id] = [] 46 | 47 | # Receive the data and retransmit it 48 | while True: 49 | data = self.connection.recv(BUFFER_SIZE).decode(ENCODING) 50 | 51 | if len(data) == 0: 52 | # Connection closed at remote; close the connection 53 | break 54 | 55 | # Manage the case where the client is very very quick 56 | if not data.endswith("\n") and "\n" in data: 57 | carriage_index = data.index("\n") 58 | buffer_data += data[:carriage_index + 1] 59 | next_buffer_data += data[carriage_index + 1:] 60 | used_data = data[:carriage_index + 1] 61 | else: 62 | buffer_data += data 63 | used_data = data 64 | 65 | message_sizes.append(len(used_data)) 66 | 67 | if first_data and len(buffer_data) <= MAX_SECURITY_HEADER_SIZE and len(buffer_data.split("&")) < MIN_FIELDS_NB: 68 | continue 69 | 70 | if first_data: 71 | split_data = buffer_data.split("&") 72 | expected_delay_results = int(split_data[4]) 73 | if len(split_data) - MIN_FIELDS_NB == expected_delay_results: 74 | message_id = int(split_data[0]) 75 | expected_req_size = int(split_data[1]) 76 | res_size = int(split_data[2]) 77 | waiting_time = int(split_data[3]) 78 | time_sent[self.id].append(int(split_data[5])) 79 | 80 | # Little check, we never know 81 | if self.id not in mac: 82 | mac[self.id] = split_data[6] 83 | elif not mac[self.id] == split_data[6]: 84 | print(self.id, ": MAC changed from", mac[self.id], "to", split_data[6], ": close the connection", file=sys.stderr) 85 | break 86 | 87 | for i in range(expected_delay_results): 88 | delay_results[self.id].append(int(split_data[7 + i])) 89 | first_data = False 90 | else: 91 | # Avoid further processing, wait for additional packets 92 | continue 93 | 94 | if len(buffer_data) == expected_req_size: 95 | # print(self.id, ": Received request of size", expected_req_size, "; send response of", res_size, "after", waiting_time, "s") 96 | time.sleep(waiting_time) 97 | self.connection.sendall((str(message_id) + "&" + "0" * (res_size - len(str(message_id)) - 2) + "\n").encode(ENCODING)) 98 | first_data = True 99 | buffer_data = "" 100 | message_sizes = [] 101 | if len(next_buffer_data) > 0: 102 | buffer_data = next_buffer_data 103 | next_buffer_data = "" 104 | message_sizes.append(len(buffer_data)) 105 | 106 | elif len(buffer_data) > expected_req_size: 107 | print(len(buffer_data), len(data), len(used_data), file=sys.stderr) 108 | print(self.id, ": Expected request of size", expected_req_size, "but received request of size", len(buffer_data), "; close the connection", file=sys.stderr) 109 | print(buffer_data, file=sys.stderr) 110 | break 111 | 112 | finally: 113 | # Clean up the connection 114 | print(self.id, ": Closing connection") 115 | self.connection.close() 116 | to_join.append(self.id) 117 | 118 | 119 | def join_finished_threads(): 120 | """ Join threads whose connection is closed """ 121 | while len(to_join) > 0: 122 | thread_to_join_id = to_join.pop() 123 | threads[thread_to_join_id].join() 124 | del threads[thread_to_join_id] 125 | 126 | # Create a TCP/IP socket 127 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 128 | # Handle reusing the same 5-tuple if the previous one is still in TIME_WAIT 129 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 130 | sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 131 | 132 | # Bind the socket to the port 133 | server_address = ('0.0.0.0', 8080) 134 | print("Stating the server on %s port %s" % server_address) 135 | sock.bind(server_address) 136 | 137 | # Listen for incoming connections 138 | sock.listen(4) 139 | 140 | # Connection identifier 141 | conn_id = 0 142 | 143 | while True: 144 | # Wait for a connection 145 | connection, client_address = sock.accept() 146 | thread = HandleClientConnectionThread(connection, client_address, conn_id) 147 | threads[conn_id] = thread 148 | conn_id += 1 149 | thread.start() 150 | join_finished_threads() 151 | --------------------------------------------------------------------------------