# Lab Exercises: Mininet & Data Center Networks These exercises will make you feel comfortable with Mininet. The part 3 of these exercises were extracted from https://hackmd.io/@pmanzoni/SyWm3n0HH?type=view. This exercise is divided in three parts: 1. Understanding Mininet Concepts 2. Running Mininet Topologies 3. Playing with Mininet shell ## Set up the environment If you haven't already downloaded the virtual machine (VM) (**mn-wifi.ova**): - https://github.com/intrig-unicamp/mininet-wifi/blob/master/README.md (user: **wifi**, psw: **wifi**) Once the Virtual Machine is booted up, open up the terminal by pressing ``Ctr + Alt + t ``. It is possible to modify the keyboard layout, if you wish: ``` $ setxkbmap br ``` <!-- Import and Start the VM, then install Wireshark: ```bash $ sudo apt update $ sudo apt install wireshark ``` --> ## Part 1: Understanding Mininet Concepts In a terminal of the virtual machine, type: ```bash $ sudo mn --mac ``` This command will start Mininet and will configure a small network with two hosts and a switch. Also use the option `--h` or `--help` in the mn command to discover more topologies available with `--topo` options. Sometimes after exiting the mininet cli, the terminal can go rogue and mix some inputs. For example, pressing the backspace key might produce the ``^H`` character. You can resolve this issue by entering the following command: ```bash $ stty erase "^H" ``` At the Mininet terminal (`mininet>`), execute the `net` command to observe the network created. Then run `intfs` to check all network interfaces, and check for connectivity between hosts. Type `xterm h1` or `xterm h2` into the Mininet CLI to open the terminal (console) of both hosts h1 and h2. For example: ```bash mininet> xterm h1 ``` You can now execute commands as if you are inside the Linux machine of these hosts. Follow the steps below: 1) Access Wireshark typing wireshark in h2 xterminal. Use it to monitor the host interface. 2) Try to ping between h1 e h2, using h1 xterminal: `ping -c1 10.0.0.2` 3) For each host (h1 e h2) and switch (s1): 3.1) Check the interfaces IP address. 3.2) Check the interfaces MAC address. 3.3) Check the routing table. 3.4) Check the ARP table. Tip: try to use the Linux toolset: ifconfig, route, arp, netstat, ip route, etc. At any time you can finish the mininet session terminal by typing `quit`, `exit` or `Ctrl+D`. :::warning Keep in mind: usually the state of switches keep buffered by mininet, so it is recommended to clear them after finishing a topology using: `$ sudo mn -c` ::: ------ ## Part 2: Running Mininet Topologies First let's enter the folder with the topology scripts: <!-- ```bash $ cd cpqd-tutorials/01-mininet/ea080/lab1 ``` --> ```bash $ cd ea080/lab1 ``` :::info If you don't have the exercises folder in the VM, you can get it with the command: ```bash git clone https://github.com/intrig-unicamp/ea080.git cd ea080/lab1 ``` ::: Run the python script, typing: ```bash $ sudo python pratica-1-II.py ``` The script source code is shown below. ```python import atexit from mininet.net import Mininet from mininet.topo import Topo from mininet.cli import CLI from mininet.log import info,setLogLevel net = None def createTopo(): topo=Topo() #Create Nodes topo.addHost("h1") topo.addHost("h2") topo.addHost("h3") topo.addHost("h4") topo.addSwitch('s1') topo.addSwitch('s2') topo.addSwitch('s3') #Create links topo.addLink('s1','s2') topo.addLink('s1','s3') topo.addLink('h1','s2') topo.addLink('h2','s2') topo.addLink('h3','s3') topo.addLink('h4','h3') return topo def startNetwork(): topo = createTopo() global net net = Mininet(topo=topo, autoSetMacs=True) net.start() CLI(net) def stopNetwork(): if net is not None: net.stop() if __name__ == '__main__': atexit.register(stopNetwork) setLogLevel('info') startNetwork() ``` Execute the command ``pingall`` in the mininet console, and check if pings fail (i.e., hosts not replying are represented by an X mark): ```bash mininet> pingall ``` :::danger Modify the script so you can run it again and all hosts will be connected by the pingall command. ::: Now, run the script python ``pratica-1-III.py`` (also located inside lab1 folder). The script source code is shown below. Look how the link parameters were specified (bandwidth Mbps, latency ms, and frame loss ratio %). ```python import atexit from mininet.net import Mininet from mininet.topo import Topo from mininet.cli import CLI from mininet.log import info,setLogLevel from mininet.link import TCLink net = None def createTopo(): topo=Topo() #Create Nodes topo.addHost("h1") topo.addHost("h2") topo.addHost("h3") topo.addHost("h4") topo.addSwitch('s1') topo.addSwitch('s2') topo.addSwitch('s3') #Create links topo.addLink('s1','s2',bw=100,delay='100ms',loss=10) topo.addLink('s1','s3') topo.addLink('h1','s2') topo.addLink('h2','s2') topo.addLink('h3','s3') topo.addLink('h4','s3') return topo def startNetwork(): topo = createTopo() global net net = Mininet(topo=topo, autoSetMacs=True, link=TCLink) net.start() CLI(net) def stopNetwork(): if net is not None: net.stop() if __name__ == '__main__': atexit.register(stopNetwork) setLogLevel('info') startNetwork() ``` :::danger Using the ping command, check the rtt and loss between h1 and h2 and h1 and h4. To check the network throughput use the command iperf, e.g., `iperf h1 h2` or `iperf h1 h4` ::: Editing the previous topology script, try to create a (data center like) network topology as in the image below, using the following parameters: ``` +----+ +------||c1||----+ | +----+ | | | +----+ +----+ +-||d1||-+ +-||d2||-+ | +----+ | | +----+ | | | | | | | | | +----+ +----+ +----+ +----+ ||a1|| ||a2|| ||a3|| ||a4|| +----+ +----+ +----+ +----+ | | | | | | | | h1 h2 h3 h4 h5 h6 h7 h8 ``` * Switches Core (c1) to distribution switches (d1 and d2): 10 Gbps, 1 ms. * Distribution switches (d1 and d2) to access switches (a1, a2, a3, a4): 1 Gbps, 3 ms. * Access switches to hosts (h1-8): 100 Mbps, 5 ms. :::danger Modify the topology file to insert 15% loss in the link between host h8 switch a4. How to measure the packet loss to h8 using ping? What's the average loss you see in practice? Using `iperf`, measure the throughput between: h1 and h2, h1 and h3, and h1 and h5. ::: Change the link parameters as following: * Switches Core (c1) to distribution switches (d1 and d2): 1 Gbps, 2 ms. * Distribution switches (d1 and d2) to access switches (a1, a2, a3, a4): 100 Mbps, 2 ms. * Access switches to hosts (h1-8): 10 Mbps, 2 ms. :::danger Using `iperf`, measure the throughput between: h1 and h2, h1 and h3, and h1 and h5. Explain the differences from the previous exercise. ::: ___ ## Part 3: Playing with Mininet shell Create the following network: $ sudo mn --topo=single,3 --controller=none --mac * `--controller=none` means that commands will be provided manually * `--mac` means that MACs will be simplified Now execute the `dump` and the `net` commands just to check the topology. The command to see how switch `s1` ports map to OpenFlow Port (OFP) numbers is: mininet> sh ovs-ofctl show s1 :::warning **`sh`** allows to execute shell commands inside mininet. You could execute them from a separate xterm using sudo, i.e., `$ sudo ovs-ofctl show s1` ::: Let's create the first flow entry: mininet> sh ovs-ofctl add-flow s1 action=normal **`normal`** means traditional switch behaviour, that is the classical switch forwarding operations. Let's test with `pingall`. :::danger What's the result? Everything was connected? ::: Now execute: mininet> sh ovs-ofctl dump-flows s1 :::danger What means the info output from the command above? ::: Delete the entry (the flow) using: mininet> sh ovs-ofctl del-flows s1 this command deletes all flows in s1. Now execute once again: mininet> sh ovs-ofctl dump-flows s1 You'll see that there are no moree flows defined. Now execute once again: mininet> pingall :::danger What happens? Why? ::: ### Using layer 1 data In this part you will work at the physical ports level. We want to programme the switch so that everything that comes at the switch s1 from OpenFlow Port 1 is sent out to OpenFlow Port 2, and vice-versa. The required commands are: mininet> sh ovs-ofctl add-flow s1 priority=500,in_port=1,actions=output:2 mininet> sh ovs-ofctl add-flow s1 priority=500,in_port=2,actions=output:1 The two instructions basically indicate the switch that what enters from port 1 has to be forwarded to port2... and vice-versa. :::danger Now run, `mininet> h1 ping -c2 h2 ` and, `mininet> h3 ping -c2 h2` What happens? Why? ::: Now execute once again: mininet> sh ovs-ofctl dump-flows s1 Now you can see the two newly created flows and the infos about them. The priority value is important. If a packet enters a switch and there are various rules, only the one with higher value is executed. Let's add another flow: mininet> sh ovs-ofctl add-flow s1 priority=32768,action=drop This flow has an higher priority (32768 which corresponds to the defaults value; Priorities range between 0 and 65535.) :::danger What's the effect of adding this flow? Try it with ping! ::: Let's now eliminate such the command below (it deletes the flow with all the default parameters): mininet> sh ovs-ofctl del-flows s1 --strict Try again ping. ==Now delete all the flows in the switch== and exit the Mininet interface. mininet-wifi> exit ### Using layer 2 data In this section you will repeat the same operations but instead of using the port numbers, using the MAC addresses of the hosts. Since we create mininet topology again with the **`--mac option`**, the hosts will have simplified MAC addresses: $ sudo mn --topo=single,3 --controller=none --mac Execute the command below: mininet> sh ovs-ofctl add-flow s1 dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02,actions=output:2 mininet> sh ovs-ofctl add-flow s1 dl_src=00:00:00:00:00:02,dl_dst=00:00:00:00:00:01,actions=output:1 Try `pingall` to see if it works. As you will see it doesn't work since ping works at IP level and the first thing it does is to use ARP to find out the MAC addresses of the different hosts. Our switch, with the rules it currently has, it's filtering out ARP data traffic. ARP is a broadcast protocol so we need to add another rule: mininet> sh ovs-ofctl add-flow s1 dl_type=0x806,nw_proto=1,action=flood This rule adds a flow that "floods" all Ethernet frames of type 0x806 (ARP) to all the ports of the switch. `nw_proto=1` indicate an "ARP request". Since the replies in ARP are unicast, we already have the right flows set. we now obtain: mininet-wifi> pingall *** Ping: testing ping reachability h1 -> h2 X h2 -> h1 X h3 -> X X *** Results: 66% dropped (2/6 received) which basically means that now h1 and h2 are in contact but h3 still is disconnected. ==Now delete all the flows in the switch== ### Using layer 3 data We'll now use layer 3 (IP) infos for the creation of flows. All hosts will talk one to another, and also we will give priority to data coming from h3 using DSCP, that is using DiffServ. OpenFlow includes a variety of rules for modifying packet contents; here is a basic example. :::warning Differentiated services or DiffServ is a computer networking architecture that specifies a simple and scalable mechanism for classifying and managing network traffic and providing quality of service (QoS) on modern IP networks. DiffServ can, for example, be used to provide low-latency to critical network traffic such as voice or streaming media while providing simple best-effort service to non-critical services such as web traffic or file transfers. ::: So we first start by excuting: mininet> sh ovs-ofctl add-flow s1 priority=500,dl_type=0x800,nw_src=10.0.0.0/24,nw_dst=10.0.0.0/24,actions=normal mininet> sh ovs-ofctl add-flow s1 priority=800,dl_type=0x800,nw_src=10.0.0.3,nw_dst=10.0.0.0/24,actions=mod_nw_tos:184,normal :::danger Describe what the config lines above mean! ::: :::warning We use the 184 since, to specify the 46 DSCP value in the IP TOS field, we have to shift it 2 bits on the left according to the meaning of the bits of TOS field. ::: Now we have to again enable ARP. We'll do it in a slightly different way: mininet> sh ovs-ofctl add-flow s1 arp,nw_dst=10.0.0.1,actions=output:1 mininet> sh ovs-ofctl add-flow s1 arp,nw_dst=10.0.0.2,actions=output:2 mininet> sh ovs-ofctl add-flow s1 arp,nw_dst=10.0.0.3,actions=output:3 In this case we are not using flooding, which can be a useful behaviour (imagine a 24 ports switch) :::danger Try if it works with `pingall`. Check with wireshark if packets have DSCP modified from and to h3. ::: ==Now delete all the flows in the switch== ### Using layer 4 data To conclude we'll see how the same can be done at layer 4, the application layer. In this example a simple python web server (the one you used in the previous session) will be executed in host 3, and host 1 and host 2 will connect to that server that runs at port 80. So let's start the web server on h3: mininet> h3 python -m SimpleHTTPServer 80 & Let's enable ARP (in a simpler way): mininet> sh ovs-ofctl add-flow s1 arp,actions=normal Let's introduce a rule that forwards all TCP traffic (`nw_proto=6`) with destination port 80, to the switch port 3: mininet> sh ovs-ofctl add-flow s1 priority=500,dl_type=0x800,nw_proto=6,tp_dst=80,actions=output:3 :::warning This rule could be used to redirect all the data traffic to a firewall that is connected to a specific port. ::: And, finally, we have to add this rule: mininet> sh ovs-ofctl add-flow s1 priority=800,ip,nw_src=10.0.0.3,actions=normal :::danger What does this last rule mean? ::: To check whether eveything works, try: mininet> h1 curl h3 ___ ## Congratulations! :::info Now you know a little about Mininet! :::