Programming Assignment 2

CS640 Fall 2014

Due: Tuesday, October 28 at 11:59pm Sunday, November 2 at 11:59pm

Overview

For this assignment, you will implement both the control and data planes of a simple IP router. Your control plane will implement a distance vector routing protocol (RIP) to build a forwarding table. Your data plane will forward packets based on entries in the forwarding table, and, when necessary, generate ICMP messages to notify packet senders of forwarding problems.

Part 1: Getting Started

Part 2: Code Overview

Part 3: Implement Router Data Plane

Part 4: Implement Router Control Plane
Submission Instructions

Clarifications


Part 1: Getting Started

You will be using Mininet, POX, and skeleton code for a simple router to complete the project. Mininet and POX are already installed in the virtual machine (VM) you used for Project 1. You should continue to use this VM for this project. You can always refer back to Part 3 of Project 1 if you have questions about using your VM.

Preparing Your Environment

Before beginning this project, there are some additional steps you need to complete to prepare your VM:

  1. Install required packages

sudo apt-get update

sudo apt-get install -y python-dev vim-nox python-setuptools flex bison traceroute ant openjdk-7-jdk git

  1. Install ltprotocol

cd ~
git clone git://github.com/dound/ltprotocol.git
cd ltprotocol
sudo python setup.py install

  1. Checkout the appropriate version of POX

cd ~/pox
git checkout f95dd1a81584d716823bbf565fa68254416af603

  1. Download the starter code from: http://cs.wisc.edu/~akella/CS640/F14/assign2/code.tgz

cd ~
wget http://cs.wisc.edu/~akella/CS640/F14/assign2
/code.tgz
tar xzvf code.tgz

  1. Symlink POX and configure the POX modules

cd ~/project2
ln -s ../pox
./config.sh

Sample Configuration

The first sample configuration consists of a single router (r1) and three emulated hosts (h1, h2, and h3). The hosts are each running an HTTP server. When you have finished implementing your router’s data plane, one host should be able to fetch a web page from any other host (using wget or curl). Additionally, the hosts should be able to ping and traceroute each other, as well as the router.

This topology is defined in the configuration file single.topo.

Running the Simple Router

  1. Start Mininet emulation by running the following commands:

$ cd ~/project2/

$ ./run_mininet.sh single.topo

You should be able to see some output that ends like the following:

*** Adding links:

(h1, r1) (h2, r1) (h3, r1)

*** Configuring hosts

h1 h2 h3

*** Starting controller

*** Starting 1 switches

r1

*** Configuring host h1

Setting ip for h1-eth0 to 10.0.1.101/24

Adding route 10.0.1.1/32 dev h1-eth0

Deleting route 10.0.1.0/24 dev h1-eth0

Adding default gw 10.0.1.1 dev h1-eth0

*** Starting SimpleHTTPServer on host h1

*** Configuring host h2

Setting ip for h2-eth0 to 10.0.2.102/24

Adding route 10.0.2.1/32 dev h2-eth0

Deleting route 10.0.2.0/24 dev h2-eth0

Adding default gw 10.0.2.1 dev h2-eth0

*** Starting SimpleHTTPServer on host h2

*** Configuring host h3

Setting ip for h3-eth0 to 10.0.3.103/24

Adding route 10.0.3.2/32 dev h3-eth0

Deleting route 10.0.3.0/24 dev h3-eth0

Adding default gw 10.0.3.2 dev h3-eth0

*** Starting SimpleHTTPServer on host h3

*** Starting CLI:

mininet>

Keep this terminal open, as you will need the mininet command line for debugging. Use another terminal to continue the next step. (Don’t press ctrl-z.)

  1. Start the controller, by running the following commands:

cd ~/project2/

./run_pox.sh

You should be able to see some output like the following:

POX 0.0.0 / Copyright 2011 James McCauley

DEBUG:.home.mininet.cs640.project2.pox_module.cs640.ofhandler:*** ofhandler: Successfully loaded ip settings for hosts

