Version 9 (modified by, 7 years ago) (diff)


TCP Experiment


In this experiment you will learn how to set up a firewall using OpenFlow. We will use the following network topology for this experiment:
Openflow firewall topology


This tutorial expects that you have completed lab 0 and know how to create a new sliver with an existing rspec. If you have not completed lab 0 please do so now.


All tools will already be installed at your nodes. This experiment uses OpenFlow and Open vSwitch.

Where to get help:

For any questions or problem with the tutorial ask your TA or Professor for help. If a GENI tool is not working correctly please email


Set up

Download the RSPEC from

Using Omni, Portal, or Flack, create a new sliver using this rspec on either ProtoGENI or InstaGENI. For ProtoGENI, use the Utah aggregate manager. On omni, this will need the command

-a pg-utah

The final topology will match the image at the start of this handout. The specific host names allocated for your experiment will be different, but the topology will be isomorphic. The host labeled left in the figure is “behind” the firewall, implemented by the Open vSwitch host labeled switch. The host labeled router handles IP routing for the firewalled network, and every host on the other side of this router (the host labeled right being the only example here) are “outside” of the firewall.

You can test that the network configured correctly by running ping right from the host allocated for left or vice-versa. Since the fallback switch configuration will act like a normal learning switch, the ping packets should go through.

helpful tips

This section contains a few tips that may help you out if you are using the Open vSwitch implementation provided with this tutorial.

If you are using a hardware OpenFlow switch, your instructor can help you find equivalent commands.

The Open vSwitch installation provided by the RSpec included in this tutorial is located in /opt/openvswitch-1.6.1-F15. You will find Open vSwitch commands in /opt/openvswitch-1.6.1-F15/bin and /opt/openvswitch-1.6.1-F15/sbin. If you add these paths to your shell’s $PATH, you will be able to access their manual pages with man.

Note that $PATH will not affect sudo, so you will still have to provide the absolute path to sudo; the absolute path is omitted from the following examples for clarity and formatting.

  • 2.1 ovs-vsctl

Open vSwitch switches are primarily configured using the ovs-vsctl command. You may find the ovs-vsctl show command useful, as it dumps the status of all virtual switches on the local Open vSwitch instance. Once you have some information on the local switch configurations, ovs-vsctl provides a broad range of capabilities to expand your network setup to more complex configurations for testing and verification. In particular, the subcommands add-br, add-port, and set-controller may be of interest.

  • 2.2 ovs-ofctl

The switch host configured by the given rspec listens for incoming OpenFlow connections on localhost port 6634. You can use this to query the switch state using the ovs-ofctl command. In particular, you may find the dump-tables and dump-flows subcommands useful. For example, sudo ovs-ofctl dump-flows tcp: will output lines that look like this:

cookie=0x4, duration=6112.717s, table=0, n packets=1, n bytes=74, idle age=78,priority=5,tcp,
nw src= actions=CONTROLLER:65535

This indicates that any TCP segment with source IP in the subnet should be sent to the OpenFlow controller for processing, that it has been 78 seconds since such a segment was last seen, that one such segment has been seen so far, and the total number of bytes in packets matching this rule is 74.

You can use 'Trema' to run your switch. 'Trema is installed in /opt/trema-trema-8e97343. Once you have implemented your switch, you can simply use this Trema install to run it and the Open vSwitch will obey its configuration.

  • 2.3 Unix utilities

The standard ping and /usr/sbin/arping tools are useful for debugging connectivity (but make sure your controller passes ICMP ECHO REQUEST and REPLY packets and ARP traffic, respectively!), and the command netstat -an will show all active network connections on a Unix host; the TCP connections of interest in this exercise will be at the top of the listing. The format of netstat output is out of the scope of this tutorial, but information is available online and in the manual pages.



To get started you can download partially complete versions of the necessary configuration files: firewall.rb, fw.conf

A firewall observes incoming packets and uses a set of rules to determine whether any given packet should be allowed to pass. A stateful firewall keeps track of the packets it has seen in the past, and uses information about them, along with the rules, to make its determinations.

In this exercise you will build a stateful firewall controller for TCP connections in OpenFlow. The first packet of each connection will be handled by your controller. All other connection packets will be handled by the OpenFlow-enabled router or switch without contacting your controller. This design will allow you to write powerful firewall rule sets without unduly impacting packet forwarding speeds. Your controller will parse a simple configuration file to load its rules. Complete stateful firewalls often handle multiple TCP/IP protocols (generally at least both TCP and UDP), track transport protocol operational states, and often understand some application protocols, particularly those utilizing multiple transport streams (such as FTP, SIP, and DHCP). The firewall you will implement for this exercise, however, needs handle only TCP, and will not directly process packet headers or data.

Firewall Configuration
The firewall configuration language is very simple. All flows not specified in the configuration are assumed to be forbidden, and the default packet processing policy on the OpenFlow device you are managing should be to drop packets. The configuration language will specify, one flow to a line, the TCP flows that should be permitted to pass the firewall. The syntax is:

<ip>[/<netmask>] <port> <ip>[/<netmask>] <port>

