Network Management Programming Assignments (in Python)

1. SNMP monitor

You are to write a python script to read the following 32-bit values from ifTable, and keep a running non-wrapping total for each interface of the following:

Python numbers are automatically high-precision. But when updating, you'll have to take into account the possibility of wrap-around. If the new and old values are newval and oldval, and newval > oldval, you update the total with

    total += (newval - oldval)

But if there was a wrap, then newval < oldval, the amount to increment by is (232-oldval) + newval. If you were doing true 32-bit arithmetic, this would be the same as (newval-oldval) above. But, in Python, you'll have to include the 232.

The model programs for you to use are walk.py and getnext.py. Both do pretty much the same thing, actually.

One approach is to use a Python dictionary for each variable, indexed by the interface number ifIndex (or ifDescr). The ifIndex is available as an integer as lastlevels(oid,1)[0]

Do the polling every 5 minutes, but then try to keep the program running for 24 hours (write out your totals every so often, just to make sure they're not lost). Binge-watch something in the meantime to try to get ifInOctets to wrap around!

It's a nice touch to save the very first values of the attributes above, and subtract them, so that your final number represents the cumulative output since the program started running.

Turn in your Python script, the final output, and also all the intermediate output values. (The latter should show if there was an overflow.)


2. Mininet Ethernet switch

In the mininet virtual machine, the file /root/pox/pox/misc/of_tutorial.py implements an Ethernet hub. Change the code so that it functions as a switch. The code has been structured to make this relatively straightforward; you will have to implement act_like_switch() and then change _handle_PacketIn so that it calls this instead of act_like_hub().

(You will need to install the mininet virtual machine! My virtual-disk image is here, suitable for VirtualBox.)

Rather than use of_tutorial.py directly, I recommend using the very similar myswitch.py. I've added some variable declarations and output messages.

Copy myswitch.py to the directory /root/pox/pox/misc, before you start, and edit only the latter.

To run this, you will need two root-login windows. In the first window, invoke mininet as follows (in the directory /root/pld, where rectangle.py is defined). Make sure, though, that the self.addLink( s4, s1 ) line is commented out first.

mn --custom brectangle.py --topo recttopo --controller remote --switch ovsk --mac

Then, start up pox in the second window as follows, from /root/pox; this will run myswitch.py (note the .py extension is not entered here). Using log.level --WARNING gets rid of a lot of annoying messages; the default log.level is INFO.

./pox.py log.level --WARNING misc.myswitch

At any time you can halt Pox (with CNTL-C) and restart it, without restarting mininet. Stopping and restarting Pox will leave the openflow tables in the switches alone, also, at least for a while.

Since you're running pox in its own window, print statements will appear right there, and you won't need the logging facility.

Handy Mininet commands are:

h1 ping h4
h1 ping -c 1 h2    # single ping
pingall            # should install all hosts into all switch forwarding tables

Information about Pox objects can be found, along with examples, in openflow.stanford.edu/display/ONL/POX+Wiki. Find misc.of_tutorial in the index and follow the link; that leads to a very detailed page on the exercise.

There are really two steps here. Step one is probably sufficient, but Step two is cool:

Step one: use self.resend_packet(packet_in, ??) to have the controller tell the switch to resend the packet out the given port.

Step two: use msg = of.ofp_flow_mod() / msg.match.dl_dst = ... / msg.actions.append(...) to install a new forwarding rule in the switch (so packets with the matching destination can be handled by the switch with no further interaction with the controller).

If working on Step two, open a third command window, again with a root prompt, and use the command ovs-ofctl dump-tables s1 to examine the forwarding table of a switch (here s1).

For Step one, you need to complete the code in act_like_switch(). Once it's reasonable (that is, once you think it might compile), change _handle_PacketIn() according to this:

# Comment out the following line and uncomment the one after
# when starting the exercise.
self.act_like_hub(packet, packet_in)
#self.act_like_switch(packet, packet_in)

For act_like_switch() itself, the Pox website has more on what's in the parameters packet and packet_in. However, I've set the important values:

psrc = packet.src
pdst = packet.dst
inport = packet_in.in_port
# outport defined below
# switch is self.switchnum

The basic idea is that you will replace act_like_hub()'s

    self.resend_packet(packet_in, of.OFPP_ALL)

by changing of.OFPP_ALL to the specific next_hop port number.

To edit on the mininet virtual machine, the simplest is to call startx to start up the X-windows environment, and then use the gedit graphical edit editor. Don't forget the key to release the mouse, if necessary: right-cntl. Alternatively, you can edit in your preferred editor on your host system, and then copy everything and paste it into the appropriate mininet-vm file. For example, you can do this:

cat > myswitch.py
paste in the contents you want
CNTL-D  (eof)

Pasting into a terminal usually involves something other than CNTL-V, though.

Additional notes added after the August 2 class:

What if the controller uses this strategy:
    if (srcaddr is NOT in the forwarding table)
        add flow rule: reach srcaddr via srcport

Demo: with 5 linear switches and a pingall:

h1 sends ping: s1-s5 learn where h1 is
hi sends response: s1-s5 KNOW where h1 is, so the controller is NOT contacted, so the controller does not learn where h2-h5 are
h2 sends ping to h1: s2 knows where h1 is; no controller involvement
h1 responds: packet gets flooded but there's no new source for controller to learn, so no new flows
h2 sends ping to h3/h4/h5: packet gets flooded, s1 (and s2-s5) learns where h2 is
...
h3 sends ping to h1 or h2: s1 and s2 know where h1,h2 are: no flooding, no new flows
h1/h2 respond to h3: packet is flooded; no new source so no new flows
h3 sends ping to h4/h5: packet gets flooded and all switches learn where h3 is
...

In the end, all switches know where h1-h4 are, but not h5:

for i in 1 2 3 4 5
do
    ovs-ofctl dump-flows s$i |egrep -i '00:0[1-5]'
    echo; echo
done

Now for tutorial3.py, which uses the following strategy:
    put srcaddr into the controller-side per-switch table when an unknown dest packet is reported
    if destaddr is in the controller-side per-switch table, insert both flows, for both src and dest

    This doesn't really work any better. In the following, cs1-cs5 are the controller-side tables

h1 sends ping: cs1-cs5 learn where h1 is, but s1-s5 just flood.
h2 sends response: s2 doesn't know anything, so controller is notified, and controller knows where h1 and h2 are, so s1 and s2 get told where h1 and h2 are.
h3 sends response: s3 doesn't know anything, so

so the controller is NOT contacted, so the controller does not learn where h2-h5 are
h2 sends ping to h1: s2 knows where h1 is; no controller involvement
h1 responds: packet gets flooded but there's no new source for controller to learn, so no new flows
h2 sends ping to h3/h4/h5: packet gets flooded, s1 (and s2-s5) learns where h2 is
...
h3 sends ping to h1 or h2: s1 and s2 know where h1,h2 are: no flooding, no new flows
h1/h2 respond to h3: packet is flooded; no new source so no new flows
h3 sends ping to h4/h5: packet gets flooded and all switches learn where h3 is
...

See also the Pox output file tutorial3_line4.out.

The core problem is that it's new source addresses that switches must inform the controller about, but the flows above match only on the destination address. So, if a packet arrives at a switch S with a destination known to S, then S will not contact the controller even if the source is new.

The fix is to identify flows by a pair (destaddr,srcaddr). Doing this means that a new srcaddr cannot -- because it is new -- be part of an existing flow, and so will always be reported to the controller. The controller would typically install a bidirectional flow for that (destaddr,srcaddr) pair if it knows the port it should use to reach destaddr. If it does not, it will record the port used to reach srcaddr (that is, the port by which this packet arrived), flood the packet, and wait for the destaddr node to reply.

This is done in the pox example forwarding/l2_pairs.py.