Protocols For Data Networks (Aka Advanced Computer Networks)
Protocols For Data Networks (Aka Advanced Computer Networks)
Lisboa, 2015
Programming Assignment 2
Contents
[2]
Programming Assignment 2
In this exercise, you will be working with Pyretic, a new programmer friendly language for networks
embedded in Python. Pyretic provides a runtime system that enables programmers to specify
network policies at a high level of abstraction, compose them together in a variety of ways, and
execute them on abstract topologies.
You will first be taken through the steps of writing network applications (i.e., a hub and a layer 2
MAC learning switch) on Pyretic and testing them using Mininet. After the walkthrough, you will be
asked to implement and submit two types of firewall.
Prerequisite
Download the pre-packaged Pyretic VM. It comes with Pyretic and Mininet.
o 64-bit: https://bitbucket.org/reich/pyretic-vms/downloads/Pyretic-0.2.2-amd64.zip
o 32-bit: https://bitbucket.org/reich/pyretic-vms/downloads/Pyretic-0.2.2-i386.zip
Import the VM to VirtualBox
Configure Pyretic VM network in VirtualBox: add another network adapter to the Pyretic VM
so that you can access the VM with ssh on your host machine
o Settings -> Network -> Adapter -> Attached to -> Host-only Adapter -> OK
Start the VM
o Username: mininet
o Password: mininet
Configure the network (in VirtualBox VM)
o $ ifconfig eth1
o $ sudo dhclient eth1
Access the VM with ssh on your host machine:
o Open a new terminal and ssh to the ip address of eth1 (192.168.56.101 in the
example). Option -X will allow you to open extra terminals under mininet later.
ssh -X 192.168.56.101
[3]
Programming Assignment 2
Overview
The network you will use in this exercise includes 3 hosts and a switch, this time with the Pyretic
runtime to express your network applications.
As mentioned above, Pyretic is a new language and system that enables modular programming by:
Defining composition operators and a library of policies for forwarding and querying traffic.
Pyretic's parallel composition operator allows multiple policies to operate on the same set
of packets, while its sequential composition operator allows one policy to process packets
after another.
Pyretic enables each policy to operate on an abstract topology that implicitly constrains
what the module can see and do.
Pyretic provides a rich abstract packet model that allows programmers to extend packets
with virtual fields that may be used to associate packets with high level metadata.
We will be using the Pyretic runtime system, so make sure no controller (the default or POX) is
running in the background. Also, confirm that the port 6633 used to communicate with OpenFlow
switches by the runtime is not bounded:
[4]
Programming Assignment 2
This will kill any existing TCP connection using this port.
[5]
Programming Assignment 2
You should also run sudo mn c and restart Mininet to make sure that everything is clean and
using the faster kernel switch. From you Mininet console:
mininet> exit
$ sudo mn -c
$ sudo mn --topo single,3 --mac --switch ovsk --controller remote
As explained, the Pyretic runtime comes preinstalled with the provided VM image.
This tells Pyretic to enable verbose logging and to start the hub component.
TIP 1: open two terminals: one for mininet, one for pyretic
TIP 2: start pyretic first for quicker hookup w/ mininet
The switches may take a bit to connect. When an OpenFlow switch loses its connection to a
controller, it will generally increase the period between which it attempts to contact the controller,
up to a maximum of 15 seconds. Since the OpenFlow switch has not connected yet, this delay may
be anything between 0 and 15 seconds. If this is too long to wait, the switch can be configured to
wait no more than N seconds using the max-backoff parameter. Alternately, you exit Mininet to
remove the switch(es), start the controller, and then start Mininet to immediately connect.
Wait until the application indicates that the OpenFlow switch has connected. When the switch
connects, Pyretic will print something like this:
Now verify that the hosts can ping each other, and that all hosts see the exact same traffic the
behaviour of a hub. To do this, we'll create xterms for each host and view the traffic in each. In the
Mininet console, start up three xterms:
mininet> xterm h1 h2 h3
[6]
Programming Assignment 2
Arrange each xterm so that they are all on the screen at once. This may require reducing the height
to fit a cramped laptop screen.
[7]
Programming Assignment 2
In the xterms for h2 and h3, run tcpdump, a utility to print the packets seen by a host:
and respectively:
The ping packets are now going up to the controller, which then floods them out all interfaces
except the sending one. You should see identical ARP and ICMP packets corresponding to the ping
in both xterms running tcpdump. This is how a hub works it sends all packets to every port on the
network.
Now, see what happens when a nonexistent host doesn't reply. From h1 xterm:
You should see three unanswered ARP requests in the tcpdump xterms. If your code is off later,
three unanswered ARP requests is a signal that you might be accidentally dropping packets.
"""Implement hub-like behavior --- send all packets to all ports on a network
minimum spanning tree, except for the input port"""
hub = flood()
def main():
return hub
[8]
Programming Assignment 2
Interestingly, Pyretics flood operator computes a minimum spanning tree under the hood, so you
dont have to worry about loops in your topology when you call Pyretics flood operator.
[9]
Programming Assignment 2
A hub is quite dumb. This type of device simply retransmits all the information it receives in one
port to all other ports. A layer 2 switch is smarter: it learns which devices are connected to its ports
(by monitoring the packets it receives), and then forwards on packets to the appropriate port only.
A layer 2 switch has this wonderful property that it is able to build its forwarding table
automatically by means of this self-learning algorithm. Similar to a traditional switch, in SDN a layer
2 learning switch is an application designed to detect the arrival of new hosts, discover their MAC
addresses, and route packets to them. To begin, the application starts by installing a default rule in
each edge switch that matches all packets and sends them to the controller. Upon receiving a
packet, the application learns the location (i.e., the switch and input port) of the sender. If the
receivers location is already known, the application installs rules that direct traffic in both
directions over a shortest path from one to the other; otherwise, the application instructs the
switch to floodbroadcasting the packet to all possible receivers. If a host moves to a new
location, the default rule at the new switch sends the next packet to the controller, allowing the
application to learn the hosts new location and update the paths that carry traffic to and from the
host. Consequently, hosts can continue communicating without disruption, even when one or both
hosts move.
Given this introduction, lets verify that hosts can ping each other when the controller is behaving
like a layer 2 learning switch. Kill the Pyretic runtime by pressing Ctrl-C in the window running the
program. Now, run the switch example:
Like before, we'll create xterms for each host and view the traffic in each. In the Mininet console,
start up three xterms:
mininet> xterm h1 h2 h3
Arrange each xterm so that they're all on the screen at once. This may require reducing the height
of to fit a cramped laptop screen. In the xterms for h2 and h3, run tcpdump, a utility to print the
packets seen by a host:
and respectively:
[ 10 ]
Programming Assignment 2
Here, the switch examines each packet and learn the sourceport mapping. Thereafter, the source
MAC address will be associated with the port. If the destination of the packet is already associated
with some port, the packet will be sent to the given port; else it will be flooded on all ports of the
switch.
Lets have a look at the pyretic_switch code (it should be pretty self explanatory):
class mac_learner(DynamicPolicy):
"""Standard MAC-learning logic"""
def __init__(self):
super(mac_learner,self).__init__()
self.flood = flood() # REUSE A SINGLE FLOOD INSTANCE
self.set_initial_state()
def set_initial_state(self):
self.query = packets(1,['srcmac','switch'])
self.query.register_callback(self.learn_new_MAC)
self.forward = self.flood # REUSE A SINGLE FLOOD INSTANCE
self.update_policy()
def set_network(self,network):
self.set_initial_state()
def update_policy(self):
"""Update the policy based on current forward and query policies"""
self.policy = self.forward + self.query
def learn_new_MAC(self,pkt):
"""Update forward policy based on newly seen (mac,port)"""
self.forward = if_(match(dstmac=pkt['srcmac'],
switch=pkt['switch']),
fwd(pkt['inport']),
[ 11 ]
Programming Assignment 2
self.forward)
self.update_policy()
def main():
return mac_learner()
In the following we present Pyretic policies that may be useful for the assignment.
match(f=v): filters only those packets whose header field f's value matches v
~A: negates a match
A & B: logical intersection of matches A and B
A | B: logical union of matches A and B
fwd(a): forward packet out port a
flood(): send all packets to all ports on a network minimum spanning tree, except for the
input port
A >> B: A's output becomes B's input
A + B: A's output and B's output are combined
if_(M,A,B): if packet filtered by M, then use A, otherwise use B
[ 12 ]
Programming Assignment 2
Assignment 2, Part 1
Background
A firewall is a network security system that is used to control the flow of ingress and egress traffic
usually between a more secure local-area network (LAN) and a less secure wide-area network
(WAN). The system analyses data packets for parameters like L2/L3 headers (i.e., MAC and IP
address) or performs deep packet inspection (DPI) for higher layer parameters (like application type
and services, etc.) to filter network traffic. A firewall acts as a barricade between a trusted, secure
internal network and another network (e.g. the Internet) which is supposed not to be secure or
trusted.
In this first part of the assignment, your task is to implement a layer 2 firewall that runs alongside
the MAC learning module on the Pyretic runtime. The firewall application is provided with a list of
MAC address pairs i.e., an access control list (ACLs). When a connection is established between the
controller and the switch, the application installs static flow rule entries in the OpenFlow table to
disable all communication between each MAC pair that is present in this list.
Network Topology
Your firewall should be agnostic of the underlying topology. It should take the MAC pair list as input
and install it on the switches in the network. To make things simple, we will implement a less
intelligent approach and will install rules on all the switches in the network.
To start this exercise, you need to download the skeleton files from https://github.com/cs-
teaching/pyretic-fw.
[ 13 ]
Programming Assignment 2
Note that you do not need to make any modification to the file firewall-policies.csv and
firewall-policies2.csv.
The pyretic_firewall.py is populated with a skeleton code. It consists of a main function and a global
variable (policy_file) that holds the path of the firewall-policies.csv file. Whenever a
connection is established between the Pyretic controller and the OpenFlow switch the main
function is executed.
Your task is to read the policy file and update the main function. The function should install policies
in the OpenFlow switch that drop packets whenever a matching src/dst MAC address (for any of the
listed MAC pairs) enters the switch.
Once you have your code, copy the pyretic_firewall.py and firewall-policies.csv to the
~/pyretic/pyretic/examples directory on your VM.
$ cd ~
$ pyretic.py pyretic.examples.pyretic_firewall &
What do you see? If everything has been done and setup correctly then host (h1) should not be
able to ping host (h2). Then try to ping host (h3) from host (h2):
[ 14 ]
Programming Assignment 2
What do you see? Host (h1) is able to ping host (h3) as there is no flow rule entry installed in the
network to disable the communication between them.
[ 15 ]
Programming Assignment 2
Assignment 2, Part 2
In the second part of the assignment your task is to implement a layer 3/4 firewall. The firewall
application is provided with a list of IP address and port pair (the ACL in firewall-
policies2.csv). In this assignment, when a connection is established between the controller and
the switch, the application installs static flow rule entries in the OpenFlow table to permit all
TCP/UDP communication between each IP, except the following (as per the ACL):
Testing the code is relatively similar to part 1. Make a test using the pingall command in mininet:
mininet> pingall
The result of this test should be positive, i.e, all hosts respond to ICMP packets.
Now, you should test the layer 4 policy using HTTP transfers using ports 80 and 8080 (you can also
test other ports). For this purpose you should start in h3 an HTTP server (as you learned in mininet
walkthrough):
None of these tests should succeed because all hosts are blocked to connections on port 8080.
Now, restart mininet and repeat the last two steps but changing the server to port 80. Make the
same test using wget from h1 and h2 (using that port, of course).
In this case you should succeed only with h2, as per the second ACL policy.
[ 16 ]
Programming Assignment 2
Submission
The students should submit the two versions of their pyretic_firewall.py (they an call them
pyretic_firewall_1.py and pyretic_firewall_2.py) in the delivery box for assignment
two in the course website.
Acknowledgements
This assignment is based on the firewall assignment from Jennifer Rexforss (Princeton University)
Advanced Computer Networks course, which was also inspired in Nick Feamsters (previously
Georgia Tech, now at Princeton University) coursera course on SDN.
[ 17 ]