Items in angle brackets (<>) represent variable values and items in square brackets ([]) represent optional syntax. The first subnet (IP address plus mask length) and port number are the subnet and port number of the host initiating the connection (that is, sending the first bare SYN), and the second subnet and port number are those of the host accepting the connection. IP addresses are specified as dotted quads (e.g., and netmasks as bit lengths (e.g., 24). If a netmask is omitted it is equivalent to /32. Port numbers are integers. Either or both of the IP address or port numbers may be replaced by the word any, equivalent to in the case of IP address, or to any port number, in the case of port numbers.

A sample configuration that implements a firewall permitting inbound connections to a web server at IP address on port 80, and any outbound connections initiated by hosts inside the firewall (protecting is as follows:

any any 80 any any any

All whitespace will be a single ASCII space character, all newlines will be a single ASCII newline character (0x0a) Empty lines (two newlines back-to-back) are permitted.

A connection is allowable if it matches any rule. A connection matches a rule if all four elements of the four-tuple match. Subnet matching uses standard rules, expressed in this pseudocode:

boolean subnet_match(IP subnet, int bits, IP addr) {
    int32 bitmask =  ̃((1 << 32 - bits) - 1);
    IP addrnet = addr & bitmask;
    return addrnet ˆ subnet == 0;

Note that rules are not bidirectional; the presence of the first rule in this set does not imply the second: any any any
any any any

The name of a firewall configuration file will be provided on the controller command line. To provide an argument to your controller application (in this case we are using 'Trema' as our controller application), it must be included with the controller file name. For example, to configure your firewall found in firewall.rb to load fw.conf, you would invoke:

trema run ’firewall.rb fw.conf’

You will then find [’firewall.rb’, ’fw.conf’] in ARGV when your controller’s start method is invoked.

Firewall Semantics

When an OpenFlow device connects to your controller (that is, you receive a switch_ready controller event), your controller should send it instructions to:

      • Pass all packets matching allowed connections to your controller
      • Drop all other packets

Priorities are going to be critical to the correct operation of your controller, so set them carefully. Higher priority rules match before lower priority rules, and the first matching rule is followed. See Section 3.4 of the OpenFlow specification for more details on flow matching.

Upon receiving a packet from the OpenFlow device (via a OFPT_PACKET_IN message), your controller should:

      • Ensure that the packet matches a rule in the configuration
      • Insert a flow match in the OpenFlow device for the complete four-tuple matching the incoming packet
      • Insert a flow match in the OpenFlow device for the complete four-tuple matching the opposite direction of the same connection
      • Instruct the OpenFlow device to forward the incoming packet normally (using OFPP_NORMAL)
      Packets which do not match a rule on the controller should be denied. Because your initial device configuration eliminates most of these packets outright, your controller should not see a large number of these packets.
      Because this firewall implementation cannot track the actual state of the TCP connections it is managing, removing accepted connections from the forwarding tables on the OpenFlow device must be handled by timers. OpenFlow rules can be removed by an idle timer as well as expired a fixed period after insertion. For this firewall, use an idle timer of 300 seconds.

Limitations of this Approach
Note that this approach to implementing a firewall has drawbacks. Because the OpenFlow controller does not, and can not efficiently, track the precise state of the TCP flow it is forwarding, the rules are a little bit sloppy. In particular, connections “in progress” when the firewall comes online are not differentiated from new connections created after the firewall is initialized, and connection closings can not be detected by the controller. The former can be managed by inspecting the packet headers included in the OFPT_PACKET_IN message when a connection is opened, but the latter cannot easily be mitigated. This means that connections with long idle times (and 300 s is not particularly unusual, in the long tail of TCP connection statistics!) will be disconnected unnecessarily, and new connections reusing recent four-tuples may be passed through the firewall without examination by the controller.

Exercises for 3.1

  1. Exercise 1: fill up the blanks in function switch_ready to insert rules into the openflow switch that allow ICMP and ARP packets to go through
  2. Exercise 2: fill up the blanks in function packet_in to insert a flow match in the OpenFlow device that allows the packets (as well as those in the reverse path) that match rules in the fw.conf to pass
  3. Exercise 3: fill up the blanks in function packet_in to insert rules that drops all other packets that does not match the rules specified in fw.conf

To verify your implementation, run the following on the switch:

 /opt/trema-trema-8e97343/trema run 'firewall.rb fw.conf'

Then try to ping from left to right. Ping should go through since you allowed ICMP packets and ARP packets to pass.

If you are using the fw.conf we provided, try to run a TCP session from left to right using iperf using port 5001, 5002, 5003.

Since in the fw.conf file we provided, we specifically allow TCP to go through port 5001 and 5002, but not port 5003, you should be able to see that iperf gives back throughput results for port 5001 and 5002 but not 5003.

Try play with the code as well as the fw.conf file to setup more rules, then verify your setting via iperf or telnet.

You can check the flow table on the OpenFlow Switch via:

sudo /opt/openvswitch-1.6.1-F15/bin/ovs-ofctl dump-flows tcp:

A sample output should be something like the following:

NXST_FLOW reply (xid=0x4):
 cookie=0x1, duration=165.561s, table=0, n_packets=6, n_bytes=360, idle_age=17,priority=65535,arp actions=NORMAL
 cookie=0xa, duration=43.24s, table=0, n_packets=3, n_bytes=222, idle_timeout=300,idle_age=22,priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=00:02:b3:65:d1:2b,dl_dst=00:03:47:94:c7:fd,nw_src=,nw_dst=,nw_tos=0,tp_src=46361,tp_dst=5003 actions=drop
 cookie=0x5, duration=147.156s, table=0, n_packets=18289, n_bytes=27682198, idle_timeout=300,idle_age=137,priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=00:02:b3:65:d1:2b,dl_dst=00:03:47:94:c7:fd,nw_src=,nw_dst=,nw_tos=0,tp_src=33385,tp_dst=5001 actions=NORMAL
 cookie=0x9, duration=105.294s, table=0, n_packets=4, n_bytes=296, idle_timeout=300,idle_age=60,priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=00:02:b3:65:d1:2b,dl_dst=00:03:47:94:c7:fd,nw_src=,nw_dst=,nw_tos=0,tp_src=46360,tp_dst=5003 actions=drop
 cookie=0x7, duration=124.764s, table=0, n_packets=17902, n_bytes=27095256, idle_timeout=300,idle_age=114,priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=00:02:b3:65:d1:2b,dl_dst=00:03:47:94:c7:fd,nw_src=,nw_dst=,nw_tos=0,tp_src=57908,tp_dst=5002 actions=NORMAL
 cookie=0x3, duration=165.561s, table=0, n_packets=1, n_bytes=74, idle_timeout=300,idle_age=124,priority=65535,tcp,nw_src=,nw_dst=,tp_dst=5002 actions=CONTROLLER:65535
 cookie=0x4, duration=165.561s, table=0, n_packets=1, n_bytes=74, idle_timeout=300,idle_age=147,priority=65535,tcp,nw_src=,nw_dst=,tp_dst=5001 actions=CONTROLLER:65535
 cookie=0x2, duration=165.561s, table=0, n_packets=0, n_bytes=0, idle_age=165,priority=65535,icmp actions=NORMAL
 cookie=0x6, duration=147.156s, table=0, n_packets=9387, n_bytes=624254, idle_timeout=300,idle_age=137,priority=65535,tcp,nw_src=,nw_dst=,tp_src=5001,tp_dst=33385 actions=NORMAL
 cookie=0x8, duration=124.764s, table=0, n_packets=9257, n_bytes=617666, idle_timeout=300,idle_age=114,priority=65535,tcp,nw_src=,nw_dst=,tp_src=5002,tp_dst=57908 actions=NORMAL

Hints - The following list of hints may help you design and debug your implementation more rapidly.

      • Remember that OpenFlow switches are an Ethernet switch first and foremost, and that not all packets on an Ethernet are IP. In particular, your hosts will require ARP in order to pass IP traffic through the switch!
      • You may pass ICMP packets without limitation, to make debugging easier.
      • The Trema Match class has a compare() method that accepts a Match argument and may be useful to you — consider the ExactMatch#from() method in conjunction.

Extra Credit
For extra credit (if permitted by your instructor), generate TCP reset segment at the firewall to reset rejected connections.

  • 3.2 Extending the Firewall

OpenFlow controllers can also make complex flow decisions based on arbitrary state. This is one benefit to removing the controller from the network device — the controller is free to perform any computation required over whatever data is available when making decisions, rather than being constrained to the limited computing power and storage of the network device. For this exercise, you will extend the firewall described in Section 3.1 to include rudimentary denial of service prevention using this capability.

Extended Firewall Configuration

You will extend the firewall configuration language to accept an additional final parameter, an integer representing the number of allowable connections matching a given rule at any point in time. As before, the keyword any will be used to indicate that no limiting is to be performed on the rule. The new firewall configuration syntax is:

<ip>/<netmask> <port> <ip>/<netmask> <port> <limit>

Connection Limiting Semantics

The extended firewall will perform flow matching as before, with one added check: if the number of existing flows allowed by a given rule exceeds the limit specified in the configuration, a new flow matching that rule will be denied. The number of existing flows matching a given rule is computed as the number of currently active flow matches in the OpenFlow device for that rule. You may wish to look into the OFPT_FLOW_REMOVED message for help in implementing this. If a connection rule specifies any as the flow limit, no limiting will be performed by the controller.

Note that the timeout-based nature of flow removal dictates that small connection limits will be quite limiting. Keep this in mind when testing your firewall!

Exercises for 3.2

  1. Exercise 1: Extend the firewall.rb to take the new firewall configuration file and accept the additional parameter


      • You probably want to change the rule structure defined in function add_rule to add two members: limit and count to store the maximum number of active flows allowed and the current number of active flows.
      • You should also over-ride the function flow_removed to subtract count when-ever a flow rule expired.
      • You might also set the idle_timeout to a smaller number (e.g., 30) so that you do not need to wait for too long before they get removed
      • To verify your implementation, use -P option in iperf to add TCP flows.

Attachments (3)

Download all attachments as: .zip