Understanding Ryu OpenFlow Controller, mininet, WireShark and tcpdump

This is probably one of the longest posts I have written on Stack Overflow. I have been learning about OpenFlow, SDN and Ryu and would like to document my knowledge for a beginner here. Please correct/edit my post if needed.

This short guide assumes you already have knowledge of computer networks and major networking protocols. This guide will help you get started with OpenFlow from system setup.

1. What is OpenFlow and SDN?

Please read SDN / OpenFlow | Flowgrammable.

Further reading: The Future of Networking, and the Past of Protocols by Scott Shenker and Software-defined Networking, IEEE INFOCOM 2009.

Before you start:

The Infrastructure layer includes the routers and switches inside the network core.

The Control layer includes the PC that runs an OpenFlow controller along with the controller itself.

The Application layer includes the applications that run on top of that controller. In Ryu, these applications are written in Python.

OpenFlow is a protocol using which the Infrastructure and the Control layer interact. OpenFlow does not provide an API of itself. It is an open source protocol that is used by vendors who develop OpenFlow capable switches and by developers who write the controllers, like Ryu. The API is provided by the controller.

2. Setting up the Ryu OpenFlow controller on Debian 8

Prerequisites

You need internet access. If you are running Debian inside a Virtual Machine, issue the following command to automatically configure your Ethernet interface via NAT:

su  
dhclient eth0

Enable sudo

Debian does not come with sudo by default. Some Ryu applications you'll use later on require sudo. You can install sudo and add yourself to the sudo'ers list as follows:

su
apt-get install sudo # you might need to do apt-get update first!
visudo

Find the line that says %sudo ALL=(ALL:ALL) ALL and add an entry immediately below it:

yourusername ALL=(ALL:ALL) ALL

Press CTRL+X and then press Y to save the changes to the sudoers file. Now you can logout as root to return to your own shell

exit

Enable Optimal Screen Resolution (VM Only)

If you are running Debian in Virtual Box, the default installation does not enable full screen resolution support for Virtual Box. You'll need a bigger screen later in section 3. It's a good idea to enable it now.

In the Virtual Machine's window, click Devices > Insert Guest Additions CD Image ...

Then cd to the directory containing the files

cd /media/cdrom

Debian won't let you run the script due to permission issues. Copy the files to your home directory, change the permissions, and then run it:

mkdir ~/VBOXGUEST  
cp * ~/VBOXGUEST  
cd ~/VBOXGUEST  
chmod 755 *  
sudo ./VBoxLinuxAdditions.run

Reboot

sudo shutdown -r now

Install Git

sudo apt-get install git

Install Mininet

Mininet allows you to virtually emulate various network interfaces on your laptop/PC. Install it using Git:

cd ~  # if you are in some other directory
git clone git://github.com/mininet/mininet
cd mininet
git tag  # this will list available versions
git checkout -b 2.2.1 2.2.1  # replace 2.2.1 with the version you wish to install
cd ..
mininet/util/install.sh -a  # default installation, includes all components, recommended

I recommend you install the OpenFlow Wireshark Dissector. You can later install Wireshark to analyze packets. The OpenFlow Wireshark Dissector helps Wireshark fetch as much information from OpenFlow packets as possible.

mininet/util/install.sh -h

Run the following command to check your mininet installation:

sudo mn --test pingall

Install Ryu OpenFlow Controller

An OpenFlow Controller communicates between the Control Layer and the Infrastructure layer using the OpenFlow protocol. Also, it's the controller that provides an API to develop SDN applications that run in the Application Layer (on top of the Control Layer). There are numerous OpenFlow controllers. Ryu OpenFlow controller is one that uses Python scripts as its applications. Again, install it using Git:

cd ~
git clone git://github.com/osrg/ryu.git

Install Wireshark

sudo apt-get install wireshark

Install the supporting Python modules

Debian 8.3 does come with both Python 2.7 and 3.4 installed by default. However, you need to install some Python modules that the Ryu applications (Python scripts) use. You can install Python modules using pip:

cd ~/ryu  
sudo apt-get install python-dev python-pip python-setuptools  
sudo pip install .

the above will automatically run setup.py located in this directory and fetch the missing Python modules from the Python Package Index. The script will automatically install all relevant modules. However, do run the following to make sure you aren't missing any module later:

sudo pip install webob  
sudo pip install eventlet  
sudo pip install paramiko  
sudo pip install routes  

Starting up

Start mininet to emulate 3 hosts and a switch using the following command:

sudo mn --topo single,3 --mac --switch ovsk --controller remote

You will see a mininet prompt. This prompt can be used to ping hosts, send packets between them, etc.

