-
Notifications
You must be signed in to change notification settings - Fork 122
Router Exercise
In this exercise, you'll make a static layer-3 forwarder/switch. It's not exactly a router, because it won't decrement the IP TTL and recompute the checksum at each hop (so traceroute won't work). Actions to do TTL and checksum modification are expected in the upcoming OpenFlow version 1.1. However, it will match on masked IP prefix ranges, just like a real router.
From RFC 1812:
An IP router can be distinguished from other sorts of packet switching devices in that a router examines the IP protocol header as part of the switching process. It generally removes the Link Layer header a message was received with, modifies the IP header, and replaces the Link Layer header for retransmission.
To simplify the exercise, your "router" will be completely static. With no BGP or OSPF, you'll have no need to send or receive route table updates.
Each network node will have a configured subnet. If a packet is destined for a host within that subnet, the node acts as a switch and forwards the packet with no changes, to a known port or broadcast, just like in the previous exercise. If a packet is destined for some IP address for which the router knows the next hop, it should modify the layer-2 destination and forward the packet to the correct port.
Our hope is that this exercise will show that with an OpenFlow-enabled forwarding device, the network is effectively layerless; you can mix switch, router, and higher-layer functionality.
You'll need a slightly different topology, something like this: Note: For Mininet 2.0, the hosts have been renumbered h1-h3 and 10.1-10.3.
… which will need to be described in a way that Mininet will understand.
There's an example custom topology at:
~/mininet/custom/topo-2sw-2host.py
First, copy the example to a new file:
$ cp ~/mininet/custom/topo-2sw-2host.py mytopo.py
To run a custom topology, pass Mininet the custom file and pass in the custom topology:
$ sudo mn --custom mytopo.py --topo mytopo --mac
Then, in the Mininet console, run:
mininet> pingall
Now, modify your topology file to match the picture and verify full host connectivity with pingall in the Mininet console.
Set up IP configuration on each virtual host to force each one to send to the gateway for destination IPs that are outside of their configured subnet.
You'll need to configure each host with a subnet, IP, gateway, and netmask.
It may seem obvious, but we will warn you anyway: do not attempt to assign IP addresses to the interfaces belonging to switches s1 and s2. If you need to handle traffic "to" or "from" a switch, do so using OpenFlow.
We can do this directly from the custom topology that you created with mininet. Edit your "mytopo.py" to include the information above.
Helpful Code:
host1 = self.addHost( 'h1', ip="10.0.1.100/24", defaultRoute = "via 10.0.1.1" )
A router generally has to respond to ARP requests. You will see ethernet broadcasts which will (initially at least) be forwarded to the controller.
Your controller should construct ARP replies and forward them out the appropriate ports.
Structures you will need:
* arp cache * routing table (create a structure with all of the information statically assigned) * ip to port dictionary * message queue (while the router waits for an ARP reply)
Sample Routing Table:
(ip with network prefix, ip of host, interface name, interface address, switch port)
['10.0.1.100/24', '10.0.1.100', 's1-eth1', '10.0.1.1', 1], ['10.0.2.100/24', '10.0.2.100', 's1-eth2', '10.0.2.1', 3], ['10.0.3.100/24', '10.0.3.100', 's1-eth3', '10.0.3.1', 2]
NOTE: the port number does not correspond to the switch name. This is not a typo but a known mininet bug.
You will need to send a MAC address back to the host that is arping for you. It does not matter what this address is, so feel free to make up your own!
Helpful information about packet structure: NOTE: The following information is specific to the POX Controller
packet = event.parsed
packet contains the ethernet packet
protocol = packet.payload
strips the ethernet header and contains the ipv4 or arp packet
packet.payload.payload
strips the protocol header and contains the underlying packet(ICMP?)
packet.payload.payload.payload
strips the ICMP header and contains the echo or unreach packet, assuming this is an ICMP packet
...etc
ARP structure contains:
- hwdst (hardware address of destination - what we are asking for) - hwsrc (hardware address of source) - protodst (IP address of destination) - protosrc (IP address of source) - opcode (type of arp package[REQUEST, REPLY, REV_REQUEST, REV_REPLY])
Once ARP has been handled, you will need to handle routing for the static configuration. Since we know what is connected to each port, we can match on IP address (or prefix, in the case of the remote subnet) and forward out the appropriate port.
We need to handle all ipv4 traffic that comes through the router by forwarding it to the correct subnet. The only change in the packet should be the source and destination MAC addresses.
If the router does not know the MAC address of the destination, it will have to arp for that host.
Additionally, your controller may receive ICMP echo (ping) requests for the router, which it should respond to.
Lastly, packets for unreachable subnets should be responded to with ICMP network unreachable messages.
NOTE: the controller only accepts 128 byte packet_ins by default. If your messages are getting truncated, this could be why. As a workaround, add another component at the end of your call too the controller. it should look like this:
$ ./pox.py log.level --DEBUG (your controller) misc.full_payload
If the router works properly:
- attempts to send from 10.0.1.2 to an unknown address range like 10.99.0.1 should yield an ICMP destination unreachable message.
- packets sent to hosts on a known address range should have their MAC dst field changed to that of the next-hop router.
- the router should be pingable, and should generate an ICMP echo reply in response to an ICMP echo request.
mininet>xterm h1 h3
host 3 will be the iperf server, so in it's own terminal run the command
$ iperf -s
host 1 will be the client, so in it's own terminal run the command
$ iperf -c (ip address of iperf server)
you may also test udp traffic by adding a -u option to the end of both commands
Note the results for this test. In your code's current state, the router sends all of the traffic to the controller via packet_ins, which makes a routing decision and sends a packet_out to the router. The next step is to install flow mods, which should yield better performance with iperf.
Flow mods should be relatively easy to install. There are a few important structures to consider when creating the flow mod:
- of.ofp_flow_mod() ==> the packet that will eventually be sent to the router to add a flow entry
- of.ofp_match() ==> the structure containing the criteria for a packet to be matched on
- of.ofp_action_output(port = [port]) ==> action to be performed on matching packets. This action specifies a port to output the packet to
- of.ofp_action_dl_addr.set_src([hwsrc]) ==> action to change the ethernet packet's hardware source address
Steps to install flow mod:
1) Create a match for the type of packet coming insteps
2) Create an ofp_flow_mod structure, and set the match attribute to your match
3) Specify the actions to be taken when receiving this type of packet
4) Append each action to the flow mod structure
5) Send the flow mod back to the router
Now that you have flow mods installed, run the iperf tests again and notice the performance difference
The exercise is meant to give more practice with packet parsing and show how to use OpenFlow to modify packets.
Congratulations! By now you understand the basics of openflow, and probably have many ideas of things to implement in your own controller. The following exercises are just examples of further projects, but the possibilities are limitless. If you wish to continue with this tutorial, below are a few more exercises you may complete:
OpenFlow Tutorial Wiki - please feel free to fix errors and add improvements!