[…]

Ready.

POX>

You have to wait for Mininet to connect to the POX controller before you continue to the next step. Once Mininet has connected, you will see output like the following:

INFO:openflow.of_01:[Con 1/1] Connected to 00-00-00-00-00-01

DEBUG:.home.mininet.cs640.project2.pox_module.cs640.ofhandler:Connection [Con 1/1]

DEBUG:.home.mininet.cs640.project2.pox_module.cs640.ofhandler:dpid=1

DEBUG:.home.mininet.cs640.project2.pox_module.cs640.srhandler:cs640_srhandler catch RouterInfo(info={'eth3': ('10.0.3.2', '16:83:f7:de:00:f5', '10Gbps', 3), 'eth2': ('10.0.2.1', '86:39:42:d4:30:96', '10Gbps', 2), 'eth1': ('10.0.1.1', '92:23:9a:41:a2:39', '10Gbps', 1)}, rtable=[], swid=r1, dpid=1)

INFO:.home.mininet.cs640.project2.pox_module.cs640.srhandler:created server

DEBUG:.home.mininet.cs640.project2.pox_module.cs640.srhandler:SRServerListener listening on 8001

Keep POX running. Open yet another terminal to continue the next step. (Don't press ctrl-z.)

  1. Build and start the simple router, by running the following commands:

cd ~/project2/

ant

java -jar SimpleRouter.jar -p 8001 -v r1 -r rtable.r1

You should be able to see some output that ends like the following:

Loading routing table

---------------------------------------------

Destination         Gateway             Mask                Iface

10.0.1.101          0.0.0.0                  255.255.255.255 eth1

10.0.2.102          0.0.0.0                  255.255.255.255 eth2

10.0.3.103          0.0.0.0                  255.255.255.255 eth3

---------------------------------------------

Router interfaces:

eth3        HWaddr 16:83:F7:DE:00:F5

            inet addr 10.0.3.2

eth1        HWaddr 92:23:9A:41:A2:39

            inet addr 10.0.1.1

eth2        HWaddr 86:39:42:D4:30:96

            inet addr 10.0.2.1

<-- Ready to process packets -->

  1. Go back to the terminal where Mininet is running. To issue an command on the emulated host, type the hostname followed by the command in the Mininet console. For example, the following command issues 2 pings from the h1 to h2:

mininet> h1 ping -c 2 10.0.2.102

The pings will fail because the router’s data plane is not fully implemented. However, in the terminal where your simple router is running, you should see the following output:

*** -> Received packet:

            arp

            dl_vlan: untagged

            dl_vlan_pcp: 0

            dl_src: c6:cb:bb:81:8e:57

            dl_dst: ff:ff:ff:ff:ff:ff

            nw_src: 10.0.1.101

            nw_dst: 10.0.1.1

*** -> Received packet:

            arp

            dl_vlan: untagged

            dl_vlan_pcp: 0

            dl_src: c6:cb:bb:81:8e:57

            dl_dst: ff:ff:ff:ff:ff:ff

            nw_src: 10.0.1.101

            nw_dst: 10.0.1.1

This indicates packets are arriving at the router and need to be processed by the router’s data plane.

  1. You can stop your simple router by pressing ctrl-c in the terminal where it’s running. You can restart the simple router without restarting POX and mininet, but it’s often useful to restart POX and mininet to ensure the emulated network starts in a clean state.

Part 2: Code Overview

The simple router code consists of four packages:

You should only need to modify (and possibly create) classes in the edu.wisc.cs.sdn.sr package to complete this assignment.

Router.java

The Router class has the full context for the router (interfaces, route table, ARP cache, etc.) and the primary functions for receiving and sending packets.

The Router class has several important class variables:

When packets are received on any of a router’s interfaces, the handlePacket(...) function is called (by the underlying code that communicates with POX and mininet). In the code we provide, this handlePacket simply prints out the received packet, but you’ll add code to this function to either forward the packet out another interface, pass the packet to the ARP or RIP subsystems, or respond with an ICMP packet. Part 3 discusses this in more detail.

You can send a packet out one of the router’s interfaces by calling the sendPacket(...) function in the Router class.

Iface.java

There is an Iface object for each interface on the router (stored in the interfaces variable in the Router object). Each interface has a name, MAC address, and IP address.

RouteTable.java & RouteTableEntry.java

The router maintains its route table in a RouteTable object (referenced via the routeTable variable in the Router object). A RouteTable object has a list of route entries (the class variable entries).

You can request the list of entries by calling the getEntries(...) function. You can add, update, and remove entries, by calling the addEntry(...), updateEntry(...), and removeEntry(...) functions, respectively. When you are implementing the router’s data plane (Part 3 of the assignment), you will use a static routing table that is automatically read from a file, so you will not need to add, update, and remove entries. You will only need these functions when you implement the router’s control plane (Part 4 of the assignment).

A RouteTableEntry object has a destination address, gateway address, mask address, and interface name.

ARPCache.java, ARPEntry.java & ArpRequest.java

The router maintains a mapping of IP address to MAC addresses in an ARPCache object (referenced via the arpCache variable in the Router object). An ARPCache object has a list of cache entries (the class variable entries maps an IP address to an entry) and a list of pending requests to determine the MAC address corresponding to a particular IP address (the class variable requests maps an IP address to a request).

You can check if there is an entry for an IP address in the ARP cache by calling the lookup(...) function. If no entry is found, then you can call waitForArp(...) to send ARP request packets into the network to determine the MAC address for a given IP address. The packet that needs the mapping will be placed on a queue until an ARP response packet is received. Once the ARP response is received, you will need to send any packets that were waiting on the mapping by completing the handleArpPacket(...) function in the Router class. If five ARP requests are sent for a particular IP address without and no response has been received, then the router gives up on trying to resolve the packets.  An ICMP error message should be sent for any packets that were waiting on the response; you’ll need to complete the updateArpRequest(...) function in the ARPCache class to send these ICMP packets.

Note that there is already a thread (started by the constructor in the ARPCache class) that: sends a new ARP request packet every second for any pending requests, and times out entries from the ARP cache that are more than 15 seconds old.

RIP.java

If a static route table is not provided (via the -r argument when running SimpleRouter.jar), then the router’s control plane uses the Routing Information Protocol (RIP) to build a route table. The RIP class we provide contains three functions that can be used as a starting point for implementing RIP (described in more detail in Part 4):


Part 3: Implement Router Data Plane

Your router’s data plane must:

  1. Forward IP packets based on the route table
  2. Invoke the appropriate control plane code for control packets (ARP and RIP)
  3. Respond to ping packets destined for the router itself
  4. Send ICMP messages when error conditions occur (e.g., no matching route table entry, or MAC address resolution (i.e., ARP) timeouts)

Your code to provide this functionality should go in the handlePacket(...) function, and other helper functions you create, in the Router class.

Given a raw Ethernet frame, it may contain an ARP packet or an IP packet.

IP Forwarding

If the frame contains an IP packet that is not destined for one of our interfaces:

If an error occurs in any of the above steps, you will have to send an ICMP message back to the sender notifying them of an error.

Invoking Control Plane Code & Responding to Pings

If the frame contains an IP packet destined for one of your router's interfaces (i.e., destined to an IP address assigned to one of your router’s interfaces):

If the frame contains an ARP packet: call the handleArpPacket(...) function in the Router class. You will need to complete the TODO block in the handleArpPacket(...) function to send any waiting packets after an ARP request is successfully resolved (i.e., the MAC address associated with an IP address is determined).

Internet Control Message Protocol (ICMP)

You will need to properly generate the following ICMP messages in response to the sending host under the following conditions:

The source address of an ICMP message can be the source address of any of the incoming interfaces, as specified in RFC 792. As mentioned above, the only incoming ICMP message destined towards the router's IPs that you have to explicitly process are ICMP echo requests. Network Sorcery's RFC Sourcebook provides a condensed reference for ICMP.

For examples of how to construct and send packets, see the sendArpRequest(...) and sendArpReply(...) functions in the ArpCache class.


Part 4: Implement Router Control Plane

Your router’s control plane must implement version 2 of the Routing Information Protocol (RIPv2) to build, and update your, router’s route table. RFC 2453 is the latest specification for RIPv2; Network Sorcery's RFC Sourcebook provides a condensed reference for RIP. Recall that RIP implements distance vector routing. Refer to the notes from lecture 9 or your textbook to remember how how distance vector routing works.

Note that the RIP only operate when a static route table is not provided (via the -r argument when running SimpleRouter.jar). The init() function of the RIP class we provide checks if the routing table has already been populated before initializing RIP.

RIP Packets

The RIPv2 and RIPv2Entry classes in the net.floodlightcontroller.packet package define the format for RIPv2 packets. All RIPv2 packets should be encapsulated in UDP packets whose source and destination ports are 520 (defined as a constant RIP_PORT in the net.floodlightcontroller.packet.UDP class). When sending RIP requests and unsolicited RIP responses, the destination IP address should be the multicast IP address reserved for RIP (224.0.0.9, defined as a constant RIP_MULTICAST_IP in the RIP class) and the destination Ethernet address should be the broadcast MAC address FF:FF:FF:FF:FF:FF (defined as a constant BROADCAST_MAC in the RIP class). When sending a RIP response for a specific RIP request, the destination IP address and destination Ethernet address should be the IP address and MAC address of the router that sent the request.

RIP Operation

Your router should send a RIP request out all of the router’s interfaces when RIP is initialized; do this in the init() function in the RIP class. Your router should send an unsolicited RIP response out all of the router’s interfaces every 10 seconds thereafter (defined as a constant UPDATE_INTERVAL in the RIP class); do this in the run() function in the RIP class.

When a RIP request or RIP response is received by your router, your data plane should invoke the handlePacket(...) function in the RIP class (as described in Part 3). Your router should update its route table based on these packets, and send any necessary RIP response packets (solicited or unsolicited).

Your router should time out route table entries for which an update has not been received for more than 30 seconds (defined as a constant TIMEOUT in the RIP class). You should never remove route entries for the subnets that are directly reachable via the router’s interfaces (i.e., the route table entries added by the init() function in the RIP class we provide).

Your router must implement the split horizon strategy to help combat the count to infinity problem. Assume a maximum hop count of 16.

Testing RIP

To test your router’s control plane, you will need a topology with more than one router.  We have provided two such topologies: pair.topo and triangle.topo. Provide one of these alternative files to the run_mininet.sh script to use one of these topologies.  

You will need to start an instance of your simple router for each router in the topology. For the first router, use port 8001 and host r1:

java -jar SimpleRouter.jar -p 8001 -v r1

For the second router, use port 8002 and host r2:

java -jar SimpleRouter.jar -p 8002 -v r2

For the third router, use port 8003 and host r3:

java -jar SimpleRouter.jar -p 8003 -v r3

And so on. You will need to run each in a separate terminal window. You should not include the -r option when testing your control plane code, since we want the router to build the route table rather than reading it from a file.


Submission Instructions

You must submit a single tar file of the src directory containing the Java source files for your simple router.  Please submit the entire src directory; do not submit any other files or directories. To create the tar file, run the following command, replacing username1 and username2 with the CS username of each group member:

 tar czvf username1_username2.tgz src

Upload the tar file to the Programming Assignment 2 dropbox on Learn @ UW. Please submit only one tar file per group.


Acknowledgements

This programming assignment borrows from the Simple Router assignment from Stanford CS144: An Introduction to Computer Networks and Rodrigo Fonseca’s IP Project from Brown University CSCI-1680: Computer Networks.