Description
Spring 2025
In this project, you will use Software Defined Networking (SDN) principles to create a configurable firewall using
an OpenFlow enabled Switch. The Software Defined Networking (OpenFlow) functionality allows you to
programmatically control the flow of traffic on the network.
This project has two phases (and one optional phase) as follows:
1. Mininet Tutorial β This phase is a brief overview of Mininet. There are no deliverables for this phase and
may be skipped, especially if you completed the Optional Simulating Networks project (Project 0). You
will find this information in Appendix D.
2. Wireshark Tutorial β This phase is a brief introduction to packet capture using Wireshark/tshark. You
will examine the packet format for various traffic to learn of the different header values used in Phase 3.
There is a deliverable of a simple packet capture file.
3. SDN Firewall β This phase involves completing code to build a simple traffic blocking firewall using
OpenFlow with the POX Controller based on rules passed to it from a configuration file. In addition, you
will create a set of rules to test the firewall implementation.
Part 0: Project References
You will find the following resources useful in completing this project. It is recommended that you review these
resources before starting the project.
β’ IP Header Format – https://erg.abdn.ac.uk/users/gorry/course/inet-pages/ip-packet.html
β’ TCP Packet Header Format – https://en.wikipedia.org/wiki/Transmission_Control_Protocol
β’ UDP Packet Header Format – https://en.wikipedia.org/wiki/User_Datagram_Protocol
β’ The ICMP Protocol – https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol
β’ IP Protocols – https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
β’ TCP and UDP Service and Port References –
https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
β’ Wireshark – https://www.wireshark.org/docs/wsug_html/
β’ CIDR Calculator – https://account.arin.net/public/cidrCalculator
β’ CIDR – https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing
There are a few videos describing various aspects of the project:
β’ Project Description β https://youtu.be/Kl4nRgoeLxw
β’ Wireshark Tutorial β https://youtu.be/AnTi1m0imVk
β’ IP Network Address / Subnets / CIDR β See Edstem
β’ How to Manually Test β https://youtu.be/dj323mdA3sg
3
Part 1: Files Layout
Unzip the SDNFirewall-Fall2024zip file into your Virtual Machine. You can extract to any folder on your system.
Do this by running the following command:
unzip SDNFirewall-Fall2024.zip
This will extract the files for this project into a directory named SDNFirewall at your current path (it is
recommended that your use the mininet root directory to aid in troubleshooting (cd ~ ). The following files will
be extracted:
β’ cleanup.sh β this file called by using following command line: ./cleanup.sh
This file will clean up the Mininet Environment and kill all zombie Python and POX processes.
β’ sdn-topology.py β this file creates the Mininet topology used in this assignment. This is like what you
created in the Simulating Networks project. When evaluating your code against the ruleset specified in
this project, do not change it. However, you are encouraged to make your own topologies (and rules) to
test the firewall. Look at the start-topology.sh file to see how to start a different topology.
β’ ws-topology.py β this file is substantially like sdn-topology, but it does not call the POX Controller. You
will use this during the Wireshark exercise.
β’ setup-firewall.py β this file sets up the frameworks used in this project. DO NOT MODIFY THIS FILE. This
file will create the appropriate POX framework and then integrates the rules implemented in sdnfirewall.py into the OpenFlow engine. It will also read in the values from the configure.pol file and
validate that the entries are valid. If you make changes to this file, the autograder will likely have issues
with your final code as the autograder uses the unaltered distribution version of this file.
β’ start-firewall.sh β this is the shell script that starts the firewall. This file must be started before the
topology is started. It will copy files to the appropriate directory and then start the POX OpenFlow
controller. This file is called by using following command line: ./start-firewall.sh
β’ start-topology.sh β this is the shell script that starts the Mininet topology used in the assignment. All it
does is call the sdn-topology.py file with superuser permissions. This file is called by using following
command line: ./start-topology.sh
β’ test-client.py β this is a python test client program used to test your firewall. This file is called using the
following command line: python test-client.py PROTO SERVERIP PORT SOURCEPORT where PROTO is
either T for TCP, U for UDP, or G for GRE, SERVERIP is the IP address of the server (destination), PORT is
the destination port, and optionally SOURCEPORT allows you to configure the source port that you are
using. Example: python test-client.py T 10.0.1.1 80
β’ test-server.py β this is a python test server program used to test your firewall. This file is called using the
following command line: python test-server.py PROTO SERVERIP PORT where PROTO is either T for
TCP, U for UDP, G for GRE, SERVERIP is the IP address of the server (the server you are running this script
on), and PORT is the service port.
Example: python test-server.py T 10.0.1.1 80
β’ test-suite β This is a student developed test script that was developed in 2021 that can be used to test
your implementation AFTER YOU FINISH BOTH THE IMPLEMENTATION FILES. The test cases in the main
folder will be used to evaluate your implementations for the first run. An alternate configuration and
4
topology will also be used to evaluate your implementations. This will be like, but not identical to what
is found in the extra sub-folder. See Appendix A for information on how to use the test suite.
Project Deliverables
β’ configure.pol – this file is where you will supply the configuration to the firewall that specifies the traffic
that should either be blocked or allowed (override blocks). The format of this file will be specified later
in this document. This file is one of the deliverables that must be included in your ZIP submission to
Canvas.
β’ sdn-firewall.py βThis file implements the firewall using POX and OpenFlow functions. It receives a copy
of the contents of the configure.pol file as a python list containing a dictionary for each rule and you will
need to implement the code necessary to process these items into POX policies to create the firewall.
This file is one of the deliverables that must be included in your ZIP submission to Canvas.
β’ packetcapture.pcap β This will be the packet capture completed in Part 4. This file is one of the
deliverables that must be included in your ZIP submission to Canvas.
Part 2: Mininet
If you did not complete the Simulating Networks Optional Project, you may want to review the βMininet
Reviewβ section in Appendix D to learn about the different aspects of how a mininet network is setup, including
how to configure the physical topology and assignment of addresses to hosts and switches.
Part 3: Wireshark
Wireshark is a network packet capture program that will allow you to capture a stream of network packets and
examine them. Wireshark is used extensively to troubleshoot computer networks and in the field of information
security. We will be using Wireshark to examine the packet headers to learn how to use this information to
match traffic that will be affected by the firewall we are constructing.
tshark is a command line version of Wireshark that we will be using to capture the packets between mininet
hosts and we will use Wireshark for the GUI to examine these packets. However, you will be allowed to use the
Wireshark GUI if you would like in doing the packet capture.
Please watch the Wireshark Tutorial Video if you would like to follow along in time for a live packet capture.
β’ Step 1: Open up a Terminal Window and change directory to the SDNFirewall directory that was
extracted in Part 1.
β’ Step 2: The first action is to start up the Mininet topology used for the Wireshark capture exercise. This
topology matches the topology that you will be using when creating and testing your firewall. To start
this topology, run the following command:
sudo python ws-topology.py
This will startup a Mininet session with all hosts created. If you use sdn-topology.py, you will get a
controller error. Ctrl-C and redo step 2 to get the correct topology.
5
β’ Step 3: Start up two xterm windows for hosts us1 and us2. After you start each xterm window, it is
recommended that you run the following command in each xterm window as you load them to avoid
confusion about which xterm belongs to which host:
export PS1=βhostname >β replacing hostname with the actual hostname.
Type in the following commands at the Mininet prompt. This is optional, but helps with identifying
which xterm window belongs to which host.
us1 xterm & (then run export PS1=βus1 >β in the xterm window that pops up)
us2 xterm & (likewise, run export PS1=βus2 >β in the second xterm window)
β’ Step 4: In this step, we want to start capturing all the traffic that traverses through the ethernet port on
host us1. We do this by running tshark (or alternatively, wireshark) as follows from the mininet prompt:
us1 sudo tshark -w /tmp/packetcapture.pcap
This will start tshark and will output a pcap formatted file to packetcapture.pcap to the tmp directory.
Note that this file is created as root, so you will need to change ownership to mininet to use it in future
steps β chown mininet:mininet /tmp/packetcapture.pcap
If you wish to use the Wireshark GUI instead of tshark, you would call us1 sudo wireshark &. You may
use this method, but the TA staff will not provide support for any issues that may occur.
YOU WILL SUBMIT THIS FILE AS A PART OF YOUR SUBMITTAL.
β’ Step 5: Now we need to capture some traffic. Do the following tasks in the appropriate xterm window:
in us1 xterm: ping 10.0.1.2 (hit control C after a few ping requests)
In us2 xterm: ping 10.0.1.1 (likewise hit control C after a few ping requests)
In us1 xterm: python test-server.py T 10.0.1.1 80
In us2 xterm: python test-client.py T 10.0.1.1 80
After the connection completes, in the us1 xterm, press Control-C to kill theserver.
In us1 xterm: python test-server.py U 10.0.1.1 8000
In us2 xterm: python test-client.py U 10.0.1.1 8000
In us1 xterm: press Control C to kill the server
In Mininet Terminal: press Control C to stop tshark
β’ Step 6: At the mininet prompt, type in exit and press enter. Next, do the chown command as described
in step 4 above to your packet capture. You may also close the two xterm windows as they are finished.
Copy the /tmp/packetcapture.pcap to your project directory. This file is the deliverable for this phase of
the project. See the instructions in Step 4 that describe how you will need to change ownership of this
file in order to change or move it.
6
β’ Step 7: At the bash prompt on the main terminal, run:
sudo wireshark
Go to the File => Open menu item, browse to the /tmp directory and select the pcap file that you saved
using tshark.
You will get a GUI that looks like the example packet capture. You will have a numbered list of all the captured
packets with brief information consisting of source/destination, IP protocol, and a description of the packet. You
can click on an individual packet and will get full details including the Layer 2 and Layer 3 packet headers,
TCP/UDP/ICMP parameters for packets using those IP protocols, and the data contained in the packet.
Example Packet Capture β Host us1 making web request to Host us2
Note the highlighted fields. You will be using the information from these fields to help build your firewall
implementation and ruleset. Note the separate header information for TCP. This will also be the case for UDP
packets.
Also, examine the three-way handshake that is used for TCP. What do you expect to find for UDP? ICMP?
Example TCP Three-Way Handshake
Please examine the other packets that were captured to help you familiarize yourself with Wireshark.
7
Part 4: SDN Firewall Implementation Details
Using the information that you learned above in running Wireshark, you will be creating two files β one is a
firewall configuration file that will specify different header parameters to match in order to allow or block
certain traffic that will define the actions of the firewall and the second is the implementation code to create the
OpenFlow Flow Modification objects that will create the firewall using the parameters given in the firewall
configuration file.
Part 4a: Specifications of configure.pol
The configure.pol file is used by the firewall implementation code to specify the rules that the firewall will use to
govern a connection. You do not need to code this first, but the format of the file is important to understand as
your implementation code will need to use these items. The format of the file is a collection of lines that have
the proper format:
Rule Number, Action, Source MAC, Destination MAC, Source IP Network Address, Destination IP
Network Address, Protocol, Source Port, Destination Port, Comment/Note
o Rule Number = this is a rule number to help you track a particular rule β it is not to be used at all in your
firewall implementation except to help you find rules that cause an error. DO NOT USE FOR PRIORITY.
o Action = Block or Allow Block rules will block traffic that matches the remaining parameters of this
rule. Allow rules will override Block rules to allow specific traffic to pass through the firewall (see below
for an example). The entry is a string in (Block,Allow) and is validated by the parser.
o Source / Destination MAC address in form of xx:xx:xx:xx:xx:xx (example: 00:a2:c4:3f:11:09) or a β-β if
you are not matching this item. You may use MAC Addresses to match an individual host. In the real
world, you would use it to match a particular piece of hardware. The MAC address of a particular host is
defined inside the sdn-topology.py file. The parser will verify that the MAC Address is in a proper form.
If not, an error will be thrown.
o Source / Destination IP Network Address in form of xxx.xxx.xxx.xxx/xx in CIDR notation or a β-β if you
are not matching this item. IMPORTANT NOTE: THIS MUST BE A VALID NETWORK ADDRESS. Thus, if
you specify a subnet mask of /24, it means the first 24 bits will be the network address and the last 8
bits will be 0. Thus, for a host 10.0.10.10/24, the NETWORK ADDRESS will be 10.0.10.0/24. If you
specify 10.0.10.10/24, you will get an error. If you want to specify a single host, then your netmask
will be all 1s for the 32-bits of the address (i.e., a /32). The parser for the file will validate that you
have a proper IP Address given, but will not parse to see if it is a valid network address. You will get a
POX error if it is not a valid Network Address.
The IP address of a particular host is defined inside the sdn-topology.py file.
8
o Protocol = integer IP protocol number per IANA (0-254) or a β-β if you are not matching this item.. An
example is ICMP is IP Protocol 1, TCP is IP Protocol 6, etc.
o Source / Destination Port = if Protocol is TCP or UDP, this is the Application Port Number per IANA. For
example, web traffic is generally TCP Port 80. Do not try to use port numbers to differentiate the
different elements of the ICMP protocol. If you are not matching this item or are using an IP Protocol
other than TCP or UDP, this field should be a β-β.
o Comment/Note = this is for your use in tracking rules.
Special Notes About Firewall Configurations:
o Any field not being used for a match should have a ‘-‘ character as its entry. A β-β means that the item is
not being used for matching traffic. It is valid for any rule element except for Action, Rulnenum, or
Comment to have a β-β specified. Note that if you pass a β-β to one of the match items in your code, you
will crash POX.
o All fields are passed as a string, so you must do type conversions as necessary.
o You should not use a subnet mask smaller than /24. Using a /16 to cover all the subnets is not allowed.
o Do not use 0.0.0.0/0 to address the internet as a whole. This may have unexpected behavior in POX.
There is an easier way to match the world than using 0.0.0.0. Hint: Think about what a β-β means.
o When should I use MAC vs IP Addresses? You will want to interchange them in this file to test the
robustness of your implementation. It is valid to specify a Source MAC address and a Destination IP
Address.
Example Rules (included in the project files:
1,Block,-,-,10.0.0.1/32,10.0.1.0/24,6,-,80,Block 10.0.0.1 host from accessing a web server on the 10.0.1.0/24 network
2,Allow,-,-,10.0.0.1/32,10.0.1.125/32,6,-,80,Allow 10.0.0.1 host to access a web server on 10.0.1.125 overriding rule
What do these rules do?
The first rule basically blocks host hq1 (IP Address 10.0.0.1/32) from accessing a web server on any host on the
us network (the subnet 10.0.1.0/24 network). The web server is running on the TCP IP Protocol (6) and uses TCP
Port 80.
The second rule overrides the initial rule to allow hq1 (IP Address 10.0.0.1/32) to access a web server running on
us5 (IP Address 10.0.1.125/32)
By definition β from the sdn-topology.py file:
This class defines the Mininet Topology for the network used in this project. This network consists of the following
hosts/networks:
Headquarters Network (hq1-hq5). Subnet 10.0.0.0/24
US Network (us1-us5). Subnet 10.0.1.0/24
India Network (in1-in5). Subnet 10.0.20.0/24
9
China Network (cn1-cn5). Subnet 10.0.30.0/24
UK Network (uk1-uk5). Subnet 10.0.40.0/24
In Part 5, you will be given a set of firewall conditions that you will need to create the configure.pol needed for
your submission.
You may create temporary rulesets to help you complete Part 5b below.
Part 4b: Implementing the Firewall in Code
After reviewing the format of the configure.pol file, you will now code a generic implementation of a firewall
that will use the values provided from the configuration file (passed to you as dictionary items). As it is
provided, the firewall implementation code blocks no traffic. You must implement code that does the following:
o Create an OpenFlow Flow Modification object
o Create a POX Packet Matching object that will integrate the elements from a single entry in
the firewall configuration rule file (which is passed in the policy dictionary) to match the
different IP and TCP/UDP headers if there is anything to match (i.e., no β-β should be passed
to the match object, nor should None be passed to a match object if a β-β is provided).
o Create a POX Output Action, if needed, to specify what to do with the traffic.
Please reference code examples in Appendix C, or you may refer to the POX API documentation
(WARNING, this is long and the API is confusing).
You will need to rewrite the rule = None to reference your Flow Modification object.
Your code will go into a section that will repeat itself for every line in the firewall configuration file that is passed
to it. The βruleβ item that is added to the βrulesβ list is an OpenFlow Modification object. The process of injecting
this rule into the POX controller is handled automatically for you in the start-firewall.py file.
TIP: If your implementation code segment is more than 25-30 lines, you are making it too difficult. The POX
API can provide many features that are not used in this project. The Appendix provides all of the information
that you will need to code the project.
Key Information:
o policies is a python list that contains one entry for each rule line contained in your configure.pol file.
Each individual line of the configure.pol file is represented as a dictionary object named policy. This
dictionary has the following keys:
o policy[βmac-srcβ] = Source MAC Address (00:00:00:00:00:00) or β-β
o policy[βmac-dstβ] = Destination MAC Address (00:00:00:00:00:00) ) or β-β
o policy[βip-srcβ] = Source IP Address (10.0.1.1/32) in CIDR notation ) or β-β
o policy[βip-dstβ] = Destination IP Address (10.0.1.1/32) ) or β-β
o policy[βipprotocolβ] = IP Protocol (6 for TCP) ) or β-β
10
o policy[βport-srcβ] = Source Port for TCP/UDP (12000) ) or β-β
o policy[βport-dstβ] = Destination Port for TCP/UDP (80) ) or β-β
o policy[βrulenumβ] = Rule Number (1)
o policy[βcommentβ] = Comment (Example Rule)
o policy[βactionβ] = Allow or Block
Use these to match traffic. Please note that all fields are strings and may contain a β-β character. You
may either use policy[βip-dstβ] or the split policy[βip-dst-addressβ]/[policy[βip-dst-subnetβ] in your
implementation (the split was requested by prior semesters), but realize that if you use the ip-dstaddress and ip-dst-subnet, you will need to carefully check your implementation to ensure that it is
blocking the addresses you intend to block.
o You will need to assume that all traffic is IPV4. It is acceptable to hardcode this value. Do not hardcode
other values. Your code should be generic enough to handle any possible configuration.
o Hints:
o The difference between an Allow or a Block is dependent on an Action and the Priority.
o You donβt necessarily need an action. See Appendix C for a discussion of what happens to a
packet after it is matched.
o There should be two priorities β one for ALLOW and one for BLOCK. Separate them sufficiently
to override any exact matching behavior that the POX controller implements). It is suggested
one priority be 0 or 1 and the other one above 10000. The reasoning for this is discussed in
Appendix C.
o Outputting extra print debug lines will not adversely impact the autograder.
o Remember, you may be tested with IP Protocols outside of 1, 6, and 17. Donβt hardcode.
Part 5: Configuration Rules
You will need to submit a configure.pol file to create policies that implement the following scenarios. You may
implement your rules in any manner that you want, but it is recommended using this step as an opportunity to
check your firewall code implementation. The purpose of these rules is to test your firewall and to help
determine how traffic flows across the network (source vs destination, protocols, etc).
DO NOT block all traffic by default and only allow traffic specified. You will lose many points because the
firewall is open by default and only blocks the traffic that is specified.
Firewall Rules for Fall 2024:
β’ Task 1: Host cn4 has a TCP-based worm virus. Block cn4 from initiating network communications to
any host on the internet (world) over the TCP Internet Protocol. You need not block ICMP or UDP.
(one rule max)
β’ Task 2: Host cn5 has had a security incident and needs to be completely isolated from the network so
it has no connectivity (incoming or outgoing) to any other host on the internet (world). (two rules
max)
11
β’ Task 3: Allow the hosts on the Headquarters network to be reachable via an ICMP ping from the
world (including all but the China subnet (to avoid conflicts with Tasks 1 and 2 above). In addition, the
corporate subnets should not be pingable from the internet (world). However, to satisfy the first half
of this task, you must allow the Headquarters network to be able to ping the US, UK, and India
subnets. Can you explain why this must happen? (six rules typically)
The China network is exempted from this rule because any overrides that you may do here will
override aspects of Task 1 or Task 2 above.
β’ Task 4: Do not allow any response back from a TCP web server (http and https) from host cn3 to any
other host on the internet (world). (two rules max)
β’ Task 5: (CIDR Notation Rule) The servers located on hosts us3 and us4 run a micro webservice on TCP
Port 9250 9520 that processes financial information. Access to this service should be blocked from
hosts uk2, uk3, uk4, uk5, in4, in5, us5, and hq5. Please use the minimal CIDR notation that will bracket
the subset of hosts for each rule (it should NOT be broader than /28). (four rules typical)
β’ Task 6: A rogue Raspberry Pi has been found on the network that has cloned the Network Address of
host us1. Block this device from accessing any other hosts on the internet (world) on the UDP Internet
Protocol. (one rule max)
Note that when testing this rule, the IP address for host us1 may differ from the published topology.
In this case, you want to address the particular hardware device.
β’ Task 7: Block the internet (world) from accessing TCP Port 25 on any corporate subnets. The behavior
of access to TCP Port 25 amongst members of the corporate subnets is undefined and you may handle
it as you wish. (five rules max)
What to Turn In
You need to submit your copy of packetcapture.pcap, sdn-firewall.py and configure.pol from your project
directory using the instructions from the Piazza Post βHow to Submit / Zip Our Projects. To recap, zip up the two
files using the following command, replacing gtlogin with your GT Login that you use to log into Canvas:
zip gtlogin_sdn.zip packetcapture.pcap configure.pol sdn-firewall.py
The key to properly zipping the project is to NOT zip up the directory. ZIP only the files you are included.
You may also include an additional text file if you have comments, criticisms, or suggestions for improvement for
this project. If you wish to provide this information, add it to your ZIP file with the name comments.txt. This is
completely optional.
Please check your submission after uploading. As usual, we do not accept resubmissions past the stated
deadlines.
What you can and cannot share
Do not share the content of your sdn-firewall.py, configure.pol, or packetcapture.pcap with your fellow
students, on Ed Discussions, or elsewhere publicly. You may share any new topologies, testing rulesets, or
testing frameworks, as well as packet captures that do not address the requirements of Part 4b.
12
Rubric
For the Summer 2024 Semester, this project is worth a total of 100 points which is distributed in the following
fashion:
β’ 5 points for submitting a version of sdn-firewall.py that indicates effort was done.
β’ 5 points for submitting a version of configure.pol that indicates effort was done.
β’ 15 points for submitting a version of packetcapture.pcap that indicates effort was done.
β’ 25 points for testing your configure.pol file with a known-good implementation.
β’ 25 points for testing your configure.pol with your implementation.
β’ 25 points for testing your implementation with a known-good configure.pol.
13
Appendix A: How to Test Host Connectivity
Part A: How to Test Manually
When you are developing your implementation or troubleshooting a firewall rule, you will want to test by hand.
Unfortunately this process is a bit difficult.
1,Block,-,-,10.0.0.1/32,10.0.1.0/24,6,-,80,Block 10.0.0.1 from accessing a web server on the
10.0.1.0/32 network
Startup Procedure:
o Step 1: Open two terminal windows or tabs on the VM and change to the SDNFirewall directory.
o Step 2: In the first terminal window, type in: ./start-firewall.sh configure.pol
If you get the following error, run chmod +x start-firewall.sh and chmod +x start-topology.sh
This should start up POX, read in your rules, and start up an OpenFlow Controller. You will see
something like this in your terminal window:
TA Note: Note that you will not see the βList of Policy Objects imported from configure.polβ and the
βAdded Ruleβ lines until after you complete Step 3 below.
o Step 3: In the second terminal window, type in: ./start-topology.sh
This should start up mininet and load the topology. You should see the following:
This will start the firewall and set the topology. You do not need to repeat Steps 1-3 unless you are
14
done testing, need to restart the firewall, or need to restart mininet. When you are done with testing all
of the rules you intend to use, type in βquitβ in the mininet window, close all of the extraneous xterm
windows generated, and run the mininet cleanup script ./cleanup.sh
How to test connectivity between two hosts:
o Step 1: To test the rule shown above, we want to use host us1 as server/destination and host hq1 as the
client. The rule we are testing involves the hq1 host attempting to connect to the web server port (TCP
Port 80) on host us1. At the mininet prompt, type in the following two commands on two different lines:
hq1 xterm &
us1 xterm &
Two windows should have popped up. You can always identify which xterm is which by running the
command: ip address from the bash shell. This will give you the IP address for the xterm window, which
will then let you discover which xterm window belongs to which host.
o Step 2: In the xterm window for us1 (which is the destination host of the rule β remember that the
destination is always the server), type in the following command:
python test-server.py T 10.0.1.1 80
This sets up the test server for us1 that will be listening on TCP port 80. The IP Address specified is
always the IP address of the machine you are running it on. If you attempt to start the test-server on a
machine that does not have the IP address that is specified in the command, you will get the following
error: OSError: [Errno 99] Cannot assign requested address.
o Step 6: In the xterm window for hq1 (which is the source host of the rule β remember that the source is
always the client), type in the following command:
python test-client.py T 10.0.1.1 80
This will start up a client that will connect to the TCP Port 80 on the server 10.0.1.1 (destination IP
address) and will send a message string to the server. However, if the firewall is set to block this
connection, you will never see the message pass on either of the client or the server.
Examples of Connection Status:
β’ The two windows below depict a successful un-blocked connection between the client and the
server.
15
β’ A blocked connection will look like this (note that the client may take a while to timeout):
You may hit Control C to kill both the server and the client.
β’ A timed out connection is shown below. The difference between a timed-out connection on how
the connection was blocked or if it was blocked on a different side of the connection.
β’ If you get an error that says βNo route to destinationβ, you have blocked the routing protocol.
Ensure that you do not have a Unspecified Prerequisite Error
16
Repeat this process for every rule you wish to test. If you feel that after some initial testing that your
implementation and ruleset is good, you may then proceed to using the automated test suite.
Part B: Automated Testing Suite
The automated Test Suit was developed by a student in Summer 2021 (htian66) and has been updated to match
the current version of this project. There are two test suites β one that tests your implementation with the
configure.pol you created that matches the rules in Part 5, and the second that is a generic test of your
implementation with a different topology and provided configuration policy. The first test suite will be the exact
same one used in the autograder that will be used after you turn the project into Canvas.
How to test normal cases:
1. Change to the test-scripts directory
2. Copy your `sdn-firewall.py` and `configure.pol` into this directory.
3. Run ./start-firewall.sh configure.pol as usual.
4. Open a new window, run sudo python test_all.py.
5. Total passed cases are calculated. Wrong cases will be displayed. For example, `2: us1 -> hq1 with U at 53,
should be True, current False` means the connection from client us1 to host hq1 using UDP at hq1 53 port is
failed, which should be successful. The first number is the index (0-based) of testcases.
True indicates that a connection was made or was expected. False indicates the opposite condition.
How to test alternte cases:
1. Change to the test-scripts directory
2. Copy your `sdn-firewall.py` file into the alt folder. Change to the alt directory.
3. Run ./start-firewall.sh (you do not need to specify your configure.pol file
4. Open a new window, run sudo python test_all.py in the test-suite/alt folder.
5. Total passed cases are calculated. Wrong cases will be displayed. For example, `2: us1 -> hq1 with U at 53,
should be True, current False` means the connection from client us1 to host hq1 using UDP at hq1 53 port is
failed, which should be successful. The first number is the index (0-based) of testcases.
True indicates that a connection was made or was expected. False indicates the opposite condition.
17
Appendix B: Troubleshooting Information
General Coding Issues
o Watch for type mismatches.
o Do not run βpip3 install poxβ. The pox module installed by pip is not the library used in this project.
o This project is virtually impossible to use the Debug utilities inside of VSCode or Pycharm since it
requires running under the mininet framework. For debugging, it is suggested that you use print
statements in your code to help determine where issues may be occurring.
o You do NOT need to reparse or revalidate any of the data provided in the dictionary other than possibly
changing the type from strings.
o If you use Visual Studio code, add the following to your workspace settings:
“python.autoComplete.extraPaths”: [ “/home/mininet/pox/” ],
Also, with Visual Studio code, it sometimes βrecommendsβ _dl_type and other names prepended with a
_. Note that this is incorrect β the name is dl_type, not _dl_type.
Firewall Implementation (sdn-firewall.py) Errors and Issues
o Pay attention to the Fields ignored due to unspecified prerequisites warning. Do not ignore this message
or your firewall will overblock (i.e., it ignores the field specified). Remember that there are items
required when you specify TCP or UDP, and when you specify IPV4.
o If you get a struct.pack or struct.unpack error message, take a look at
https://github.com/att/pox/blob/7f76c9e3c9bc999fcc97961d408ab0b71cbc186d/pox/OpenFlo
w/libOpenFlow_01.py for more information. Also, the struct.pack error might reference how to fix
(i.e., not an integer, EthAddr(), etc).
o The following error means that you should check your output action: βTypeError: ord() expected string
of length 1, but int foundβ
o If you find that all traffic is blocked, double check the unspecified prerequisite warning.
o Note that you may have issues with certain rules that donβt seem to work when you first start the
firewall. This is typically caused by starting the Spanning Tree. You may run βpingallβ to overcome this
before testing. This can be enabled in the autograder utility.
Mininet/Topology Issues
o On the topology terminal window, if you get an error message that states βUnable to contact Remote
Controllerβ, it means that the POX controller had crashed and normally shows that there is a bug in your
implementation code. Look at the windows where you started the firewall.
o If you get the following error message, please run the cleanup.sh utility: “Exception: Error creating
interface pair (s1-eth0,hq1-eth0): RTNETLINK answers: File existsβ
18
Appendix C: POX API Excerpt
This section contains a highly modified excerpt from the POX Manual (modified to remove extraneous
features not used in this project and to provide clarifications). You should not need to use any other
POX objects for this project. TA Comments are highlighted. Everything on these pages is important to
complete the project.
Excerpted and modified from: https://noxrepo.github.io/pox-doc/html/
Object Definitions:
Flow Modification Object
The main object used for this project is a βFlow Modificationβ object. This adds a rule to the OpenFlow
controller that will affect a modification to the traffic flow based on priority, packet characteristic
matchin, and an action that will be done to the traffic that is matched. IF AN OBJECT is matched, it is
pulled from the network stream and will only be forwarded, modified, or redirected if you do an
action. If you do not specify an action and the packet is matched, the packet will basically be dropped.
The following class descriptor describes the contents of a flow modification object. You need to define
the match, priority, and actions for the object.
class ofp_flow_mod (ofp_header):
def __init__ (self, **kw):
ofp_header.__init__(self)
self.header_type = OFPT_FLOW_MOD
self.match = ofp_match()
self.priority = OFP_DEFAULT_PRIORITY
self.actions = []
Match Structure
OpenFlow defines a match structure β ofp_match β which enables you to define a set of headers for
packets to match against.
The match structure is defined in pox/OpenFlow/libOpenFlow_01.py in class ofp_match. Its attributes
are derived from the members listed in the OpenFlow specification, so refer to that for more
information, though they are summarized in the table below.
You should create a match object and attach it to the flow modification object.
19
Attribute Meaning
dl_src Ethernet/MAC source address (Type of EthAddr)
dl_dst Ethernet/MAC destination address (Type of EthAddr)
dl_type Ethertype / length (e.g. 0x0800 = IPv4) (Type of Integer)
nw_proto IP protocol (e.g., 6 = TCP) or lower 8 bits of ARP opcode (Type of integer)
nw_src IP source NETWORK address (Type of String)
nw_dst IP destination NETWORK address (Type of String)
tp_src TCP/UDP source application port (Type of Integer)
tp_dst TCP/UDP destination application port (Type of Integer)
TA Note: (IMPORTANT_
If you use VSCode or Pycharm, it may make the recommendation to use _dl_src and _dl_dst. These are not
valid. Please use what is specified above.
Attributes may be specified either on a match object or during its initialization. That is, the following
are equivalent:
matchobj = of.ofp_match(tp_src = 5, dl_type = 0x800,dl_dst = EthAddr(“01:02:03:04:05:06”))
#.. or ..
matchobj = of.ofp_match()
matchobj.tp_src = 5
matchobj.dl_type = 0x800
matchobj.dl_dst = EthAddr(“01:02:03:04:05:06”)
IMPORTANT NOTE ABOUT IP ADDRESSES
TA Note: What isnβt very clear by this documentation is that nw_* is expecting a network address. If
you are calling out an IP Address like 10.0.1.1/32, it is an acceptable response to nw_*. However, if you
are calling out a subnet like 10.0.1.0/24, the IP address portion of the response MUST BE the Network
Address.
From Wikipedia: IP addresses are described as consisting of two groups of bits in the address: the most
significant bits are the network prefix, which identifies a whole network or subnet, and the least
significant set forms the host identifier, which specifies a particular interface of a host on that network.
This division is used as the basis of traffic routing between IP networks and for address allocation
policies. (https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing)
Thus for a /24 network, the first 24 bits of the address comprises the network address. Thus, it would
be 10.0.1.0. For a /25 network, there would be two networks in the 10.0.1.x space β a 10.0.1.0/25 and
a 10.0.1.128/25.
20
Your implementation code does NOT need to convert the given IP Address into a network β you can
assume that any given address in a possible configuration file must be valid. However, your
configure.pol file MUST be using the proper form if you are using a CIDR notation other than /32. Why
would you do this? To reduce the number of rules needed. You may use this for the 5th rule from Part
6.
Note that some fields have prerequisites. Basically this means that you canβt specify higher-layer fields
without specifying the corresponding lower-layer fields also. For example, you can not create a match
on a TCP port without also specifying that you wish to match TCP traffic. And in order to match TCP
traffic, you must specify that you wish to match IP traffic. Thus, a match with only tp_dst=80, for
example, is invalid. You must also specify nw_proto=6 (TCP), and dl_type=0x800 (IPv4). If you violate
this, you should get the warning message βFields ignored due to unspecified prerequisitesβ.
This question also presents itself as βWhat does the Fields ignored due to unspecified prerequisites
warning mean?β
Basically this means that you specified some higher-layer field without specifying the corresponding
lower-layer fields also. For example, you may have tried to create a match in which you specified only
tp_dst=80, intending to capture HTTP traffic. You canβt do this. To match TCP port 80, you must also
specify that you intend to match TCP (nw_proto=6). And to match on the TCP protocol, you must also
match on IPV4 type (dl_type=0x800).
OpenFlow Actions
The final aspect needed to fully implement a flow modification object is the action. With this, you
specify what you want done to a port. This can include forwarding, dropping, duplicating and
redirecting, or modify the header parameters. For the purposes of this project, we are only dealing
with forwarding of match traffic to its destination. But please note that for a Software Defined
Network system, you can do all sorts of actions including round robin server, DDOS blocking, and many
other possible options.
Output
Forward packets out of a physical or virtual port. Physical ports are referenced to by their integral
value, while virtual ports have symbolic names. Physical ports should have port numbers less than
0xFF00.
Structure definition:
class ofp_action_output (object):
def __init__ (self, **kw):
self.port = None # Purposely bad — require specification
21
port (int) the output port for this packet. This is a bit misleading because it can confuse you with the
application βportβ for TCP/UDP. For openflow, this port represents the physical swith port that the host is
plugged into. However, you do NOT know which physical port on which switch a host is connected to. Thus, you
will need to use one of the virtual ports to define what you want to happen:
β’ of.OFPP_IN_PORT β This action will send the port back to the sender (i.e., the port it came
into the network on)
β’ of.OFPP_NORMAL – Process the packet and handle via a normal L2/L3 legacy switch
configuration (i.e., send traffic to its destination without modification) β See https://studyccna.com/layer-3-switch/ for information on how normal L2/L3 legacy switches work.
β’ of.OFPP_FLOOD β This action will cause the traffic to be sent out to all ports except the
source (IN_PORT) and any ports that have flooding turned off. This is very chatty and can
be used to do network based attacks (see UDP Amplifications). This should be avoided.
β’ of.OFPP_ALL – output all OpenFlow ports except the source (IN_PORT). This is the same as
FLOOD but it includes ports that have had flood turned off.
β’ of.OFPP_CONTROLLER β This action sends the packet to the switch controller. What
happens with the port depends on the state of the switch controller. Thus it may work, but
also may not work, based on the current state of the switch.
Think carefully about the definitions given above for output actions. Remember that if
you match a packet, no action (i.e., packet will be dropped) will be done unless you
set an output action as the packet is pulled from the stream until it is resolved.
Example: Sending a FlowMod Object
The following example describes how to create a flow modification object including matching a
destination IP Address, IP Type, and Destination IP Port, and setting an action that would redirect the
matching packet out to physical switch port number 4 (note that you generally DO NO KNOW what
physical switch port to use.
rule = of.ofp_flow_mod()
rule.match = of.ofp_match()
rule.match.dl_type = 0x800
rule.match.nw_dst = “192.168.101.101/32”
rule.match.nw_proto = 6
rule.match.tp_dst = 80
rule.priority = 42
rule.actions.append(of.ofp_action_output(port = of.OFPP_????))
Flow Modification Objects work as thus:
1. Packet enters the system and is examined by the Flow Modification Objects (1 for each rule in your
configuration ruleset)
2. The packet will then be examined to see if the different header items match the items specified for
that rule.
22
3. If the packet matches all the applicable items, it is pulled from the stream for you to program an
action for it (forward it, readdress it, change it). If you donβt do an action for it, the package is
essentially dropped. If the packet does not match all the applicable header items, it continues to the
next Flow Modification rule to test it. If it isnβt matched by any rules, it is passed on to the specific
destination.
For this project, you are making a flow modification object and action while using a matching pattern
that can match any or all of the different parameters of the header. Make your implementation
generic.
23
Appendix D: Review of Mininet
Mininet is a network simulator that allows you to explore SDN techniques by allowing you to create a network
topology including virtual switches, links, hosts/nodes, and controllers. It will also allow you to set the
parameters for each of these virtual devices and will allow you to simulate real-world applications on the
different hosts/nodes.
The following code sets up a basic Mininet topology similar to what is used for this project:
#!/usr/bin/python
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import CPULimitedHost, RemoteController
from mininet.util import custom
from mininet.link import TCLink
from mininet.cli import CLI
class FirewallTopo(Topo):
def __init__(self, cpu=.1, bw=10, delay=None, **params):
super(FirewallTopo,self).__init__()
# Host in link configuration
hconfig = {‘cpu’: cpu}
lconfig = {‘bw’: bw, ‘delay’: delay}
# Create the firewall switch
s1 = self.addSwitch(‘s1’)
hq1 = self.addHost(‘hq1′,ip=’10.0.0.1′,mac=’00:00:00:00:00:1e’, **hconfig)
self.addLink(s1,hq1)
us1 = self.addHost( ‘us1′, ip=’10.0.1.1′, mac=’00:00:00:01:00:1e’, **hconfig)
self.addLink(s1,us1)
This code defines the following virtual objects:
β’ Switch s1 β this is a single virtual switch with the label βs1β. In Mininet, you may have as many virtual
ports as you need β for Mininet, βportsβ are considered to be a virtual ethernet jack, not an application
port that you would use in building your firewall.
β’ Hosts hq1 and us1 β these are individual virtual hosts that you can access via xterm and other means.
You can define the IP Address, MAC/Hardware Addresses, and configuration parameters that can define
cpu speed and other parameters using the hconfig dictionary.
β’ Links between s1 and hq1 and s1 and us1 β consider these like an ethernet cable that you would run
between a computer and the switch port. You can define individual port numbers on each side (i.e., port
on the host and port on the virtual switch), but it is advised to let Mininet automatically wire the
network. Like hosts, you can define configuration parameters to set link speed, bandwidth, and latency.
24
REMINDER β PORTS MENTIONED IN MININET TOPOLOGIES ARE WIRING PORTS ON THE VIRTUAL
SWITCH, NOT APPLICATION PORT NUMBERS.
Useful Mininet Commands:
β’ For this project, you can start Mininet and load the firewall topology by running the ./start-topology.sh
from the project directory. You can quit Mininet by typing in the exit command.
β’ After you are done running Mininet, it is recommended that you cleanup Mininet. There are two ways of
doing this. The first is to run the sudo mn -c command from the terminal and the second is to use the
./cleanup.sh script provided in the project directory. Do this after every run to minimize any problems
that might hang or crash Mininet.
β’ You can use the xterm command to start an xterm window for one of the virtual hosts. This command is
run from the mininet> prompt. For example, you can type in us1 xterm & to open a xterm window for
the virtual host us1. The & causes the window to open and run in the background. In this project, you
will run the test-*-client.py and test-*-server.py in each host to test connectivity.
β’ The pingall command that is run from the mininet> prompt will cause all hosts to ping all other hosts.
Note that this may take a long time. To run a ping between two hosts, you can specify host1 ping host2
(for example, us1 ping hq1 which will show the result of host us1 pinging hq1).
β’ The help command will show all Mininet commands and dump will show information about all hosts in
the topology.