├── .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 |
--------------------------------------------------------------------------------