Lab 3: Introduction to Network Emulation in Mininet

ENSE 472 - Digital Networks - Laboratory

University of Regina - Engineering and Applied Science - Software Systems Engineering

Lab Instructor: Adam Tilson


The purpose of this lab is to:

  • Investigate Mininet, a network emulator
  • Understanding Software-Defined Networking (SDN) and the OpenFlow Protocol
  • Investigate common network topologies
  • Understand the difference between switches and hubs
  • Run your code on the emulated network and investigate security and performance concerns

A computer running Windows, MacOS or Linux, with an Intel or AMD-based processor (x86 or x86-64) with administrator privileges

And

A VirtualBox Image of ZorinOS Lite prepared by Adam (Updated for Lab 3)

  • You can find a link to this on URCourses
  • Open VirtualBox, go to Tools -> Import -> Select the virtualbox image, start it up
  • On boot, username is zorin, and password is zorin
  • This is Zorin OS Lite, the lite version of my daily driver, responsive and great ui
  • I updated this in lab 3 to install mininet
  • Please backup your files from Lab 2 before migrating over!
  • Recall the username and password are zorin and zorin.

Mininet is a network emulator that can be used to construct a virtual network which runs real kernel, switch and application code, on a single Linux machine. Once these components are initialized, they can be interacted with through a command line interface or scripts. Furthermore, topologies may be created in Python Scripts. The routing in mininet is configured using OpenFlow, an important protocol in Software-Defined Networking (SDN). As with networks in general, there is quite a bit to learn, it’s best to start simple and work up from there!

To start using mininet, launch the updated VirtualBox images provided, open a terminal and run the following:

sudo mn

Recall, the password for our virtual machine is zorin.

$ sudo mn
[sudo] password for zorin:
*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2 
*** Adding switches:
s1 
*** Adding links:
(h1, s1) (h2, s1) 
*** Configuring hosts
h1 h2 
*** Starting controller
c0 
*** Starting 1 switches
s1 ...
*** Starting CLI:
mininet> 

The summary provides an overview of what mininet has constructed - we have built a virtual network architecture consisting of two hosts, h1 and h2, a controller, c0, and a switch, s1. Further, the hosts h1 and h2 are each connected to the switch.

A host represents an end user device, such as a PC. In mininet, these run standard linux network code, and will be able to make use of some networking utilities you have used in other classes. It will also let you run applications you have developed on the virtual network, as well as give access to the VirtualBox’s filesystem.

A switch represents a traffic routing device. The routing method will be configured from the controller using OpenFlow, which we will discuss in the next section. At its simplest, you may think of a switch as a device with many connections to other devices which relies on a routing table to determine where to send packets.

The following shows the topology: (Credit: David Mahler)

Finally, a command line interface was started. This uses mininet commands to allow us to issue commands to our network components, for example, to test connectivity and debug operations.

For now, let us attempt to run some commands on one of the hosts.

To see list of commands available, type:

mininet> help

Note that, going forward, whenever you see mininet> at the beginning of a prompt, this is to be run on the mininet command line.

This will list some common operations you may wish to perform, such as:

Documented commands (type help <topic>):
========================================
EOF    gterm  iperfudp  nodes        pingpair      py      switch  xterm
dpctl  help   link      noecho       pingpairfull  quit    time  
dump   intfs  links     pingall      ports         sh      wait  
exit   iperf  net       pingallfull  px            source  x     

You may also send a command to a node using:
  <node> command {args}
For example:
  mininet> h1 ifconfig

The interpreter automatically substitutes IP addresses
for node names when a node is the first arg, so commands
like
  mininet> h2 ping h3
should work.

Some character-oriented interactive commands require
noecho:
  mininet> noecho h2 vi foo.py
However, starting up an xterm/gterm is generally better:
  mininet> xterm h2

You can also get help about a specific question:

mininet> help nodes
mininet> help nodes
List all nodes.

Let’s check to see which nodes are available:

mininet> nodes
mininet> nodes
available nodes are: 
c0 h1 h2 s1

Next, let’s investigate which links are connected:

mininet> net
h1 h1-eth0:s1-eth1
h2 h2-eth0:s1-eth2
s1 lo:  s1-eth1:h1-eth0 s1-eth2:h2-eth0
c0