Open up another terminal window to run Ryu. In this example, we will run an application (simple_switch_13.py) that will emulate a simple layer 2 switch that will forward all received packets to all ports except the one received on.

cd ~/ryu  
PYTHONPATH=. ./bin/ryu-manager ryu/app/simple_switch_13.py

Make sure you are in your home directory when you run this.

You are all set. To ping hosts and analyse the packet transmissions, please move onto the next section.

3. Experimenting with Wireshark and tcpdump

In this section, we will fire packets from one host to another using mininet, and analyze the resulting transmissions using tcpdump and Wireshark.

The way the packets get transmitted is exactly what we can control in Software Defined Networking. We do this by writing different applications that run on top of the controller. These applications form the Application layer of the SDN control plane.

Set up topology and run the controlling application

Note: In the earlier section, you created a topology with mininet, and started a Ryu application to control the transmissions. In case you rebooted, or exited any of them, I repeat the commands to create the topology and start the Ryu application here:

cd ~
sudo mn --topo single,3 --mac --switch ovsk --controller remote

and in a separate terminal window:

cd ~/ryu
PYTHONPATH=. ./bin/ryu-manager ryu/app/simple_switch_13.py

Play with packets

In the mininet prompt, issue the following to open a console window for each of the three hosts in the topology you created:

mininet> xterm h1 h2 h3

Stack these consoles so you can see them all simultaneously! Then in the xterms for h2 and h3, run tcpdump, a utility to print the packets seen by a host:

tcpdump -XX -n -i h2-eth0  
tcpdump -XX -n -i h3-eth0

Note: If you have used Wireshark earlier, it's like capturing packets on the eth0 interface of these two hosts respectively.

When creating topology, mininet assigned the following IP addresses to the three hosts:

h1: 10.0.0.1  
h2: 10.0.0.2  
h3: 10.0.0.3

From host 1's shell, ping host 2 and host 3 and observe the effect on the other two consoles after each command:

ping 10.0.0.2  
ping 10.0.0.3

Try pinging an unreachable (non existent host), and see the effect on the consoles:

ping 10.0.0.7

You should have observed the ICMP (ping) and ARP (who has this IP address) protocols live in this section! You can also do the above using Wireshark instead of tcpdump. That's a graphical alternative to tcpdump.

Note: The way the packets are forwarded ALL depends on the application running on top of Ryu. You could write an application to drop all packets. In that case, your pings would produce no effect on the other two consoles.

4. Understanding the basic Layer 2 Switch application

In this section, we analyze the working of a simplified version of a Layer 2 Switch application that controlled the transmissions of packets in section 3.

Working of a learning bridge (or a Layer 2 Switch)

I mentioned earlier that if you are reading this guide, I assume you already have knowledge of basic networking protocols, (which includes the working of a layer 2 switch, or a learning bridge, or an Ethernet switch!) I'll summarize it in a few lines below regardless.

A "learning" bridge stores a database of the hosts its connected to, against it's ports. The hosts are identified by the MAC address of their network card, which looks like this: ab:cd:ef:12:34:56 (it's in hexadecimal). The ports are identified simply by their number. For example, a switch with 4 ports has port 1, 2, 3 and 4.

If a switch receives a packet on its port 2, it will look at the destination MAC address (which host it's destined to) of that packet. It then looks into it's database to see if it knows which port is that host connected to. If it finds it out, it forwards that packet ONLY to that specific port. But if it doesn't have an entry in it's database yet, it floods that packet to ALL ports, and the hosts can check for themselves if the packet was destined for them.

At the same time, the switch looks at the source MAC address of that packet, and it immediately knows that host X is located at port 2. It stores that entry in that database. So now you know that if the destination host replies to the source host, the switch won't have to flood the reply packet!

Introduction to the Ryu API Python code

Instead of going directly to simple_switch_13.py, let's choose a very simple program that has no "learning" capability. For now, there is no forwarding database. The below program is just a simple layer 2 switch that transmits a received packet to all ports (floods the packet):

    from ryu.base import app_manager
    from ryu.controller import ofp_event
    from ryu.controller.handler import MAIN_DISPATCHER
    from ryu.controller.handler import set_ev_cls

    class L2Switch(app_manager.RyuApp):
        def __init__(self, *args, **kwargs):
            super(L2Switch, self).__init__(*args, **kwargs)

        @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
        def packet_in_handler(self, ev):
            msg = ev.msg
            dp = msg.datapath
            ofp = dp.ofproto
            ofp_parser = dp.ofproto_parser

            actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]
            out = ofp_parser.OFPPacketOut(
                datapath=dp, buffer_id=msg.buffer_id, in_port=msg.in_port,
                actions=actions)
            dp.send_msg(out)

The imports

I will not delve into the import statements yet. We will discuss the imports individually as we analyze the code that uses them.

The basic application skeleton

The following code is a perfectly complete Ryu application. In fact you can execute it too! It won't do anything though:

    from ryu.base import app_manager

    class L2Switch(app_manager.RyuApp):
        def __init__(self, *args, **kwargs):
            super(L2Switch, self).__init__(*args, **kwargs)

As an argument to the class, we pass ryu.base.app_manager.RyuApp import (imported in the first line). From the Ryu API handbook, app_manager class is the central management of Ryu applications. It loads Ryu applications, provide contexts to them and routes messages among Ryu applications.

The EventOFPPacketIn Event

A new method packet_in_handler is added to L2Switch class. This is called when Ryu receives an OpenFlow packet_in message. When Ryu receives a packet_in message, a ofp_event.EventOFPPacketIn event is raised. The set_ev_cls decorator tells Ryu when the associated function, packet_in_handler should be called.

The first argument of the set_ev_cls decorator indicates an event that makes function called. As you expect easily, every time a ofp_event.EventOFPPacketIn event is raised, this function is called.

The second argument indicates the state of the switch when you want to allow Ryu to handle an event. Probably, you want to ignore OpenFlow packet_in messages before the handshake between Ryu and the switch finishes. Using MAIN_DISPATCHER as the second argument means this function is called only after the negotiation completes. MAIN_DISPATCHER denotes the normal state of the switch. During the initialization stage, the switch is in HANDSHAKE_DISPATCHER state!

Now let's look at the body of the function. We'll break it down into two parts.

    msg = ev.msg  
    dp = msg.datapath  
    ofp = dp.ofproto  
    ofp_parser = dp.ofproto_parser

ev.msg is a data structure that contains the received packet.

msg.dp is an object inside that data structure that represents a datapath (switch).

dp.ofproto and dp.ofproto_parser are objects that represent the OpenFlow protocol that Ryu and the switch negotiated.

    actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]  
    out = ofp_parser.OFPPacketOut(  
        datapath=dp, buffer_id=msg.buffer_id, in_port=msg.in_port,  
        actions=actions)  
    dp.send_msg(out)

OFPActionOutput class is used with a packet_out message to specify a switch port that you want to send the packet out of. Since there is no forwarding database in this simplified application, we flood the packet to all ports, so the constant OFPP_FLOOD is used.

OFPPacketOut class is used to build a packet_out message.

By using datapath class's send_msg method, you can send an OpenFlow message object to the ports defined in the actions variable. I repeat, in this case, actions is built such that the destination includes ALL ports.

Events

You repeatedly saw the term event in the above code. In event driven programming, the flow of the program is controlled by events, which are raised by messages received by the system (e.g. EventOFPPacketIn is raised when the packet_in message is received by Ryu from the (OpenFlow enabled) switch). We earlier discussed that OpenFlow is a protocol using which the controller (Ryu, PC) and the infrastructure (or switch) communicate. Messages like packet_in are exactly what the communication between the two looks like using the OpenFlow protocol!

Next steps

You might want to go ahead and build your own Ryu applications. Learning the Ryu API (or the Python language, if you are not already familiar with it) might be a good point to start. Good luck!


Something you may find useful for working with the Ryu controller is Ryuretic. Ryuretic is a modular, SDN-based, framework for network application development. It allows network operators to work directly with packet header fields at various levels of the OSI model, including L2, L3, L4, and shim layer protocols. The user simply chooses match fields and selects provided operations to update the OpenFlow switch.

The Ryuretic backend renders all events to the user as a pkt (a dictionary object), and the contents of the pkt are retrieved by providing the header field of interest (e.g, pkt['srcmac'], pkt['dstmac'], pkt['ethtype'], pkt['inport'], pkt['srcip'], etc.) Using the information from the pkt, the user can choose which fields to match and what action (fwd, drop, redirect, mirror, craft) to take when a match is found.

To install Ryuretic, simply copy the [files] (https://github.com/Ryuretic/RyureticLabs/tree/master/ryu/ryu/app/Ryuretic) to the directory /ryu/ryu/app/Ryuretic. If you installed Ryu, then you already have the /ryu/ryu/app directory. You just need to create the Ryuretic directory and copy the files there.

Ryuretic Labs provides setup instruction and some use cases for implementing security features on SDNs using Ryuretic. It also provides a Mininet testbed for testing your network applications on the VM provided by SDN-Hub.