Emulating Networks with Mininet

Some time ago I came across Mininet which allows us setup and emulate networks on a single machine. Mininet uses the Linux container technology (similar to Docker) to run multiple lightweight isolated Linux environments on a single Linux host. I never had time to play with it, but recently I wanted to experiment with a new transport protocol and see how it reacted to lossy links and Mininet seemed like an good choice for emulating the network.

Mininet seems quite popular for people how wants to emulate and experiment with newer networking concepts such as SDN (Software Defined Networks). Beause the overhead of running Linux containers is very low, it allows us to emulate large networks with many connected hosts on a single computer (unfeasible with virtual machines). But, it can also be used to emulated simple networks like point-to-point links, with some loss and delay characteristics. Which is what we will do here. Another great strength of Mininet is that it allows us to run existing applications over these networks. So for example on one emulated host I can open the Firefox browser and on another host a webserver. I can even connect hosts on my emulated network to the real network and let them speak to services on the Internet. How great is that!?

Let get this thing up and running.

Installing mininet

So the first thing we need to do is to install Mininet. There are a copule of options to choose from the easiest way it to use the prepared virtual machine. You can find additional instructions on how to set it up on the Mininet webpage here.

Note: The Mininet setup guide suggests that you add a "Host-only Adapter", when I initially tried this, it gave me a "no host only network adapter selected" error. This was resolve following the suggestions described here.

For some reason after booting the Mininet virtual machine the host only network was not brought up, the commands to make it accessible (run the following commands in the virtual machine):

sudo ip link set dev eth1 up
sudo dhclient eth1
ip link show

First we bring up the interface, then ask for an IP using DHCP and finally we show the available interfaces and their IPs.

Alternatively you can add the following to /etc/network/interfaces inside the virtual machine:

auto eth1
iface eth1 inet dhcp

Now I could ssh from my machine to the Mininet virtual machine using:

ssh mininet@192.168.56.101

To make things a bit easier I add the following to .ssh/config:

Host mininet
User mininet
HostName 192.168.56.101

Now I can login using ssh mininet, see the bottom of the Mininet VM setup notes guide to see how to setup password less login.

Shared folders

To make it easier to move files between the virtual machine and the host computer you can setup a shared folder. In Virtualbox go to the settings for the Mininet virtual machine and you should be able to select a host folder that you want to share in the virtual machine. In my case is just created a folder called vm_mininet in my home directory.

To access shared folders inside the virtual machine we need to install the Virtualbox Guest Additions. Fortunately there is a package for that, so inside the Mininet virtual machine run:

sudo apt-get install virtualbox-guest-utils

As a final step you need to add the mininet user to the vboxsf group to have access to the folder:

usermod -aG vboxsf mininet

Rebooting the virtual machine, and my shared folder shows up under /media/sf_vm_mininet.

Creating the network

Now that we have the Mininet virtual machine up and running we can start to emulate some networks. One of the really cool things about Mininet is how easy it is to define network topologies using Python scripts. As an example lets try to create a network with two hosts and ping from one to the other.

The script for setting this up is available here. The %%file simple_ping.py is an iPython cell magic command that will save the content of the cell to a file called simple_ping.py, so that we later can runn the script in our Mininet virtual machine.

In [8]:
%%file simple_ping.py
#!/usr/bin/python

"""
Simple example of point-to-point link running ping
"""

from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import OVSController
from mininet.link import TCLink
from mininet.util import pmonitor
from mininet.log import setLogLevel

if __name__ == '__main__':
    setLogLevel('info')

    # Build the topology we want to play with:
    #
    #             +-----------------------+
    #             |     10Mbit/s link     |
    #             |     5ms delay         |
    #             |     10% packet loss   |
    #             |                       |
    #             +-----------+-----------+
    #                         |
    #                         |
    #                         |
    #                         |
    # +-------------+         v        +-------------+
    # |             |                  |             |
    # | host 1 (h1) +------------------+ host 2 (h2) |
    # |             |                  |             |
    # +-------------+                  +-------------+
    #   
    topo = Topo()
    topo.addHost('h1')
    topo.addHost('h2')
    topo.addLink('h1','h2', bw=10, delay='5ms', loss=10)

    # The TCLink is needed for use to set the bandwidth, delay and loss
    # constraints on the link
    #
    # waitConnected
    net = Mininet(topo=topo,
                  link=TCLink,
                  waitConnected=True)
    net.start()

    h1, h2 = net.getNodeByName('h1', 'h2')

    # Will open the two ping processes on both hosts and read the output
    popens = {}
    popens[h1] = h1.popen('ping -c10 {}'.format(h2.IP()))
    popens[h2] = h2.popen('ping -c10 {}'.format(h1.IP()))

    for host, line in pmonitor(popens):
        if host:
            # Output each line written as "<hX>: some output", where X
            # will be replaced by the host number i.e. 1 or 2.
            print("<{}>: {}".format(host.name, line.strip()))

    net.stop()
Overwriting simple_ping.py

To run the above network simply copy the simple_ping.py file to the Mininet virtual machine and run it (e.g. using the shared folder):

sudo ./simple_ping.py

The script should now generate a bunch of output, giving you diagnostics about the network and output from the two ping processes.

mininet@mininet-vm:/media/sf_vm_mininet$ sudo ./simple_ping.py 
*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2 
*** Adding switches:

*** Adding links:
(10.00Mbit 5ms delay 10% loss) (10.00Mbit 5ms delay 10% loss) (h1, h2) 
*** Configuring hosts
h1 h2 
*** Starting controller
c0 
*** Starting 0 switches

*** Waiting for switches to connect

<h2>: PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
<h2>: 64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=12.5 ms
<h1>: PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
<h1>: 64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=17.8 ms
<h1>: 64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=10.4 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=10.2 ms
<h1>: 64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=10.2 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=10.2 ms
<h1>: 64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=10.2 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=4 ttl=64 time=10.2 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=5 ttl=64 time=10.1 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=6 ttl=64 time=10.2 ms
<h1>: 64 bytes from 10.0.0.2: icmp_seq=6 ttl=64 time=10.2 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=7 ttl=64 time=10.1 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=8 ttl=64 time=10.1 ms
<h1>: 64 bytes from 10.0.0.2: icmp_seq=8 ttl=64 time=10.1 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=9 ttl=64 time=10.1 ms
<h1>: 64 bytes from 10.0.0.2: icmp_seq=9 ttl=64 time=11.0 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=10 ttl=64 time=10.1 ms
<h2>: 
<h2>: --- 10.0.0.1 ping statistics ---
<h2>: 10 packets transmitted, 10 received, 0% packet loss, time 9014ms
<h2>: rtt min/avg/max/mdev = 10.139/10.442/12.594/0.725 ms
<h1>: 64 bytes from 10.0.0.2: icmp_seq=10 ttl=64 time=10.1 ms
<h1>: 
<h1>: --- 10.0.0.2 ping statistics ---
<h1>: 10 packets transmitted, 8 received, 20% packet loss, time 9024ms
<h1>: rtt min/avg/max/mdev = 10.144/11.294/17.819/2.484 ms
*** Stopping 1 controllers
c0 
*** Stopping 0 switches

*** Stopping 1 links

*** Stopping 2 hosts
h1 h2 
*** Done

Pretty cool right?

The code was tested with Mininet version 2.2.0, to see which version you have run:

>>> import mininet
>>> print(mininet.net.VERSION)
2.2.0

For more examples see the official Mininet examples. Also you can find lots more information on the API etc. on the Mininet pages.

This blog post was written entirely in the IPython Notebook. The full notebook can be downloaded here, or viewed statically here.