So we can see that h1-eth0 port is connected to s1-eth1 port. We can envision this as a physical connection, with an ethernet cable connected to a physical switch device. However, at this point we don’t know if this is acting as a hub or a smart switch.

We can also collect some additional information about our network with the following command:

mininet> dump
<Host h1: h1-eth0:10.0.0.1 pid=1812> 
<Host h2: h2-eth0:10.0.0.2 pid=1814> 
<OVSSwitch s1: lo:127.0.0.1,s1-eth1:None,s1-eth2:None pid=1819> 
<Controller c0: 127.0.0.1:6653 pid=1805> 

We can see the mininet classes being used (Host, OVSSwitch, Controller), IP-addresses assigned to our devices (10.0.0.1, 10.0.0.2), the port that our controller is running its router configuration service on (6653), and process id’s for this appliance in our virtual machine.

We will investigate the role of the controller in the next section, however it is important to note that it is a service which the OVSSwitch’s may talk to to learn how properly to route their packets.

We wish to run a command on a node. Let’s start by investigating the network interface of one of our hosts:

mininet> h1 ifconfig
h1-eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.0.1  netmask 255.0.0.0  broadcast 10.255.255.255
        inet6 fe80::8c74:3cff:feeb:7159  prefixlen 64  scopeid 0x20<link>
        ether 8e:74:3c:eb:71:59  txqueuelen 1000  (Ethernet)
        RX packets 43  bytes 4488 (4.4 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 13  bytes 1006 (1.0 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

This confirms what we saw earlier, that we have an emulated ethernet device which is available on the network. It has the same ipv4 address we saw earlier (10.0.0.1).

Let’s have this host ping the other host:

mininet> h1 ping h2

This will start the ping operation, which will continue indefinitely. You may press ctrl+c to terminate this process.

PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=12.7 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.637 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.149 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.153 ms
^C
--- 10.0.0.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3051ms
rtt min/avg/max/mdev = 0.149/3.405/12.681/5.359 ms

The ping succeeded - the network is successfully connected. You will notice the first ping required additional time. This is because the switch did not know where to send the packet, and needed to communicate with the controller server over the openflow protocol to learn where to send the packet. Once the switch knows, it will cache this address in it’s local routing table, so subsequent sends are quicker. After a set amount of time, a timeout will occur, and the cached routing table is erased, and new rules are determined. Why might this be important?

Solution Because physical devices may change over time, and thus the most efficient path to the destination may change too. Imagine if your switch had 10 ports, and you unplugged your PC from port 4 and plugged it into port 5. The router would need to learn the new location of this device to successfully deliver its packets!

Sometimes, it may useful to spawn an extra terminal window which is running directly on each host. You may do this with the following command:

mininet> xterm h1

This environment is inside the host, thus some shortcuts available for us in the mininet command line will not be present. For example, in order to perform a ping, we will require the full IP address of the second host:

ping 10.0.0.2

In previous labs we observing traffic coming to a device using WireShark. We will return to this in a later section. For now, a simpler way to see when packets arrive at a host is using a utility available on the device via the xterm window

tcpdump

Head back to the mininet window, and create some traffic to the PC:

mininet> h2 ping h1

You can halt the TCPDump with ctrl+c

When you wish to close the xterm terminal, simply type exit.

Another way to test connectivity in mininet is to use the pingall command:

mininet> pingall
mininet> pingall
*** Ping: testing ping reachability
h1 -> h2 
h2 -> h1 
*** Results: 0% dropped (2/2 received)

This quickly shows us that all devices are successfully connected.

Running a simple HTTP server in python

One of the best things about mininet is that we can run real application code on our hosts. Did you know python can host a simple webserver right from the commandline? Let’s try it out, in our Zorin VirtualBox environment (not mininet) to start:

sudo python -m http.server 80 &

And then, using Firefox, we can access this

This runs it in the background. We may bring it back to the foreground with fg and close it with ctrl+c

Of course, this directory listeing is pretty boring. It would be better if served an actual webpage. Let’s create one in our home directory:

cd ~
codium .

Create a new file called index.html, and use Emmet to get some boilerplate (!), and then fill in content!

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World</title>
</head>
<body>
    My first webpage!
</body>
</html>

Let’s start our server again, and see if we can see our new page:

So that’s neat. Let’s try this on our host in mininet:

mininet> h1 python -m http.server 80 &

However, this is not a graphic environment. On the second host, let’s use a built in linux utility to grab the page, wget, dumping the output (-O) to the console (- means stdout)

h2 wget -O - h1
mininet> h2 wget -O - h1
--2023-10-17 19:18:45--  http://10.0.0.1/
Connecting to 10.0.0.1:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 225 [text/html]
Saving to: ‘STDOUT’

-                     0%[                    ]       0  --.-KB/s               <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World</title>
</head>
<body>
    My first webpage!
</body>
-                   100%[===================>]     225  --.-KB/s    in 0s      

2023-10-17 19:18:45 (1.15 MB/s) - written to stdout [225/225]

The reason your mininet host had access to the file you created on you Zorin VirtualBox is that each mininet host uses the filesystem of the directory you started mininet in. If you don’t see this file here, you likley started mininet in a different directory!

When we are finished, let’s exit our server:

h1 kill %python

When you are finished with mininet, you can close it with:

mininet> exit

However, occasionally mininet will not close gracefully and may leave zombie processes running. The ensure these resources and ports are free for your next emulation, run the cleanup step:

sudo mn -c

So far we have been looking at mininet only as a virtual representation of real devices, but it actually serves as an introduction to Software Defined Networks. In other words, the concept of emulating a network has real world applications which are employed in real systems today.

Why might one want to do this? As the internet becomes increasingly virtualized, i.e. appliances and applications existing on the cloud, decoupled from hardware, it becomes necessary to also have the networking solution decoupled from the real hardware. This provides significant performance, flexibility and security gains over physical hardware. For example, if a application running on a virtual machine is migrated to a different physical server, it is simply a matter of reconfiguring the Software Defined Network to redirect to it. Other desired features like load balancing and distrubted offerings are simplified.

Typically SDN architecture is comprised of:

Applications - Run application code, e.g. our hosts

Networking Devices - Recieve packets from applications and forward it to other applications, e.g. our switches

Controllers - Instruct the networking devices how to route our data

Note that the Network Devices are decoupled from the Controllers. Recall back to ENSE 374 where we discussed the benefits of separating responsibility for different components. One advantage of this is that mutiple networking devices can be configured from a single controller. The analogs in real hardware must necessarily either run directly on the device, or be a connected external device, e.g. a configuration PC which sense configuration packets to the switch, either directly or over a network.

How are controllers implemented?

There are several approaches to Software Defined Networking. We are utilizing OpenSDN, in which our controllers and network devices employ a protocol, in our case, OpenFlow, to decide where to route traffic. The OpenFlow protocol can be used on real world and virtual devices.

It is important to note that a software defined network is not necessarily isolated, like a sandbox, but may communicate with the wider web as well.

If you wish to learn more about software defined networks, you may check out this textbook (free with your O’Reilly account through U of R!) SDN: Software Defined Networks. https://www.oreilly.com/library/view/sdn-software-defined/9781449342425/

To better understand how mininet is working, see the following figure from SDN: Software Defined Networks (credit O’Reilly Media, Inc.)

We can see that, virtually speaking, the switch is connected to the controller over the loopback address. The controller is running a service, and acting as a server, on port 6633. (By convention, these services either run on port 6633 or 6653.) This service is accessed by the routers to determine where to send the packets. Meanwhile, other connections are represented with virtual devices, such as ethernet. We’ll be able to see the packets going through these in WireShark.

Let’s look at how specificially OpenFlow is used to tell the packets where to go. Consider our simple network, as shown in the previous figure.

If we were to type h2 ping h4, we know that h2 would need to send that traffic out its only port, h2-eth0 / s1-eth0.

  • This packet would arrive at the OpenFlow switch.
    • This switch would not know where to send the packet.
  • It would need to send a message to the controller, on loopback 127.0.0.1.
    • The controller would have some algorithm programmed for deciding where to send it, which may require communications with other connected switches, which in turn may lead to communications with other hosts.
  • The switch will rely on some addressing protocol to identify the target device, either the MAC address or IP address.
    • Eventually, it will either determine a route, or be unable to, and inform the switch.
    • Assuming a route has been found, the switch will store this route in its routing table, and forward the packet appropriately.
    • Temporarily, when new packets arrive, the switch will not need to communicate with the controller to make decisions, and will just forward the packets directly as per the rules cached in its routing table.
    • Eventually, the switch will timeout, clearing out the routing table entries for that device, and will then need to contact the controller again for updated routes.
      • This is important as it will help the switch adapt to changes in the network, and can even support more complicated algorithms which change routes based on traffic.

The best part is, we now have tools for seeing all of this happen live!

We may wish to use WireShark and Mininet together to observe the flow of traffic.

First start a mininet emulation. Then, from a terminal in the Zorin VirtualBox, (not mininet), type:

sudo wireshark

Depending on if the Mininet network is running, you may see different interfaces. All of the interfaces on switches are available on the Zorin’s Wireshark. The hosts may not be.

As shown in the previous figure, the OpenFlow packets are always sent on the loopback interface. If you are writing OpenFlow protocols and wish to debug them, , this is the interface you should monitor. However, for now we just wish to monitor traffic. Due to the way mininet operates, the switches share the network namespace with our Zorin instance, so we can monitor these as well from WireShark, though we may not monitor hosts’ interface directly, however it is safe to assume the outgoing traffic on the switch and the incomming traffic on the device will be the same.

If we check out one of the switch interfaces, and then perform a ping across hosts, we should see the packets transfer between them.

We can make use of this to help us debug applications in settings similar to real world use.

So far we have primarily used built-in utilities on our hosts in Mininet. However, one advantage of the network emulator from a programming perspective is the ability to run your own code.

Each of the hosts have access to the same filesystem as the Zorin VirtualBox. While this may occasionally be problematic, e.g. when applications rely on config files, overall this convenience outweighs the drawbacks.

Copy the TCP Chat Application code from Lab 2 into the virtual machine, if it is not already there.

You may wish to open these in VSCodium, which has been installed in the newest version of the VirtualBox, and can be accessed from a terminal with:

codium .

The server should not need to make any changes, however, the client will need to connect the the server by IP address. Let’s find out what this should be:

Since our chat works with one server and two or more clients, let’s start up mininet in a configuration with three hosts. Out of the box Mininet supports a number of topologies, and we can even create our own! For now, let’s run:

sudo mn --topo=single,3

Read the startup comments and see that you understand the architecture of the network mininet has created.

Get the IP addresses of the connected machines with dump. Let’s use h1 as the server.

You will need to change the code in line 24, which previously read as

    reactor.connectTCP("localhost", 1234, ClientFactory())

To the correct IP address for h1. Then, in mininet, open an xterm for each host. Use a string format, e.g. "192.168.0.1"

mininet> xterm h1 h2 h3

make sure you are in the same directory as your source files, and run the server script on h1 and the client script on h2 and h3. Try sending messages, does it work?

Let’s try again with the same topology, this time adding four hosts. Have hosts h2 and h3 join the chat and send messages via chat. Using wireshark, verify if host 4 is able to see the messages. Based on this, how is the Switch acting, as a hub or a smart switch?

Solution We are unable to see the packets. This means that the Switch is acting as a smart switch, rather than a simple hub. It learns where our hosts are connected, and only sends packets along those lines.

Since we are trying to observe real world protocols, we would like see the way that simple ethernet works. Traditional ethernet networks operates like a hub.

  • A hub simply forwards all packets to all other hosts and trusts the appropriate hosts recieve those packets, and innapropriate hosts ignore those packets.
  • When learning networks it can be useful to observe this behaviour first-hand, however Mininet does not support this out of the box.

Since mininet does not support simple packet forwarding, or flooding, out of the box, we will need to use an external controller to do this for us. Recall from our discussion in Part 2 that the controller is simply a service running on a server - if we can start one of these ourselves and tell mininet to connect to it, it should be able to perform the same operations. I have already installed one such application for us in our Zorin virtualbox. It is called POX, and is a framework for developing OpenFlow controllers in python.

In a separe terminal window, start up POX as a simple flooding controller with the following command:

python ~/pox/pox.py --verbose forwarding.hub

POX is a framework for creating OpenFlow controllers. This forwarding.hub profile tells it behave as a hub does - whenever you recieve a packet, send it out on every other port. Thus your packet is guaranteed to arrive at your destination, with the downside of a LOT of repeated traffic.

In a separate terminal window, restart your simulation with the following:

sudo mn --topo=single,4 --controller=remote

Now whenever mininet switches need to learn about routing information, they will simply contact POX as an external controller, running on port 6633.

With this controller connected try to perform a ping operation. Notice that there is now minimal delay in the first message being sent compared to the others. Why might this be?

Solution This is a very simple routing operation, it doesn't require any computation or planning at all! In fact POX may proactively tells switches just to flood, and not to bother it at all! Check ou the source code in: ~/pox/pox/forwarding/hub.py

So let’s head back to our earlier example, of two PC’s trying to chat privately over TCP. If we examine h4’s connection on the switch, despite the messages only involving h1, h2 and h3:

The connection to h4 can still see it!

While you might suspect this configuration to be rare, similar real world situations exist, such as connecting an ethernet hub, or connecting to an unencrypted public wifi. This is a case where TLS, which we looked at last week, comes in handy - if the medium does not protect us, we may always rely on end-to-end encryption!

Also note this Flooding strategy will only work on the simplest of architectures, i.e. those without loops. Anything more complex will result in a broadcast storm.

Mininet has a number of topologies built in. We can access these directly from the command line by using the --topo= argument, as we saw earlier. For more info, check out the help file:

sudo mn -h

Where we can see:

  --topo=TOPO           linear|minimal|reversed|single|torus|tree[,param=value
                        ...] minimal=MinimalTopo linear=LinearTopo
                        reversed=SingleSwitchReversedTopo

Let’s investigate a few of these and see if we can figure out the architecture of each from the statement:

sudo mn --topo=linear,4
sudo mn --topo=linear,4
*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2 h3 h4 
*** Adding switches:
s1 s2 s3 s4 
*** Adding links:
(h1, s1) (h2, s2) (h3, s3) (h4, s4) (s2, s1) (s3, s2) (s4, s3) 
*** Configuring hosts
h1 h2 h3 h4 
*** Starting controller
c0 
*** Starting 4 switches
s1 s2 s3 s4 ...

We can see that this gives us 4 hosts and 4 switches, with one host connected to each switch.

image credit: David Mahler

Some topologies can take multiple arguements, with different functionality depending on the topology. Let’s try the following:

sudo mn --topo=linear,4,2

Which gives us:

sudo mn --topo=linear,4,2
*** Creating network
*** Adding controller
*** Adding hosts:
h1s1 h1s2 h1s3 h1s4 h2s1 h2s2 h2s3 h2s4 
*** Adding switches:
s1 s2 s3 s4 
*** Adding links:
(h1s1, s1) (h1s2, s2) (h1s3, s3) (h1s4, s4) (h2s1, s1) (h2s2, s2) (h2s3, s3) (h2s4, s4) (s2, s1) (s3, s2) (s4, s3) 
*** Configuring hosts
h1s1 h1s2 h1s3 h1s4 h2s1 h2s2 h2s3 h2s4 
*** Starting controller
c0 
*** Starting 4 switches
s1 s2 s3 s4 ...
*** Starting CLI:

We now how have four switches with two hosts per switch.

We can also do:

sudo mn --topo=tree,2,2
*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2 h3 h4 
*** Adding switches:
s1 s2 s3 
*** Adding links:
(s1, s2) (s1, s3) (s2, h1) (s2, h2) (s3, h3) (s3, h4) 
*** Configuring hosts
h1 h2 h3 h4 
*** Starting controller
c0 
*** Starting 3 switches
s1 s2 s3 ...

In this case the arguments refer to the depth and fanout of the tree.

Here is an image representation of a tree, though ours has additional hosts in each leaf:

image credit: David Mahler

This is an important point to notice that all of the switches are communicating with the same OpenFlow controller. Thus OpenFlow routing algorithms are typically centralized rather than decentralized.

The tree topology may appear more complex than previous topologies we looked at, but importantly, it involves no loops. The default controller built into mininet will handle it fine - it seems to fail only when loops are involved.

We can build our own topologies as well, using python code.

Custom topologies are stored in:

/home/zorin/mininet/custom/

Recall, the shortcut for /home/zorin is ~

If you would like to see an example of how this works, use codium to open up

codium ~/mininet/custom/topo-2sw-2host.py

Where we can see:

"""Custom topology example

Two directly connected switches plus a host for each switch:

   host --- switch --- switch --- host

Adding the 'topos' dict with a key/value pair to generate our newly defined
topology enables one to pass in '--topo=mytopo' from the command line.
"""

from mininet.topo import Topo

class MyTopo( Topo ):
    "Simple topology example."

    def build( self ):
        "Create custom topo."

        # Add hosts and switches
        leftHost = self.addHost( 'h1' )
        rightHost = self.addHost( 'h2' )
        leftSwitch = self.addSwitch( 's3' )
        rightSwitch = self.addSwitch( 's4' )

        # Add links
        self.addLink( leftHost, leftSwitch )
        self.addLink( leftSwitch, rightSwitch )
        self.addLink( rightSwitch, rightHost )


topos = { 'mytopo': ( lambda: MyTopo() ) }

In order to create a new topology, we just need to follow this template

  • create a class which extends the Topo class
  • implement the build( self ) class
  • use the function self.addHost( ... ) to add hosts.
    • It’s a good idea to use the same naming conventions as mininet, e.g. h1, h2 … etc.
  • use the function self.addSwitch( ... ) to add switches.
  • use the addLink( ... ) function to add links.
    • You may use programming logic, e.g. loops, to make this easier!
  • Finally, we need to give our class a name in the dictionary at the bottom, so that mininet will know which topology to build.

You may run this file by running:

cd ~/mininet/custom
sudo mn --custom topo-2sw-2host.py --topo mytopo

Read this output. Does this match up with your expectations for the topology?

*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2 
*** Adding switches:
s3 s4 
*** Adding links:
(h1, s3) (s3, s4) (s4, h2) 
*** Configuring hosts
h1 h2 
*** Starting controller
c0 
*** Starting 2 switches
s3 s4 ...

Let’s try creating our own. We’ll build a 2x2 grid of switches, with a gap, and connect a host on two of the corners:

copy topo-2sw-2host.py to broken-square.py:

And create the following:

"""Custom topology example

We will create a broken square topology as follows:

            swith  --- switch --- host
              |          
   host --- switch --- switch

"""

from mininet.topo import Topo

class MyTopo( Topo ):
    "Simple topology example."

    def build( self ):
        "Create custom topo."

        # Add hosts and switches
        leftHost = self.addHost( 'h1' )
        rightHost = self.addHost( 'h2' )
        lowerLeftSwitch = self.addSwitch( 's1x1' )
        lowerRightSwitch = self.addSwitch( 's1x2' )
        upperLeftSwitch = self.addSwitch( 's2x1' )
        upperRightSwitch = self.addSwitch( 's2x2' )


        # Add links
        self.addLink( leftHost, lowerLeftSwitch )
        self.addLink(rightHost, upperRightSwitch )
        self.addLink( lowerLeftSwitch, lowerRightSwitch )
        self.addLink( lowerLeftSwitch, upperLeftSwitch )
        self.addLink( upperLeftSwitch, upperRightSwitch )
        #self.addLink( lowerRightSwitch, upperRightSwitch )

topos = { 'brokenSquare': ( lambda: MyTopo() ) }

We are going to leave the last link unconnected. The default controller for mininet does not support switches with loops. Let’s confirm our topology works by running:

sudo mn --custom=brokenSquare.py --topo=brokenSquare

Ensure you can still ping across. Great it works!

sudo mn --custom=brokenSquare.py --topo=brokenSquare
*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2 
*** Adding switches:
s1x1 s1x2 s2x1 s2x2 
*** Adding links:
(h1, s1x1) (h2, s2x2) (s1x1, s1x2) (s1x1, s2x1) (s2x1, s2x2) 
*** Configuring hosts
h1 h2 
*** Starting controller
c0 
*** Starting 4 switches
s1x1 s1x2 s2x1 s2x2 ...
*** Starting CLI:
mininet> h1 ping h2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=6.46 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.553 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.103 ms
...

Why did we leave that last link disconnected? This is because otherwise we would have a loop. Mininet’s default controller is not equipped to handle loops. Try reconnecting it and see what happens. Uncomment the line, save, and rerun the simulation, then try pinging across:

$ sudo mn --custom=broken-square.py --topo=brokenSquare
*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2 
*** Adding switches:
s1x1 s1x2 s2x1 s2x2 
*** Adding links:
(h1, s1x1) (h2, s2x2) (s1x1, s1x2) (s1x1, s2x1) (s1x2, s2x2) (s2x1, s2x2) 
*** Configuring hosts
h1 h2 
*** Starting controller
c0 
*** Starting 4 switches
s1x1 s1x2 s2x1 s2x2 ...
*** Starting CLI:
mininet> h1 ping h2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
From 10.0.0.1 icmp_seq=1 Destination Host Unreachable
From 10.0.0.1 icmp_seq=2 Destination Host Unreachable
From 10.0.0.1 icmp_seq=3 Destination Host Unreachable
...

This time it has completely failed.

Recall earlier, we used the POX simple flooding protocol. What this protocol does is, whenever a packet is recieved in one port, it is simply ejected out every other port. This should definitely get our packet to the destination. What could go wrong? Let’s see it in action in this topology:

In one terminal

python ~/pox/pox.py --verbose forwarding.hub

In a separate terminal run

sudo mn --custom=broken-square.py --topo=brokenSquare --controller=remote

And then have h1 ping h2

We get flooded with:

...
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=1658 ms (DUP!)
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=1658 ms (DUP!)
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=1658 ms (DUP!)
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=1658 ms (DUP!)
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=1658 ms (DUP!)
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=687 ms (DUP!)
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=687 ms (DUP!)
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=687 ms (DUP!)
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=687 ms (DUP!)
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=687 ms (DUP!)
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=687 ms (DUP!)
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=687 ms (DUP!)
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=687 ms (DUP!)
...

The (DUP!) shows that this is a duplicated ping message. For some reason the packets are getting sent to use many many times. Why might this be?

Consider the behaviour in the lowerLeftSwitch and upperRightSwitch. Each of these has one entry and two exits. So every time they get a packet in, they sent two out, one goes to the destination, and the other gets caught in the loop forever. Worse, each time a packet arrives, another copy is made! This is called a “Broadcast Storm”.

With this simple forwarding strategy, loops are useless. However, in the real world, network architecture without loops are very fragile, as these can have a single point of failure. Loops create redundancy, alternate paths that traffic can take in the event of a failure. However, these also require more complex routing algorithms, such as spanning trees, which only uses a subset of physical connection resembling a tree.

In the next lab we will look at more complex algorithms for solving some of these problems, and investigate the OpenFlow protocol which is used by the controller to train the network.

If you would like to see some more examples of topologies, check out the source code in:

~/mininet/mininet/topo.py

Starting around line 295.

This will also show you how to use arguments, as we did with our single and tree topologies.

and also

~/mininet/mininet/topolib.py

for the Tree and Torus topologies.

As with the previous assignment, in this one you will be submitting a report. Please collect figures and screenshot as you go, as you will be summarize your findings in a brief report!

In this assignment, you are to use mininet to build a custom tree structure as shown in the following figure:

  1. Create a mininet simulation and verify connectivity using the default mininet controller from h1 and h8, and simple ping tests.

  2. Load the TCP-based chat server from lab 2 with the server on h5, and clients on h1 and h8. Using the mininet controller for routing, and wireshark for testing, see if either h4 and/or h6 can see the chat packets being sent and read their contents.

  3. Replace the mininet controller with the POX Flooding algorithm. Do the TCP-based chat packets still arive at their destination? Are there any broadcast storms?

  4. With the flooding controller, verify if either h4 and/or h6 can see the chat packets being sent and read their contents.

  5. Finally, using the SSL-based chat protocol from Part 2 of lab 2, using the flooding algorithm. See if either h4 and/or h6 can see the chat packets being sent and read their contents.

As with Lab 2, please summarize your findings in a report. Also submit source code for the mininet network. Please use screen capture figures to demonstrate your findings.

Please submit your lab documents and code to URCourses by the due date. If you are working with a partner, you need only submit one copy, but please include both partners names on all assignments.

Mininet Walkthrough. http://mininet.org/walkthrough/

vvv This guy rocks! Watch all his videos! vvv

David Mahler. Mininet Custom Topologies. https://www.youtube.com/watch?v=jmlgXaocwiE

David Mahler. Introduction to Mininet. https://www.youtube.com/watch?v=yHUNeyaQKWY

David Mahler. Mininet and Remote SDN Controllers (Floodlight + Pox). https://www.youtube.com/watch?v=CPasnNg9Z4I

David Mahler. Introduction to OpenFlow. https://www.youtube.com/watch?v=l25Ukkmk6Sk

A nice overview of the SDN field, with a small section on mininet and POX

SDN: Software Defined Networks. https://www.oreilly.com/library/view/sdn-software-defined/9781449342425/ Free with your O’Reilly account, woooooo!