Jump to: navigation, search

SDN Programming using High Level Programming Abstractions

Introduction

This tutorial is created for the FAST Maple Tutorial during ODL Summit 2016.

At various points when slides are referred to, those slides can be found attached slides.

Also mentioned is a "USB key". That USB Key Image can be found here

Finally, after this tutorial is recorded at the ODL Summit 2016, we will post the video clips at YouTube.

Conceptual Intro: The FAST Maple SDN programming model

We will go over this part of the slides together as a group.

Conceptual Intro: The programming-level gap problem, and the Maple approach

We will also go over this part of the slides as a group; in particular we will discuss the issues of programming level gaps, the access library wrapper idea of Maple, and the basic Maple programming model: How to access input, and specify output.

Getting started

Overview

Basic SDN programming involves 3 components: (1) the network to be controlled; (2) the controller that controls the network; and (3) the programming environment. In this tutorial, we use the following setup:

  • A mininet: the network to be controlled, where the switches in the mininet use OpenFlow;
  • An OpenDaylight controller: the controller controlling the mininet;
  • A Browser-based IDE: the programming environment to simplify tasks such as writing programs, uploading them to the OpenDaylight controller, and viewing the system status.

We install the mininet, the OpenDaylight Controller, and the backend of the IDE on a VM. You use a browser as the front end to access the IDE backend.

Install and start the VM

You have been provided with a USB key to help you install the VM. The USB key contains:

  • VirtualBox 5.1.6 Installer
  • FastMaple.ova - The aforementioned dev VM


Take the following steps to install and start the FastMaple VM:

  • If you do not have VirtualBox, use the VirtualBox Installer to install it;
  • Import FastMaple.ova VM into Virtual Box (File->Import Appliance...);
  • Boot the VM and login ( localhost login: vagrant, password: vagrant).

Start the IDE

After you login to the VM, use the login VM terminal to start the IDE backend, using the following command:

tutorial/cloud9/start.sh

You should see the message below to verify that the IDE backend has started properly:

 vagrant@vagrant:~$ tutorial/cloud9/start.sh 
 Starting test-config
 Connect server listening at http://10.0.2.15:9000
 CDN: version standalone initialized /home/vagrant/tutorial/cloud9/build
 Started '/home/vagrant/tutorial/cloud9/configs/test-config' with config 'standalone'!
 Cloud9 is up and running
 Controller manager listening on  3000
 Refresh cache list
 Cache List: {}
 ......

With the IDE backend running, you can use a browser to do most of your tasks now. From your host machine, go to the URL http://localhost:9000 to access the IDE backend. You should see the IDE as the following:

Blank-ide.png

Congratulations! You have the IDE up and running.

Start the controller

Our programming environment allows you to deploy your program on multiple controllers, to test the same program on different networks (e.g., one test mininet and one real network).

In this tutorial, we use an OpenDaylight controller controlling a mininet. Recall that the tutorial VM contains an OpenDaylight controller and we will use it. On the lower right of the IDE, find a bash terminal (labelled "bash - localhost"). This terminal runs a bash shell on the VM hosting the IDE backend. If you do not see this terminal, click the plus icon, and choose New Terminal, and you will get such a terminal. We refer to this terminal as the controller terminal. Type the following command into this terminal to start the controller on the VM:

start_maple_controller

The terminal will give the following output about the controller. The key sentence to wait for is "ODLMapleProvider Session Initiated". You may need to wait for a few seconds for the output to appear.

Controller-success.png

At this point, the controller which includes the Maple system is ready.

Connect IDE to the controller

Now click the panel labelled "Controller" on the left side of the IDE. This is where you manage controllers used in your programming. Since there is only one controller you started in the previous step, you connect to it by clicking the "Add" button. Give information on the controller as follows:

Add-a-controller.png

Wait for a few seconds for the IDE to connect to the controller. When the yellow circle beside the controller turns green, the connection is complete. Now press the star button above the list of controllers to set it as the default controller---you will see the word default is appended at the end of the controller name.

Set-default-controller.png

Create a mininet topology

In real deployment, a controller connects to real networks. In this tutorial, we use a mininet. The IDE provides a utility to allow you to create mininet topologies to be controlled by controllers. Please follow along as we show you how to use this nice utility:

File -> New -> Mininet Topology

New-topology.png

Then you will see a pop-up canvas. Draw a topology following:

  • Shift key + click mouse: create a host ( orange circle ); you can assign a name to the host while creating it.
  • Alt key + click mouse: create a switch ( blue circle); you can assign a name to the switch while creating it.
  • You can use Shift key + click on a host or switch to modify its name.
  • Hold on Shift key and drag from a node to another one: you create a link between them.
  • Click a node or a link and press DELETE key: delete a node or a link.
  • Ctrl + S (or CMD + S in Mac OSX) to save a topology file to the workspace.


As an exercise, create a topology as below:

Sample-topology.png

After you are done with creating a topology, save it in to the work space.

Save-topology.png

As we will use the same mininet across the tutorial, it has to have some complexity to support later exercises. To save time, we have prepared the following mininet topology for you.

Start the test mininet

The test topology is a little more complex than the previous one in Create a mininet topology.

Topo-graph.png

Create a test mininet for the preceding network topology is easy. Start a terminal on the desktop VM. Type the following command:

cd /home/vagrant/Maple_Topo_Scripts
start_maple_mininet

After the command, you will have a terminal where you can control the mininet. You refer to this window as the mininet terminal.

Finally, you have your dev environment fully set up: the controller is running, the IDE is running, and the test mininet is also running. To help you find locations to issue command: always remember that the controller terminal is at the IDE, and the mininet terminal is a window in the VM desktop.

Create a Maple project

Let's start Maple programming. Maple programming starts with a Maple project. To help you better understand the IDE, remember that a Maple project contains one to multiple Maple Apps. We will use one Maple App first and then introduce multiple later.

Use the browser IDE to create a Maple project, and name it FastMapleODL16: File -> New -> Maple Project

Create-maple-project.png

Create-maple-project-2.png

After clicking "OK", you should see that a Maple Project named "FASTMapleODL16" is created! When the IDE creates a Maple Project, it creates many files to give you flexibility in customization. In most cases, however, you deal with only a single Maple App .java file. In this tutorial, it is ODL16MapleApp.java, created automatically. Go to impl/src/main/java/org/opendaylight/mapleapp/impl and you will see the file. You will edit this file to specify the algorithmic policy.

Maple-app-javafile.png

We know you are very excited now and cannot wait to write down your first Maple App. Let's do it!

Exercise M1: Write your first Maple App

Objective: Understand basic Maple programming, on input access (pkt attributes), output construction (route action, set-packet-header actions):

Write M1

The first program is simple: it sets up routes between h1 to h2. If the traffic is HTTP traffic (port 80), the route from h1 to h2 will be the higher one (h1 -> s1 -> s2 -> s4 ->h2); otherwise, the route will be the lower one (h1 -> s1 -> s3 -> s4 > h2 ). You may consider the top one as high priority, and the lower one lower priority. The traffic for the reverse direction (h2->h1) should be the same as the forward direction (h1->h2). The preceding hardcoded routes are not professional, but the goal is to get you familiar with the syntax first and we will fix the issue in later exercises.

Specifically, a path that a Maple App returns is simple: it is a sequence of switch:egress ports. Let's look at the topology to and learn how to specify the path (h1 -> s1 -> s2 -> s4 ->h2). It should be "openflow:1:3", "openflow:2:2", "openflow:4:1".

From the IDE source panel,

  • Choose FASTMapleODL16.java;
  • Modify the FASTMapleODL16.java as follows:


First, import a set of classes which will be used later:

import org.opendaylight.maple.core.increment.app.MapleAppBase;
import org.opendaylight.maple.core.increment.packet.Ethernet;
import org.opendaylight.maple.core.increment.packet.IPv4;
import org.opendaylight.maple.core.increment.tracetree.MaplePacket;
import org.opendaylight.maple.core.increment.tracetree.Route;


Then, define a set of constants at the beginning of FASTMapleODL16.java:

	private static final String      H1    = "10.0.0.1";
	private static final int H1_IP = IPv4.toIPv4Address(H1);

	private static final String      H2    = "10.0.0.2";
	private static final int H2_IP = IPv4.toIPv4Address(H2);

	private static final int HTTP_PORT = 80;

	private static final String[] H12_HIGH_PATH = { "openflow:1:3", "openflow:2:2", "openflow:4:1" };
	private static final String[] H12_LOW_PATH  = { "openflow:1:4", "openflow:3:2", "openflow:4:1" };
	private static final String[] H21_HIGH_PATH = { "openflow:4:4", "openflow:2:1", "openflow:1:1" };
	private static final String[] H21_LOW_PATH  = { "openflow:4:5", "openflow:3:1", "openflow:1:1" };


Then modify the onPacket method to become the following:

	        int ethType = pkt.ethType();

		// For IPv4 traffic only
		if ( ethType == Ethernet.TYPE_IPv4) {
			
			// H1 -> H2
			if ( pkt.IPv4SrcIs(H1_IP) && pkt.IPv4DstIs(H2_IP) ) {

				String[] path = null;

				if ( ! pkt.TCPDstPortIs(HTTP_PORT) ) {  // All non HTTP IP, e.g., UDP, PING, SSH
					path = H12_LOW_PATH; 
				} else {                                // Only HTTP traffic
					path = H12_HIGH_PATH;
				}

				pkt.setRoute(path);
			}

			// H2 -> H1
			else if (  pkt.IPv4SrcIs(H2_IP) && pkt.IPv4DstIs(H1_IP) ) {

				String[] path = null;

				if ( ! pkt.TCPSrcPortIs(HTTP_PORT) ) {
					path = H21_LOW_PATH;
				} else {
					path = H21_HIGH_PATH;
				}
				pkt.setRoute(path);
            
		    // All other pairs non than H1 <-> H2
			} else {

				pkt.setRoute(Route.DROP);

			}
		}                       // end of ethType == Ethernet.TYPE_IPv4

		else {                  // Other type of traffic handled by another Maple App
			passToNext(pkt);
		}

If you want to cut and paste the whole code (you need to change the file name from M1 to the original file name if you cut and paste the whole code), you can find the code of M1 from this link: https://github.com/snlab/fastmaple16/blob/master/M1.java But directly cut and paste the whole file is not recommended, as we want you to read more about the code.

Let's read the code together to better understand its intention.

See flow tables before deploying M1

Before we build and deploy M1, first see flow tables at switches before any deployment. At the mininet terminal, type the following commands to see the flow tables at the switches. They should be empty.

mininet> sh ovs-ofctl dump-flows s1 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):

mininet> sh ovs-ofctl dump-flows s2 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):

mininet> sh ovs-ofctl dump-flows s4 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):

Build and deploy M1

Now, we build and deploy M1. The IDE makes this job quite easy:

  • First right click on the FastMapleODL16 project folder on the left and choose Set as Active. This makes the project as the current active one. It is necessary because the IDE can have multiple projects open at the same time.
  • Run -> Run Configurations -> Default Controller


A terminal tab on the IDE shows progress: first files are compiled, and you should see at the end a message "Deploy successfully", at which point the Maple project has been installed at the default controller.

Maple-app-archetype-stucture.png

Now, display the flow tables at the switches again. You should see that the flow tables are still empty, as no packets have been triggered. Remember, Maple by default is a reactive design.

Let's generate some traffic now. First, let h1 ping h2:

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=3 ttl=64 time=0.796 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.067 ms
64 bytes from 10.0.0.2: icmp_seq=5 ttl=64 time=0.053 ms

Of course, h1 can ping h2. Read the source code of the Maple App and think which path the ping packets should take, and what flow rules should be generated.

Now, display the flow tables:

mininet> sh ovs-ofctl dump-flows s1 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000017, duration=13.046s, table=0, n_packets=3, n_bytes=294, priority=2,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=output:1
 cookie=0x3a0000000000000e, duration=13.134s, table=0, n_packets=3, n_bytes=294, priority=6,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:4
 cookie=0x3a00000000000011, duration=13.094s, table=0, n_packets=0, n_bytes=0, priority=7,tcp,nw_src=10.0.0.1,nw_dst=10.0.0.2,tp_dst=80 actions=CONTROLLER:65535

mininet> sh ovs-ofctl dump-flows s3 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000016, duration=29.202s, table=0, n_packets=3, n_bytes=294, priority=2,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=output:1
 cookie=0x3a0000000000000f, duration=29.261s, table=0, n_packets=3, n_bytes=294, priority=6,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:2

mininet> sh ovs-ofctl dump-flows s4 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000015, duration=33.513s, table=0, n_packets=3, n_bytes=294, priority=2,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=output:5
 cookie=0x3a00000000000010, duration=33.556s, table=0, n_packets=3, n_bytes=294, priority=6,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:1
 cookie=0x3a0000000000000d, duration=33.604s, table=0, n_packets=0, n_bytes=0, priority=4,ip,nw_src=10.0.0.1 actions=CONTROLLER:65535
 cookie=0x3a00000000000008, duration=33.677s, table=0, n_packets=0, n_bytes=0, priority=3,tcp,nw_src=10.0.0.2,nw_dst=10.0.0.1,tp_src=80 actions=CONTROLLER:65535

Does the flow rules match what you think? A minimal requirement is that the network should have set up flow rules to handle h1 ping h2 properly. Look at switch s1. The rule matches the ping traffic is the second rule, and it indeed sends all ping traffic along the low path. An issue of the rule is that it will also send all HTTP traffic to the low path. To handle such issues, Maple automatically inserts a barrier rule (the last rule) so that s1 queries the controller should such such a packet arrives in the future.

Now, let's try an HTTP session:

mininet> h2 python -m SimpleHTTPServer 80 &
mininet> h1 wget -O - 10.0.0.2

Which route does HTTP take? Display the flow tables at switches again, and you should see:

mininet> sh ovs-ofctl dump-flows s1 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a0000000000002e, duration=8.828s, table=0, n_packets=0, n_bytes=0, priority=7,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:4
 cookie=0x3a00000000000017, duration=83.943s, table=0, n_packets=3, n_bytes=294, priority=2,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=output:1
 cookie=0x3a00000000000033, duration=8.772s, table=0, n_packets=14, n_bytes=4588, priority=4,tcp,nw_src=10.0.0.2,nw_dst=10.0.0.1,tp_src=80 actions=output:1
 cookie=0x3a0000000000003d, duration=7.692s, table=0, n_packets=12, n_bytes=898, priority=9,tcp,nw_src=10.0.0.1,nw_dst=10.0.0.2,tp_dst=80 actions=output:3

mininet> sh ovs-ofctl dump-flows s2 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000032, duration=15.806s, table=0, n_packets=14, n_bytes=4588, priority=4,tcp,nw_src=10.0.0.2,nw_dst=10.0.0.1,tp_src=80 actions=output:1
 cookie=0x3a0000000000003e, duration=14.707s, table=0, n_packets=12, n_bytes=898, priority=9,tcp,nw_src=10.0.0.1,nw_dst=10.0.0.2,tp_dst=80 actions=output:2

mininet> sh ovs-ofctl dump-flows s4 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000022, duration=19.698s, table=0, n_packets=0, n_bytes=0, priority=5,ip,nw_src=10.0.0.1 actions=CONTROLLER:65535
 cookie=0x3a00000000000030, duration=19.569s, table=0, n_packets=0, n_bytes=0, priority=7,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:1
 cookie=0x3a00000000000015, duration=94.721s, table=0, n_packets=3, n_bytes=294, priority=2,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=output:5
 cookie=0x3a00000000000031, duration=19.554s, table=0, n_packets=14, n_bytes=4588, priority=4,tcp,nw_src=10.0.0.2,nw_dst=10.0.0.1,tp_src=80 actions=output:4
 cookie=0x3a0000000000003f, duration=18.436s, table=0, n_packets=12, n_bytes=898, priority=9,tcp,nw_src=10.0.0.1,nw_dst=10.0.0.2,tp_dst=80 actions=output:1

From the flow tables we can see that HTTP traffic from h1 to h2 indeed uses the high path: (h1 -> s1 -> s2 -> s4 -> h2). We can also see that Maple has automatically installed all correct flow rules to handle any IP packets between h1 and h2, after seeing just two flows.

For those who are curious, which of course include everyone, let's see a bit how Maple generates those flow rules automatically.

Conceptual Intro: Maple magic using trace tree

We will go over this part of the slides as a group.

Display M1 trace tree

With the concept in mind, let's see the trace tree of M1 using our IDE: Tracetree.png


The trace tree of M1 after the preceding steps looks like:

TODO: a snapshot

M1 Completion

M1 generates a hardcoded path between h1 and h2. Now let's see what will happen when packets not between h1 and h2 enter the network. Please take a look at the program and think what it indicates.

Now let h3 ping h2:

mininet> h3 ping h2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
^C
--- 10.0.0.2 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss, time 4031ms

Of course, the packets cannot pass through the network. But Maple can do more than just blocking the packets from h3. Display the flow table at s1:

mininet> sh ovs-ofctl dump-flows s1 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000047, duration=7.403s, table=0, n_packets=0, n_bytes=0, priority=1,ip,nw_src=10.0.0.2 actions=CONTROLLER:65535
 cookie=0x3a00000000000059, duration=7.273s, table=0, n_packets=0, n_bytes=0, priority=8,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:4
 cookie=0x3a00000000000058, duration=7.28s, table=0, n_packets=0, n_bytes=0, priority=3,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=output:1
 cookie=0x3a00000000000051, duration=7.33s, table=0, n_packets=0, n_bytes=0, priority=5,tcp,nw_src=10.0.0.2,nw_dst=10.0.0.1,tp_src=80 actions=output:1
 cookie=0x3a00000000000041, duration=7.446s, table=0, n_packets=5, n_bytes=490, priority=0,ip actions=drop
 cookie=0x3a0000000000005e, duration=7.237s, table=0, n_packets=0, n_bytes=0, priority=10,tcp,nw_src=10.0.0.1,nw_dst=10.0.0.2,tp_dst=80 actions=output:3

You should see that instead of generating a rule matching only h3, Maple generates a wildcard rule to drop all IP packets. And the lowest priority of that rule guarantees correctness.

Exercise M2: Generic shortest path for HTTP

Objective: Understand basic IO + state access of Maple

A real program can compute a route for an arbitrary source-destination pair on a generic topology (collected by LLDP, or other protocols). This exercise shows you how to do this.

Revise M1

You will apply a generic shortest-path algorithm to compute the route between any source-destination pair. No worry, we will not ask you to write such a method on spot, which you should know if you are planning to interview for a job. In this tutorial, you can use the following method provided by Maple: it takes a source (srcPort) and a destination (dstPort) and return a path computed from a network topology (specified as a set of links):

MapleUtil.shortestPath(List<Link> links, Port srcPort, Port dstPort);

Now, invoke the shortestPath library call, by modifying M1 to M2 https://github.com/snlab/fastmaple16/blob/master/M2.java. In particular, the sample code has refactored example in M1 so that it first detects the hardcoded (h1, h2), and then handles general pairs.

Specifically, first, add the following imports:

import java.util.Map;

import org.opendaylight.maple.core.increment.app.MapleAppBase;
import org.opendaylight.maple.core.increment.app.MapleUtil;
import org.opendaylight.maple.core.increment.packet.Ethernet;
import org.opendaylight.maple.core.increment.packet.IPv4;
import org.opendaylight.maple.core.increment.tracetree.MaplePacket;
import org.opendaylight.maple.core.increment.tracetree.Port;
import org.opendaylight.maple.core.increment.tracetree.Route;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;

Then, make sure the variables are defined:

private static final String TOPO_URL       = "/root/network-topology/topology";
private static final String HOST_TABLE_URL = "/root/host-table";

private static final String H1     = "10.0.0.1";
private static final int    H1_IP  = IPv4.toIPv4Address(H1);

private static final String H2     = "10.0.0.2";
private static final int    H2_IP  = IPv4.toIPv4Address(H2);

private static final int HTTP_PORT = 80;

private static final String[] H12_HIGH_PATH = { "openflow:1:3", "openflow:2:2", "openflow:4:1" };
private static final String[] H12_LOW_PATH  = { "openflow:1:4", "openflow:3:2", "openflow:4:1" };
private static final String[] H21_HIGH_PATH = { "openflow:4:4", "openflow:2:1", "openflow:1:1" };
private static final String[] H21_LOW_PATH  = { "openflow:4:5", "openflow:3:1", "openflow:1:1" };

And move the code in M1 into a method to allow easy reading:

	public void staticRoute(MaplePacket pkt) {
		// H1 (client) -> H2 (server)
		if ( pkt.IPv4SrcIs(H1_IP) && pkt.IPv4DstIs(H2_IP) ) {

			String[] path = null;

			if ( ! pkt.TCPDstPortIs(HTTP_PORT) ) {  // All non HTTP IP, e.g., UDP, PING, SSH
				path = H12_LOW_PATH; 
			} else {                                // Only HTTP traffic
				path = H12_HIGH_PATH;
			}

			pkt.setRoute(path);

			// Reverse: H2 -> H1
		} else if ( pkt.IPv4SrcIs(H2_IP) && pkt.IPv4DstIs(H1_IP) ) {

				String[] path = null;

				if ( ! pkt.TCPSrcPortIs(HTTP_PORT) ) {
					path = H21_LOW_PATH;
				} else {
					path = H21_HIGH_PATH;
				}

				pkt.setRoute(path);

		}
	} // end of staticRoute

Finally, we change the content of onPacket function as the following:

		int ethType = pkt.ethType();

		// For IPv4 traffic only
		if ( ethType == Ethernet.TYPE_IPv4 ) {
			staticRoute( pkt );
			
			if (pkt.route() == null) {

				// Handle only HTTP traffic
				if (pkt.TCPDstPortIs(HTTP_PORT) || pkt.TCPSrcPortIs(HTTP_PORT)) {

					int srcIP = pkt.IPv4Src();
					int dstIP = pkt.IPv4Dst();
					
					Topology topo = (Topology) readData(TOPO_URL);
					Map<Integer, Port> hostTable = (Map<Integer, Port>) readData(HOST_TABLE_URL);

					Port srcPort = hostTable.get(srcIP);
					Port dstPort = hostTable.get(dstIP);
					
					pkt.setRoute(MapleUtil.shortestPath(topo.getLink(), srcPort, dstPort));

				} else {

					pkt.setRoute(Route.DROP);

				}
			}

		} // end of IPv4 packets        

		else {                  // Other type of traffic handled by another Maple App

			passToNext(pkt);

		}

Before deploying a new Maple project

You have now written up M2, but before deploying a new Maple project, you need to clean up the environment. Follow the instructions below:

1. Exit the running mininet from the mininet terminal.

mininet> exit

2. Logout the running OpenDaylight controller and restart it from the controller terminal.

Move all scripts at the same location First logout,

opendaylight-user@root> logout

Start a new IDE terminal, and type the following command in the terminal:

 vagrant@vagrant: start_maple_controller

3. Restart the mininet, by typing the following command at the mininet terminal:

cd /home/vagrant/Maple_Topo_Scripts
start_maple_mininet

Deploy and see

Go through the steps similar to Exercise M1. First click Deploy on the IDE. Next test HTTP request between the pair hardcoded in M1:

mininet> h2 python -m SimpleHTTPServer 80 &
mininet> h1 wget -O - 10.0.0.2

They should still work:

mininet> sh ovs-ofctl dump-flows s1 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000012, duration=5.447s, table=0, n_packets=11, n_bytes=4338, priority=3,tcp,nw_src=10.0.0.2,nw_dst=10.0.0.1,tp_src=80 actions=output:1
 cookie=0x3a0000000000000d, duration=5.504s, table=0, n_packets=10, n_bytes=766, priority=7,tcp,nw_src=10.0.0.1,nw_dst=10.0.0.2,tp_dst=80 actions=output:3

mininet> sh ovs-ofctl dump-flows s2 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000011, duration=12.264s, table=0, n_packets=11, n_bytes=4338, priority=3,tcp,nw_src=10.0.0.2,nw_dst=10.0.0.1,tp_src=80 actions=output:1
 cookie=0x3a0000000000000e, duration=12.297s, table=0, n_packets=10, n_bytes=766, priority=7,tcp,nw_src=10.0.0.1,nw_dst=10.0.0.2,tp_dst=80 actions=output:2

mininet> sh ovs-ofctl dump-flows s4 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000013, duration=17.804s, table=0, n_packets=0, n_bytes=0, priority=4,ip,nw_src=10.0.0.1 actions=CONTROLLER:65535
 cookie=0x3a00000000000010, duration=17.829s, table=0, n_packets=11, n_bytes=4338, priority=3,tcp,nw_src=10.0.0.2,nw_dst=10.0.0.1,tp_src=80 actions=output:4
 cookie=0x3a0000000000000f, duration=17.837s, table=0, n_packets=10, n_bytes=766, priority=7,tcp,nw_src=10.0.0.1,nw_dst=10.0.0.2,tp_dst=80 actions=output:1

Now, test the pair failed in the M1 Limitation step. If it is ping, it should still not work:

mininet> h3 ping h2

But how about HTTP,

mininet> h2 python -m SimpleHTTPServer 80 &
mininet> h3 wget -O - 10.0.0.2

Let's see the trace tree. Use the IDE to display the trace tree. You should see:

Now ping a different pair, and the trace tree should grow, leading to growth of the flow table as well:

mininet> sh ovs-ofctl dump-flows s1 -O OpenFlow13
mininet> sh ovs-ofctl dump-flows s1 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000017, duration=62.749s, table=0, n_packets=1, n_bytes=74, priority=2,tcp,tp_dst=80 actions=CONTROLLER:65535
 cookie=0x3a00000000000039, duration=6.343s, table=0, n_packets=0, n_bytes=0, priority=4,ip,nw_src=10.0.0.2 actions=CONTROLLER:65535
 cookie=0x3a00000000000053, duration=5.375s, table=0, n_packets=0, n_bytes=0, priority=10,tcp,nw_src=10.0.0.2,nw_dst=10.0.0.1,tp_src=80 actions=output:1
 cookie=0x3a00000000000049, duration=5.446s, table=0, n_packets=11, n_bytes=4338, priority=6,tcp,nw_src=10.0.0.2,nw_dst=10.0.0.3,tp_src=80 actions=output:2
 cookie=0x3a0000000000001b, duration=62.701s, table=0, n_packets=4, n_bytes=392, priority=0,ip actions=drop
 cookie=0x3a00000000000018, duration=62.74s, table=0, n_packets=0, n_bytes=0, priority=1,tcp,tp_src=80 actions=CONTROLLER:65535
 cookie=0x3a0000000000004d, duration=5.417s, table=0, n_packets=0, n_bytes=0, priority=14,tcp,nw_src=10.0.0.1,nw_dst=10.0.0.2,tp_dst=80 actions=output:3
 cookie=0x3a0000000000003a, duration=6.335s, table=0, n_packets=11, n_bytes=840, priority=3,tcp,nw_src=10.0.0.3,nw_dst=10.0.0.2,tp_dst=80 actions=output:4

mininet> sh ovs-ofctl dump-flows s2 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000052, duration=27.955s, table=0, n_packets=0, n_bytes=0, priority=10,tcp,nw_src=10.0.0.2,nw_dst=10.0.0.1,tp_src=80 actions=output:1
 cookie=0x3a00000000000048, duration=28.027s, table=0, n_packets=11, n_bytes=4338, priority=6,tcp,nw_src=10.0.0.2,nw_dst=10.0.0.3,tp_src=80 actions=output:1
 cookie=0x3a0000000000004e, duration=27.986s, table=0, n_packets=0, n_bytes=0, priority=14,tcp,nw_src=10.0.0.1,nw_dst=10.0.0.2,tp_dst=80 actions=output:2

mininet> sh ovs-ofctl dump-flows s3 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a0000000000003b, duration=45.256s, table=0, n_packets=11, n_bytes=840, priority=3,tcp,nw_src=10.0.0.3,nw_dst=10.0.0.2,tp_dst=80 actions=output:2

mininet> sh ovs-ofctl dump-flows s4 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000050, duration=48.528s, table=0, n_packets=0, n_bytes=0, priority=11,ip,nw_src=10.0.0.1 actions=CONTROLLER:65535
 cookie=0x3a00000000000042, duration=48.63s, table=0, n_packets=0, n_bytes=0, priority=8,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=CONTROLLER:65535
 cookie=0x3a00000000000051, duration=48.519s, table=0, n_packets=0, n_bytes=0, priority=10,tcp,nw_src=10.0.0.2,nw_dst=10.0.0.1,tp_src=80 actions=output:4
 cookie=0x3a00000000000047, duration=48.595s, table=0, n_packets=11, n_bytes=4338, priority=6,tcp,nw_src=10.0.0.2,nw_dst=10.0.0.3,tp_src=80 actions=output:4
 cookie=0x3a00000000000046, duration=48.604s, table=0, n_packets=0, n_bytes=0, priority=7,tcp,nw_src=10.0.0.2,tp_dst=80 actions=CONTROLLER:65535
 cookie=0x3a0000000000004f, duration=48.534s, table=0, n_packets=0, n_bytes=0, priority=14,tcp,nw_src=10.0.0.1,nw_dst=10.0.0.2,tp_dst=80 actions=output:1
 cookie=0x3a0000000000003c, duration=49.448s, table=0, n_packets=11, n_bytes=840, priority=3,tcp,nw_src=10.0.0.3,nw_dst=10.0.0.2,tp_dst=80 actions=output:1

From the flow tables above, we can see the path h3 -> h2 and h2 -> h3 use different paths.

Exercise M3: Load balance routing

Objective: basic IO + state access + complete instruction

Design M3

M3 implements simple load balancing, by conducting the following:

  • Forward the traffic to different servers sharing the same Virtual IP based on the src IP of packet
  • Rewrite packet’s dst IP/MAC for different servers

In particular, please type the code as: https://github.com/snlab/fastmaple16/blob/master/M3.java

Deploy and see

First restart controller:

start_maple_controller

And then let's deploy the Maple project with M3.

Restart mininet:

cd /home/vagrant/Maple_Topo_Scripts/
sudo mn --controller remote,127.0.0.1 --topo mytopo --custom ./exampletopo.py --switch ovsk,protocols=OpenFlow13 --mac

Since we need multiple hosts, start several host terminals from the mininet terminal:

mininet> xterm h4 h6 h1 h3

Start two iperf servers at h4 and h6.

h4> /home/vagrant/Maple_Topo_Scripts/start_server_tcp.sh
h6> /home/vagrant/Maple_Topo_Scripts/start_server_tcp.sh

Now let h1 connect to the severs serving the virtual IP address, 10.0.0.7

h1> /home/vagrant/Maple_Topo_Scripts/start_client_tcp.sh 10.0.0.7

You should be able to see the following result from the h4 terminal:

------------------------------------------------------------
Server listening on TCP port 5550
TCP window size: 85.3 KByte (default)
------------------------------------------------------------
[ 26] local 10.0.0.4 port 5550 connected with 10.0.0.1 port 39329
[ ID] Interval       Transfer     Bandwidth
[ 26]  0.0- 1.0 sec  1.50 GBytes  12.9 Gbits/sec
[ 26]  1.0- 2.0 sec  1.91 GBytes  16.4 Gbits/sec
[ 26]  2.0- 3.0 sec  1.90 GBytes  16.4 Gbits/sec
[ 26]  3.0- 4.0 sec  1.92 GBytes  16.5 Gbits/sec
[ 26]  4.0- 5.0 sec  1.95 GBytes  16.7 Gbits/sec
[ 26]  5.0- 6.0 sec  1.94 GBytes  16.7 Gbits/sec
[ 26]  6.0- 7.0 sec  1.90 GBytes  16.3 Gbits/sec
[ 26]  7.0- 8.0 sec  1.97 GBytes  16.9 Gbits/sec
[ 26]  8.0- 9.0 sec  1.91 GBytes  16.4 Gbits/sec
[ 26]  9.0-10.0 sec  1.93 GBytes  16.6 Gbits/sec
[ 26]  0.0-10.0 sec  18.9 GBytes  16.2 Gbits/sec

Display the flow tables:

mininet> sh ovs-ofctl dump-flows s1 -O OpenFlow13
NXST_FLOW reply (xid=0x4):
 cookie=0x2a00000000000004, duration=102.039s, table=0, n_packets=148138, n_bytes=9777452, idle_age=91, priority=13,ip,nw_src=10.0.0.4,nw_dst=10.0.0.1 actions=mod_dl_src:00:00:00:00:00:07,mod_nw_src:10.0.0.7,output:1
 cookie=0x3a0000000000000f, duration=102.036s, table=0, n_packets=748371, n_bytes=19184349022, idle_age=91, priority=16,ip,nw_src=10.0.0.1,nw_dst=10.0.0.7 actions=output:4

mininet> sh ovs-ofctl dump-flows s2 -O OpenFlow13
NXST_FLOW reply (xid=0x4):
 cookie=0x3a00000000000003, duration=109.767s, table=0, n_packets=0, n_bytes=0, idle_age=109, priority=11,ip actions=CONTROLLER:65535
 cookie=0x3a0000000000000e, duration=108.631s, table=0, n_packets=148138, n_bytes=9777452, idle_age=97, priority=13,ip,nw_src=10.0.0.4,nw_dst=10.0.0.1 actions=output:1

mininet> sh ovs-ofctl dump-flows s3 -O OpenFlow13
NXST_FLOW reply (xid=0x4):
 cookie=0x3a00000000000010, duration=115.535s, table=0, n_packets=748371, n_bytes=19184349022, idle_age=104, priority=16,ip,nw_src=10.0.0.1,nw_dst=10.0.0.7 actions=output:2

mininet> sh ovs-ofctl dump-flows s4 -O OpenFlow13
NXST_FLOW reply (xid=0x4):
 cookie=0x3a00000000000011, duration=121.822s, table=0, n_packets=0, n_bytes=0, idle_age=121, priority=14,ip,nw_dst=10.0.0.7 actions=CONTROLLER:65535
 cookie=0x3a0000000000000d, duration=121.87s, table=0, n_packets=148138, n_bytes=9777452, idle_age=110, priority=13,ip,nw_src=10.0.0.4,nw_dst=10.0.0.1 actions=output:4
 cookie=0x2a00000000000005, duration=121.827s, table=0, n_packets=748371, n_bytes=19184349022, idle_age=110, priority=16,ip,nw_src=10.0.0.1,nw_dst=10.0.0.7 actions=mod_nw_dst:10.0.0.4,mod_dl_dst:00:00:00:00:00:04,output:2

You can see that Maple generates several rules containing modification actions, such as the first rule at s1 and the last rule at s4.

Next, let h3 connect to the virtual server:

h3> /home/vagrant/Maple_Topo_Scripts/start_client_tcp.sh 10.0.0.7

Please see the result from the h6 terminal:

------------------------------------------------------------
Server listening on TCP port 5550
TCP window size: 85.3 KByte (default)
------------------------------------------------------------
[ 26] local 10.0.0.5 port 5550 connected with 10.0.0.3 port 37376
[ ID] Interval       Transfer     Bandwidth
[ 26]  0.0- 1.0 sec  1.48 GBytes  12.7 Gbits/sec
[ 26]  1.0- 2.0 sec  1.88 GBytes  16.1 Gbits/sec
[ 26]  2.0- 3.0 sec  1.92 GBytes  16.5 Gbits/sec
[ 26]  3.0- 4.0 sec  1.91 GBytes  16.4 Gbits/sec
[ 26]  4.0- 5.0 sec  1.88 GBytes  16.2 Gbits/sec
[ 26]  5.0- 6.0 sec  1.96 GBytes  16.9 Gbits/sec
[ 26]  6.0- 7.0 sec  1.93 GBytes  16.6 Gbits/sec
[ 26]  7.0- 8.0 sec  1.94 GBytes  16.7 Gbits/sec
[ 26]  8.0- 9.0 sec  1.96 GBytes  16.9 Gbits/sec
[ 26]  9.0-10.0 sec  1.90 GBytes  16.3 Gbits/sec
[ 26]  0.0-10.0 sec  18.8 GBytes  16.1 Gbits/sec

The result shows that although h1 and h3 connect to the same IP address, their traffic is split to two different servers. We have h1 -> h4, h3 -> h6, which is load balancing. You can imagine that it is quite easy to modify the code to implement more advanced load balancing algorithms.

Exercise M4: Maple App composition

Objective: basic IO + state access + compound action + Maple App composition

Conceptual Intro: Maple App composition

We will go over this part of the slides together as a group.

Write the code

Users can write multiple Maple apps and construct them as an app chain which indicates the order of packet procession. Now let's see how to construct the chain. First, we write another Maple app. This is a simple access control program which specifies which IP src can go through the network.

1. To do this, add the code from M4 (https://github.com/snlab/fastmaple16/blob/master/M4.java) into a new file, called M4.java, in the same directory as FASTMapleODL16.java.

2. Now, for clarity, let's rename FASTMapleODL16 to M3. To do this, we first change the filename to M3.java, and then change the class name as well.

3. Finally, we modify the config file (./src/main/config/default-config.xml) to specify the chain of Maple apps to run:

<chain>org.opendaylight.mapleapp.impl.M4,org.opendaylight.mapleapp.impl.M3,org.opendaylight.mapleapp.impl.DefaultMapleApp</chain>

Now, every packet will first pass to M4 and then M3 (the load balance code) and finally to a default maple app which will handle arp packets. Of course, if you want to handle arp packets yourself, you can remove this value.

Deploy and see

Exit the mininet and restart the maple controller just like you did in M2 and M3. And then re-deploy your Maple project.

Now, let's test our Maple App.

First make h4 and h6 as the servers by the following command:

h4> /home/vagrant/Maple_Topo_Scripts/start_server_tcp.sh
h6> /home/vagrant/Maple_Topo_Scripts/start_server_tcp.sh

Then run this command at h1:

h1> /home/vagrant/Maple_Topo_Scripts/start_client_tcp.sh 10.0.0.7

It can connect to the server (h4):

Client connecting to 10.0.0.7, TCP port 5550
TCP window size: 85.3 KByte (default)
------------------------------------------------------------
[ 25] local 10.0.0.1 port 41323 connected with 10.0.0.7 port 5550
[ ID] Interval       Transfer     Bandwidth
[ 25]  0.0- 1.0 sec  2.20 GBytes  18.9 Gbits/sec
[ 25]  1.0- 2.0 sec  2.02 GBytes  17.4 Gbits/sec
[ 25]  2.0- 3.0 sec  1.71 GBytes  14.7 Gbits/sec
[ 25]  3.0- 4.0 sec  1.67 GBytes  14.4 Gbits/sec
[ 25]  4.0- 5.0 sec  1.54 GBytes  13.2 Gbits/sec
[ 25]  5.0- 6.0 sec  1005 MBytes  8.43 Gbits/sec
[ 25]  6.0- 7.0 sec  1.10 GBytes  9.49 Gbits/sec
[ 25]  7.0- 8.0 sec  1.03 GBytes  8.81 Gbits/sec
[ 25]  8.0- 9.0 sec  1.12 GBytes  9.66 Gbits/sec
[ 25]  9.0-10.0 sec  1.18 GBytes  10.1 Gbits/sec
[ 25]  0.0-10.0 sec  14.6 GBytes  12.5 Gbits/sec

However, if you use h3 to access the virtual IP, a connection cannot be established because M3 blocks packets from h3.

h3> /home/vagrant/Maple_Topo_Scripts//start_client_tcp.sh 10.0.0.7

Some basic questions on Maple

We have shown many features of Maple. At this point, you may have some basic questions on Maple programming, in particular, the following two:

* If a program/state changes, how can Maple maintain consistency w/ state (e.g., how can Maple recompute routes after failure of a link on a used path)?
* What if you want proactive rule installation, not reactive?

Now we transition to the next part of the tutorial, to show that the preceding issues can be systematically solved by another programming abstraction, the FAST function store.

Conceptual Intro: Why and what is FAST?

We will go over this part of the slides together as a team. We will cover the basic ideas of FAST: why function store, automatic data access tracking, automatic re-execution scheduling.

Exercise F1: FAST host-to-host intent

To demonstrate how powerful FAST is, but also how simple it is to use FAST, we will use FAST to maintain static routes for a given host-to-host pair whenever possible. This is called an Intent in SDN programming.

Write the code

First, create a FAST function to compute the shortest path for a source-destination pair of hosts. To do that, choose the PathIntentImp.java from the IDE source panel, and modify the run() method to the following:

public void run() {
        LOG.info("PathIntentImpl.run()");

	//set the match field to be source IP and destination IP
        Match match = new Match();
        match.fields.put(Match.Field.IPv4_SRC, Integer.toString(IPv4.toIPv4Address(this.srcIp)));
        match.fields.put(Match.Field.IPv4_DST, Integer.toString(IPv4.toIPv4Address(this.dstIp)));

	//read the topology
        Topology topo = null;
        topo = this.utils.readTopology();
        if (topo == null) {
            LOG.error("Fail to get topology.");
            return;
        }

        //get the hosts from the host table
        String srcId = this.utils.getHost(this.srcIp);
        String dstId = this.utils.getHost(this.dstIp);

        //a path can be computed only when both hosts are in the host table
        if (srcId != null && dstId != null) {
            this.utils.installPath(utils.shortestPath(srcId, dstId, topo),
                    match, this.DEFAULT_PRIORITY);
        }
        else {
            LOG.info("Hosts are not in host table yet");
        }
 }

Now, we need to create FAST function instances. There are quite a few ways to achieve the goal. One is that FAST provides an RPC to allow remote submission of FAST instances. In this exercise, we use the FastTutorialMain initialization to create them.

Select the FastTutorialMain.java from the IDE source panel. Define a set of constants at the beginning of FastTutorialMain.java:

    private static final String H1 = "10.0.0.1";
    private static final String H2 = "10.0.0.2";
    private List<String[]> srcDstPairs = new LinkedList<>();

Then modify the onCreate method to become the following:

public void onCreate() {
        LOG.info("IntentMain.onCreate()");

        srcDstPairs.add(new String[] {H1, H2});

        //iterate through all source-destination pairs
        for (String[] pair : srcDstPairs) {
            String srcIp= pair[0];
            String dstIp = pair[1];

            //create two FAST function instances for computing the paths of both directions
            PathIntentImp srcDstPathFunction = new PathIntentImp(srcIp, dstIp);
            PathIntentImp dstToSrcPathFunction = new PathIntentImp(dstIp, srcIp);

            //create a FAST group
            String gid = fast.createGroup();
            UserHints hints = new UserHints(gid);

            //submit the two FAST functions instances as a group
            System.out.println("Submitting intent functions.");
            fast.submit(srcDstPathFunction, hints);
            fast.submit(dstToSrcPathFunction, hints);
        }
    }

Build and Deploy

It is slightly complex to demonstrate the effects of FAST. Hence, we use the following steps.

First, deploy F1 using IDE. This is easy. [TODO: steps]

Now, let's use the IDE to see if the instances are created. First, let's see if they are there [TODO]. You should see an output like: [TODO]

FAST supports the concept of group, and you can see the grouping using the Instance Relation Display: [TODO: how]. You should see a following display:

A magic provided by FAST is automatic tracking of data access. Let's see what data have been read by your instances. [TODO: how]. You should see:

Now let's ping the source-destination host pair (h1, h2) predefined in FastTutorialMain.

mininet> h1 ping h2

And we can see:

PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
^C
--- 10.0.0.2 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 2998ms

When we dump the flows from each switch, we see that:

mininet> sh ovs-ofctl dump-flows s1 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
mininet> sh ovs-ofctl dump-flows s2 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
mininet> sh ovs-ofctl dump-flows s3 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
mininet> sh ovs-ofctl dump-flows s4 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):

No flow rule is inserted into the switches. This is because the hostTable does not have h1 and h2.

Trigger automatic re-execution

From the IDE we can see that the submitted FAST function instances read both the host table and the network topology. Now let's see how a change in these two data can trigger the automatic re-execution of instances. We first insert h1 and h2 into the host table. Then we let h1 ping h2 again and this time we can see:

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=0.234 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.040 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.047 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.043 ms
64 bytes from 10.0.0.2: icmp_seq=5 ttl=64 time=0.048 ms
64 bytes from 10.0.0.2: icmp_seq=6 ttl=64 time=0.087 ms
^C
--- 10.0.0.2 ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 4998ms
rtt min/avg/max/mdev = 0.040/0.083/0.234/0.069 ms

On all the switches, we can also see that corresponding flow rules are installed:

mininet> sh ovs-ofctl dump-flows s1 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000000, duration=35.293s, table=0, n_packets=6, n_bytes=588, priority=20,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:3
 cookie=0x3a00000000000002, duration=35.142s, table=0, n_packets=6, n_bytes=588, priority=20,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=output:1

mininet> sh ovs-ofctl dump-flows s2 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000001, duration=39.149s, table=0, n_packets=6, n_bytes=588, priority=20,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:2

mininet> sh ovs-ofctl dump-flows s3 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000001, duration=41.661s, table=0, n_packets=6, n_bytes=588, priority=20,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=output:1

mininet> sh ovs-ofctl dump-flows s4 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000002, duration=44.773s, table=0, n_packets=6, n_bytes=588, priority=20,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:1
 cookie=0x3a00000000000000, duration=44.773s, table=0, n_packets=6, n_bytes=588, priority=20,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=output:5

This shows that when entries are inserted into the host table, FAST will capture this data change and automatically re-execute both PathIntentImpl instances to get the shortest paths between h1 and h2. In this example, the paths are h1->s1->s2->s4->h2, and h2->s4->s3->s1->h1, respectively.

Next let’s see what would happen when we bring down a link in the topology. For example, we break the link between s1 and s2:

mininet> link s1 s2 down

From the previous flow tables, we can see that the link (s1, s2) are used to forward packets from h1 to h2. So this broken link should have forbidden h1 from successfully pinging h2. But when we try to do the same ping again

mininet> h1 ping h2

We see that

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=0.228 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.085 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.059 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.057 ms
64 bytes from 10.0.0.2: icmp_seq=5 ttl=64 time=0.042 ms
^C
--- 10.0.0.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4003ms
rtt min/avg/max/mdev = 0.042/0.094/0.228/0.068 ms

Apparently the ping still succeeds. This is because FAST would respond to this data change event by rollbacking the installed shortest paths, recomputing the new shortest paths and reinstall the flow rules in to the switches. To verify that, we dump the flow rules on each switch:

mininet> sh ovs-ofctl dump-flows s1 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000000, duration=26.38s, table=0, n_packets=5, n_bytes=490, priority=20,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:4
 cookie=0x3a00000000000002, duration=26.38s, table=0, n_packets=5, n_bytes=490, priority=20,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=output:1

mininet> sh ovs-ofctl dump-flows s2 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):

mininet> sh ovs-ofctl dump-flows s3 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000001, duration=35.26s, table=0, n_packets=5, n_bytes=490, priority=20,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=output:1
 cookie=0x3a00000000000001, duration=35.26s, table=0, n_packets=5, n_bytes=490, priority=20,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:2

mininet> sh ovs-ofctl dump-flows s4 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000002, duration=37.597s, table=0, n_packets=5, n_bytes=490, priority=20,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:1
 cookie=0x3a00000000000000, duration=37.597s, table=0, n_packets=5, n_bytes=490, priority=20,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=output:5

We see that the flow rule which used to be in s2 was deleted, and s3 has one more rule to forward packets from h1 to h2. Now h1->s1->s3->s4->h2 is the new shortest path from h1 to h2.

Similarly, when we bring back the link (s1, s2), this data change event will also trigger automatic re-execution of PathIntentImp instances.

mininet> link s1 s2 up

We can observe the re-execution by dumping the flows in switches and see that

mininet> sh ovs-ofctl dump-flows s1 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000000, duration=10.918s, table=0, n_packets=0, n_bytes=0, priority=20,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:3
 cookie=0x3a00000000000002, duration=10.918s, table=0, n_packets=0, n_bytes=0, priority=20,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=output:1

mininet> sh ovs-ofctl dump-flows s2 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000001, duration=14.286s, table=0, n_packets=0, n_bytes=0, priority=20,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:2

mininet> sh ovs-ofctl dump-flows s3 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000001, duration=16.839s, table=0, n_packets=0, n_bytes=0, priority=20,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=output:1

mininet> sh ovs-ofctl dump-flows s4 -O OpenFlow13
OFPST_FLOW reply (OF1.3) (xid=0x2):
 cookie=0x3a00000000000000, duration=19.621s, table=0, n_packets=0, n_bytes=0, priority=20,ip,nw_src=10.0.0.2,nw_dst=10.0.0.1 actions=output:5
 cookie=0x3a00000000000002, duration=19.621s, table=0, n_packets=0, n_bytes=0, priority=20,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:1

Summary

Summary of what we learned

  • Maple: datapath oblivious programming (e.g., no worry about generating barrier rules, priorities)
  • FAST: data-driven, modular programming (e.g., no worry about data tracking, rollback)