For this project you are to install a Mininet virtual machine, and complete two of the experiments below. Neither involves "serious" programming, but you will have to use Python as a configuration language, and maybe be able to do some basic shell scripting.
I've placed a zipped 64-bit virtual-machine disk image in OneDrive, at https://loyolauniversitychicago-my.sharepoint.com/:u:/g/personal/pdordal_luc_edu/EelOc-rKkjdFtQRhmqAmsW8BRXyxT_hmsIyIdzMD9DvirQ?e=yleJPq. You will need to be logged in to your Loyola account to access this; there's probably a two-factor-authentication step too. Unzipped, it is 2.9 GB. To make this into a working VM, do the following:
The VirtualBox Guest Additions have been installed.
The login account/password is mininet/mininet. Once you're logged in, use the command startx, which takes ~30 seconds to start because of some issue with the .Xauthority file (I recommend the command rm .Xauthority, as root and in /home/mininet, to fix this). This starts the lxde graphical environment.
When you're done with your session, just click "close" on your virtual machine and you should be asked if you want to save the machine state. If you click "yes", then you can just resume where you left off (with the lxde environment still running).
The directory "loyola" has several python files. Open a terminal window (press cntl-alt-T) to run them. You will need to be root to do this, so use the command sudo bash. I recommend having multiple terminal tabs (use cntl-shift-T to open a new one), with only one of them as root.
Lxde supports two "desktops" out of the box; so if all your windows went away, you probably switched to the other desktop. Use the desktop icons at the lower middle-left, or else cntl-shift-left_arrow/right_arrow.
The editor is leafpad. Start it from the terminal with leafpad &; the ampersand allows you to then continue to use that terminal while leafpad is open.
Once you have the LXDE graphical interface up and running (LXDE is a subset of Ubuntu, btw), you can open new terminal windows with Cntl+Alt+T (on Macs, without an Alt key, use Cntl+Opt+T). You create new terminal tabs with Cntl+Shift+T.
You can change the terminals to a white background and dark text by using Edit => Preferences. Black is, in RGB terms, (0,0,0) while white is (255,255,255).
When starting leafpad, if you forget the '&', just leave that terminal alone, and create a new terminal tab. You can also specify a filename to open: leafpad demo.py &, but selecting the file to open with the File menu is just fine. Leafpad does not support multiple edit tabs, but you can have multiple leafpad windows open.
For this demo, I will start Mininet with the topology below, and watch a TCP connection using WireShark.
h1 ---- r1 ---- r2 ---- h2
We get this topology by running (as root) the command
python routerline -N 2
in the loyola subdirectory.
At the mininet> prompt, we can run simple commands, like links and nodes. We can also run h1 ping h2, which runs the command "ping h2" on h1. Finally, if we run h1 ifconfig or h2 ifconfig, we can find the IPv4 addresses of h1 and h2. We will need these below, because the names h1 and h2 do not work in general.
For this demo, we can open an xterm terminal window on each of h1, r1 and h2 with xterm h1 r1 h2 at the mininet> prompt. Three black terminal windows should open up.
In the h2 window, we will run netcat -l 5432, which sets up netcat as a "listener" on port 5432.
On r1 we will start Wireshark, using the command wireshark. Have it listen on interface r1-eth0.
Finally, on h1, run netcat 10.0.2.10 5432 (the 10.0.2.10 should be the IPv4 address of h2). You should see the three-way handshake. Type a couple data lines, such as "hello" or "floccinaucinihilipilification". Then press cntl-D to exit. You should see the strings printed on h2; you should also be able to see the data in the tcpdump output. Finally, you should see the closing handshake.
You should expect to complete two of these.
For this project, you are to arrange so that you can view and record TCP simultaneous open. Here is a suitable network configuration:
h1 ---- R ---- h2
The main purpose of the router R is to have a place where you can monitor the traffic, using tcpdump or wireshark.
Start xterms (x-terminals) on each of h1, h2, and R. I will assume that h1's IPv4 address is 10.0.0.10 and h2's is 10.0.1.10; these are the assignments if you run python routerline.py -N 1. Then run the following:
But this doesn't get you a simultaneous open. To do that, you'll have to run netcat -p 5432 ip-address 5432 on both h1 and h2 simultaneously. The -p 5432 specifies the source port; you can also do netcat -p 2000 h2 3000 on h1, and netcat -p 3000 h1 2000 on h2, for example.
To run the two commands simultaneously is impossible, of course. However, you can add some delay to the h1--R and R--h2 links, at least 1000 ms worth. You will probably also want to use ssh from, say, R to h1 and h2, to run the commands in quick succession.
To set the delay, see the file competition2.py, which unfortunately I did not get pre-installed in the virtual machine (use ssh or scp to transfer, or copy/paste if you are able to get that working, or just open a browser within the virtual machine and download it from this page). I've set DELAY='250ms' in the file, and, if mininet is running and I type h1 ping h3, I get
10.0.3.10 (10.0.3.10) 56(84) bytes of data.
64 bytes from 10.0.3.10: icmp_seq=1 ttl=63 time=250 ms
64 bytes from 10.0.3.10: icmp_seq=2 ttl=63 time=250 ms
64 bytes from 10.0.3.10: icmp_seq=3 ttl=63 time=250 ms
which is exactly what it is supposed to be.
I did this experiment using a modified version of competition.py, which I named delayline.py. (Specifically, I increased the DELAY variable and made sure r's interfaces were being assigned IP addresses (in my version I also eliminated h2, but that was unnecessary).) The file creates a topology h1 ---- r ---- h3, with a one-way delay on the r---h3 link. The delay applies only in the h3-to-r direction, though, so to arrange a packet crossing you will have to take that into account. But this is good enough, provided you start at h3. Here's the basic idea:
One way to do this quickly is to type both commands, but don't press Enter. Then give the h3 window the focus (that is, click on it), and move the mouse over the h1 window, but don't click yet. Press ENTER, and the h3 command will start, because that window still has the focus. Now click -- on the h1 window -- and press ENTER again to start the previously typed command on h1. In other words, the sequence is ENTER-CLICK-ENTER. That's easy to do quickly; there's no mouse motion in that sequence.
It took me a fair number of tries, but I finally got it to work (almost all my failed attempts were because I typed a port number or IP address wrong). One bright spot: if you get the simultaneous open to work, then typing a string on either side will cause it to appear on the other side. I had thought that netcat would simply create the connection (visible in tcpdump) and then lock up.
How much delay do you need? 3000 ms is easy; can you do it in 1000?
Bidirectional delay is straightforward to set up, but we don't need it here. All we need is for packets to cross in the wire. So start on h3, and while that SYN packet is making its way to h1, start on h1 before it gets there. Note that if you start on h1, the SYN packet arrives at h3 essentially immediately, and there is no hope of arranging a simultaneous open.
There's a problem with competition.py: the commands to set up node r are commented out, and they use the wrong interfaces. Here are the commented-out lines:
# r.cmd('ifconfig r-eth0 10.0.1.1/24')
# r.cmd('ifconfig r-eth1 10.0.2.1/24')
# r.cmd('ifconfig r-eth2 10.0.3.1/24')
But at some point an update to Mininet changed the interface numbering,
so instead of eth0,eth1,eth2 it's now eth1,eth2,eth3. Change the
interface names as follows:
r.cmd('ifconfig r-eth1 10.0.1.1/24')
r.cmd('ifconfig r-eth2 10.0.2.1/24')
r.cmd('ifconfig r-eth3 10.0.3.1/24')
Bonus points: close the connection on h3 first and then as soon as possible on h1 in order to generate a simultaneous close.
Alternative method: set up ssh so, from r, you can put ssh commands in a shell script to start netcat on h1 and then netcat on h3. But I forgot to set up ssh before giving you the virtual machine, and you also have to do more work with this approach to handle the delay, and I think the Enter-Click-Enter way is easier.
To submit, save the .pcap file from tcpdump (it may have a .pcapng extension; ng for "next generation")
The goal is to compare the total throughput for the two TCP congestion mechanisms. For this example, the relevant Python file is competition.py. TCP Reno and TCP Cubic are both available.
Choose a time interval, eg 10 seconds (maybe less for trial runs), and compare the throughput of two connections sharing a link. Do a few runs to get an idea of how consistent your results are.
I recommend comparing Reno to Cubic and also, as a control, Reno to Reno.
It is important to try different RTTs, eg 50 ms, 100 ms and 200 ms (for the basic tests, you can implement delay on the common link; be careful whether it is bidirectional or not).
You might also try different delays on the two paths. TCP Cubic is supposed to handle this reasonably well for cubic-to-cubic competition.But this is optional.
This one is probably the most straightforward: use competition.py, with dualreceive.py (a python3 program!) running on h3 and two instances of sender.py on each of h1 and h2. Note that sender.py takes command-line arguments to control its behavior: the following
python3 sender.py 10000 10.0.3.10 5430 reno
sends 10000 blocks to h3 at port 5430 (one of the two used by default in dualreceive.py) using TCP Reno. You can replace 'reno' by 'cubic' to get TCP Cubic.
In the original handout (in the first experiment) I described a file competition2.py, but the differences between it and competition.py are pretty minimal (except that the variable DELAY is set to a different value). It turns out that competition2.py was a file I created after distributing the virtual hard disk, so it won't be on your system. Again, all I did was to change DELAY to a new value.
To submit, send me (a) a description of the setup you actually used, (b) some of your output, and (c) a brief discussion of how you determined the relative throughputs of reno and cubic from that output. Note that you'll need to do this for each RTT you used.
Set up a topology like this, based on the competition.py file:
Run htb on R, and set separate classes for traffic from h1/h2/h3 to h4. Run sender.py on each of h1/h2/h3, and a receiver on h4 (I'm still working on that; dualreceive.py only receives from two and that should be good enough to get started while I work out a more general solution).
Set appropriate bandwidth limits for each of h1/h2/h3, and show that they are obeyed.
Experiment with a large burst for h1, but not h2/h3. What happens?
Can you change the bandwidth while traffic is being sent, and see a change in the received bandwidth?
To do this you must set up the Linux bandwidth-throttling system, known as htb for hierarchical token bucket. The command you use is tc, for traffic control.
I will for the time being stick with the competition.py configuration with one less host:
r ---- h3
You run htb on r. If we're trying to control traffic through r, we must apply htb to the downstream interface, which for h1->h3 and h2->h3 traffic is named r-eth3. Here are the commands necessary to limit h1->h3 traffic to 1 mbit/sec, and h2->h3 traffic to 10 mbit/sec. When specifying rates, mbit is megabit/sec and mbps is megabyte/sec. Also, these units are written adjacent to the numeric value, with no intervening space!
First, set up the htb "root", and a default class (which we eventually do not use, but the point is that traffic goes through the default class until everything else is set up, so traffic is never blocked).
tc qdisc del dev r-eth3 root # delete any previous htb stuff on r-eth3
tc qdisc add dev r-eth3 root handle 1: htb default 10 # class 10 is the default class
Now we add two classes, one for h1 (class 1) and one for h2 (class 2). At this point you can't tell yet that class 1 is for h1; that's in the following paragraph.
tc class add dev r-eth3 parent 1: classid 1:1 htb rate 1mbit # '1mbit' has no space! The classes are 1:1 and (next line) 1:2
tc class add dev r-eth3 parent 1: classid 1:2 htb rate 10mbit
Now we have to create filters that assign traffic to one class or another. The flowid of the filter matches the classid above. We'll assign traffic from h1 (10.0.1.10) to classid 1:1, and traffic from h2 to classid 1:2. The "parent 1:" identifies the root above with handle 1:. The "u32" refers to the so-called u32 classifier; the name comes from unsigned 32-bit.
tc filter add dev r-eth3 protocol ip parent 1: u32 match ip src 10.0.1.10 flowid 1:1
tc filter add dev r-eth3 protocol ip parent 1: u32 match ip src 10.0.2.10 flowid 1:2
Now if we start the senders, we should see h1 getting 1/10 the bandwidth! Also, each sender should get 1mbit and 10mbit respectively. We can also just drop the 1:2 class (and its filter), and have that traffic go to the default. In this case, h1 is limited to 1mbit and h2 is not limited at all.
We can also apply filters to traffic from selected ports, so different traffic from the same host is treated differently.
We can also replace "add" by "change" to update the rules. This makes the most sense for the "tc class" statements that assign rates. (We can also use "del" to delete classes.)
To submit, send me your own list of tc/htb commands,
some output (from, eg, dualreceive.py) showing the bandwidth
throttling, and a brief discussion.
This is not quite straightforward! Here are two well-known solutions from makeuseof.com/tag/transfer-files-virtual-machine-guest-host-pc:
But here are three other ways that are easier: