CS 6250 Project 3 SDN Firewall with POX solution


Original Work ?


5/5 - (5 votes)


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 function allows you to programmatically control the flow of traffic on the network This project will start with a review of Mininet (this was first used in the optional Simulating Networks project).

This review will explain the basic concepts of Mininet and the functionality you may need to complete this project. There are no deliverables from the Mininet review. The next phase will involve examining network traffic using Wireshark. This will allow you to see the contents of the IP and Protocol Headers that will be important in building the code necessary to implement the firewall as well as the configuration ruleset you will create to test your firewall implementation.

After this, you will need to perform two tasks that need to be conducted in parallel:

1. You will code implementation of a firewall using the POX OpenFlow SDN Frameworks that will block or allow traffic depending on a configuration that you will build in step 2 to implement a firewall.

This code will need to create a Flow Modification Object and will use Actions and Matching to block or allow traffic based on IP/Protocol header packet parameters such as Source IP Address, Destination Port Number, IP Protocol, and Destination MAC Address (there are more parameters, these are given as an example). By default, you will block traffic and then create Allow rules to create a “pinhole” in these block rules.

2. The second step of the project is to create the firewall configuration ruleset that you will use to test your firewall implementation. A series of rules are given in Part 6 that will need to be implemented. In addition, you can create additional rulesets to test your firewall.

Please start early on this project, especially if you are unfamiliar working with Python APIs. 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 – https://erg.abdn.ac.uk/users/gorry/course/inet-pages/ip-packet.html • TCP Packet Header – https://en.wikipedia.org/wiki/Transmission_Control_Protocol • UDP Packet Header – https://en.wikipedia.org/wiki/User_Datagram_Protocol • The ICMP Protocol – https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol 4 • POX Reference Manual – https://noxrepo.github.io/pox-doc/html/ • Flow Modification- https://noxrepo.github.io/pox-doc/html/#OpenFlow-messages • Packet Matching – https://noxrepo.github.io/pox-doc/html/#match-structure • Output Actions – https://noxrepo.github.io/pox-doc/html/#OpenFlow-actions • 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 YouTube Videos Several YouTube videos have been posted concerning this project. These consist of the following: o Broad Overview and Administration – https://youtu.be/zS86rZYIaGQ o Wireshark Walkthrough – https://youtu.be/CR4226dHE5Y o Implementation Walkthrough – https://youtu.be/LhbLETpM5-4 o Ruleset Walkthrough – https://youtu.be/dZ1ol7KEx0I o

Testing Overview

– https://youtu.be/dj323mdA3sg You are highly encouraged to watch these videos to see an overview of how the firewall works and for tips and tricks to successfully complete your implementation. Part 1: Files Layout Unzip the SDNFirewall.zip file into your Virtual Machine.

Do this by running the following command: unzip SDNFirewall.zip This will extract the files for this project into a directory named SDNFirewall at your current path. 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. 5 • 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 is the other deliverable that must be included in your ZIP submission to Canvas. 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. • 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 similar to 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 sdn-firewall.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 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 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 or U for UDP, 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 (IMPORTANT: ONLY USE SOURCE PORTS > 1024 OR YOU WILL DEAL WITH PERMISSIONS ISSUES) Example: python test-client.py T 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 or U for 6 UDP, 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 80 Part 2: Before You Begin This project assumes basic knowledge about IP and TCP/UDP Protocols. It is highly encouraged that you review the following items before starting.

This will help you in understanding the contents of IP packet headers and what you may need to match. o What is the IP (Internet Protocol)? What are the different types of Network Layer protocols? o Review TCP and UDP? How does TCP or UDP differ from IP? o Examine the packet header for a generic IP protocol entry. Contrast that with the packet header for a TCP packet, and for a UDP packet.

What are the differences? What does each field mean? o What constitutes a TCP Connection? How does this contrast with a UDP connection. o A special IP protocol is ICMP. Why is ICMP important? What behavior happens when you do an ICMP Ping? If you block an ICMP response, what would you expect to see? o If you block a host from ICMP, will you be able to send TCP/UDP traffic to it? o Can you explain what happens if you get a ICMP Destination Unreachable response? o What is CIDR notation? How do you subnet a network? o What IP Protocols use Source or Destination Ports?

Part 3: 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__() 7 # 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=’′,mac=’00:00:00:00:00:1e’, **hconfig) self.addLink(s1,hq1) us1 = self.addHost( ‘us1′, ip=’′, 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.

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 8 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 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.

Part 4: 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.

However, Wireshark is NOT currently installed on the VM, so please run the following commands on your VM: sudo apt update sudo apt install wireshark tshark -y When you install, you may get a popup screen that asks whether non-superusers be able to capture packets. Select “” for this. 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.

Part 4a: Wireshark Example Packet Capture

Please watch the video referenced in Part 2 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 startup 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: 9 sudo python ws-topology.py This will startup a Mininet session with all hosts created.

• 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. 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/capture.pcap

This will start tshark and will output a pcap formatted file to /tmp/capture.pcap. 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/capture.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.

• Step 5: Now we need to capture some traffic. Do the following tasks in the appropriate xterm window: in us1 xterm: ping (hit control C after a few ping requests) In us2 xterm: ping (likewise hit control C after a few ping requests) In us1 xterm: python test-server.py T 80 In us2 xterm: python test-client.py T 80 In us1 xterm: press Control C to kill the server In Mininet Terminal: press Control C to stop tshark 10

• 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. DO NOT TURN IN THIS PACKET CAPTURE. You may also close the two xterm windows as they are finished.

• 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.

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. 11 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.

Part 4b: Packet Capture Assignment (packetcapture.pcap)

The deliverable for the Wireshark portion of the assignment is a new packet capture using different hosts and using protocols and ports similar to what you will be using to create your firewall configuration file as described in Part 6. This file will need to be titled packetcapture.pcap and submitted in the ZIP as described in the “What to Turn In” section.

Your packet capture may contain additional data in addition to the steps described below. Your submission will be evaluated to ensure that there is at least one packet captured that describes most of the events below. You will use techniques similar to what was described in Part 4a above – except using different hosts and different ports.

For questions regarding the testclient.py and test-server.py file, you can either start the file without parameters to get instructions or refer to Part 5c below that describes in detail how these utilities are used. You will need to use create xterm windows for hosts hq1, hq2, and cn1. You will capture packets that traverse through host cn1. You can find the IP address for these hosts by examining the ws-topology.py file.

Create a series of traffic as such: • Have host hq1 ping host cn1. • Have host cn1 ping host hq2. • Make cn1 be a server for UDP Port 514 (Syslog) and have host hq1 send a request (client) to cn1. Kill the server when finished capturing the connection. • Make hq2 be a server for TCP Port 22 (SSH) and have cn1 send a request to hq2. Kill the server when finished capturing the connection. • Make hq2 be a server for UDP Port 22 (SSH) and have cn1 send a request to hq2. Kill the server when finished capturing the connection.

Part 5: 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 and the second is the implementation code to create 12 OpenFlow Flow Modification objects that will create the firewall using the parameters given in the firewall configuration file.

Please read this entire section before starting work on it. It is recommended that you write your implementation FIRST using the information that you get from how to build a policy file. Then you should create different versions of a configure.pol file that will help you test your implementation of the firewall code.

Part 5a: Specifications of configure.pol

This file consists of a series of entries that describe each desired firewall rule. A particular final rule may contain any number of implementation rules (or lines) in this file. Each line has the following format: Rule Number, Action, Source MAC, Destination MAC, Source IP, Destination IP, 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 used in the firewall implementation. It can be any value and is NOT validated in setupfirewall.py. DO NOT USE this field to match traffic and DO NOT USE this field to assign priority to rules. 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). o Source / Destination MAC address in form of xx:xx:xx:xx:xx:xx. You may use MAC Addresses to match an individual host. In the real world, you would use to match a particular piece of hardware.

The MAC address of a particular host is defined inside the sdn-topology.py file. o Source / Destination IP Network Address in form of xxx.xxx.xxx.xxx/xx in CIDR notation. You can use this to match either a single IP Address (using it’s IP address and a subnet mask of /32, or a particular Subnet. An entry here would look like:

NOTE: If you are using a CIDR mask other than /32 (individual host), make sure that the IP Address shown is the Network Address. PRIMER ABOUT CIDR NOTATION: An IP Address consists of 32 bits which contains both the network and the host addresses. These 32 bits are divided into 4 sections consisting of 8 bits. The subnet mask 13 /24 defines how many bits of the IP Address define the network. For a /24 network, there are 24 bits defining the network and 8 bits that define the host.

Thus, if you specify, the first 24 bits (the 192.168.10) define the network address, and the 0 specifies the host (255 hosts). The IP specified here must be the network address (for a /24, it must represent the first 24 bits). For the /32 address, the entire 32 bits is a network address and represents a single host. The IP address of a particular host is defined inside the sdn-topology.py file. o Protocol = integer IP protocol number per IANA (0-254).

An example is ICMP is IP Protocol 1, TCP is IP Protocol 6, etc. This must be an integer. 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.

FOR THIS PROJECT, the following link is what will be used for rules checking (Yes and Unofficial only, not Assigned): https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers Also, do not try to use port numbers to differentiate the different elements of the ICMP protocol. o Comment/Note = this is for your use in tracking rules.

Special Notes:

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 to have a ‘-‘. (i.e., a rule like: 1,Block,-,-,-,-,-,-,-,Block the world is valid). DO NOT TRY TO MATCH ANY ITEM USING A “-“. DOING SO MAY CAUSE THE FIREWALL TO CRASH.

o When a rule states to block the world from accessing a particular host, this means that you are matching against all possible hosts which may include hosts that are not in your topology. An example host “other1” has been included to help you test this condition. THE AUTOGRADER WILL USE A DIFFERENT IP ADDRESS FOR THE HOST other1 USED TO TEST WORLD FUNCTIONALITY. DO NOT USE THE IP ADDRESS FOR “WORLD”. THINK ABOUT HOW TO LEVERAGE A “-“ TO MAKE “WORLD” WORK. o Note that a rule does not necessarily need a MAC or IP Address.

Also, it is possible to have a rule that only has network addresses and no ports/protocols. What won’t ever be tested is using a src/dst port WITHOUT an IP Protocol. Read Appendix A for the reasons for this. 14 o What is the difference between source and destination? Source makes a request of the destination. For ports, you will most often use destination ports, but make sure that your firewall implements both source and destination ports.

For IP and MAC addresses, you will use both. 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. NOTE THAT THE AUTOGRADER WILL TEST SCENARIOS THAT INCLUDE COMBINATIONS OF MAC AND IP ADDRESSES.

Example Rules: 1,Block,-,-,,,6,-,80,Block from accessing a web server on the network 2,Allow,-,-,,,6,-,80,Allow to access a web server on overriding previous rule What do these rules do? The first rule basically blocks host hq1 (IP Address from accessing a web server on any host on the us network (the subnet 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 to access a web server running on us5 (IP Address 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 US Network (us1-us5). Subnet India Network (in1-in5). Subnet China Network (cn1-cn5). Subnet UK Network (uk1-uk5). Subnet There is also a single host named other1 that is at This is to be used for testing a “world” scenario. Remember that WORLD indicates any host on the network (the spectrum would be in CIDR notation as the contents for the set WORLD).

THE AUTOGRADER WILL USE A DIFFERENT IP ADDRESS FOR THE WORLD HOST 15 TIP: Do NOT attempt to use a single CIDR notation to represent the 5 corporate networks. Use the /24 for each one when you create your ruleset in Part 6. In Part 6, 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 in order to help you complete Part 5b below.

Part 5b: Implementing the Firewall in Code

For this step, you will be implementing the coding that will convert the entries from the configure.pol file into a rule that POX will understand. Other than basic python syntax, all you need to know to implement the firewall can be found in these three sections of the POX manual (and detailed in Appendix A attached to this documentation): • Flow Modification- https://noxrepo.github.io/pox-doc/html/#OpenFlow-messages • Packet Matching – https://noxrepo.github.io/pox-doc/html/#match-structure • Output Actions – https://noxrepo.github.io/pox-doc/html/#OpenFlow-actions

Your code will need to do the following: o Create a OpenFlow Flow Modification object o Create a POX Packet Matching object that will integrate the elements from the firewall configuration policy that are passed to it using the policy dictionary. o Create a POX Output Action, if needed, to specify what to do with the traffic. 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. Key Information: o policies is a python list that contains one entry for each rule line contained in your configure.pol file. policy is a dictionary that is consists of one line from the policies list. This dictionary has the following keys: o policy[‘mac-src’] = Source MAC Address o policy[‘mac-dst’] = Destination MAC Address o policy[‘ip-src’] = Source IP Address ( o policy[‘ip-src-address’] = Source IP Address part ( o policy[‘ip-src-subnet’] = Source IP Address subnet (32) o policy[‘ip-dst’] = Destination IP Address ( 16 o policy[‘ip-dst-address’] = Destination IP Address part ( o policy[‘ip-dst-subnet’] = Destination IP Address subnet (32) o policy[‘ipprotocol’] = IP Protocol o policy[‘port-src’] = Source Port for TCP/UDP o policy[‘port-dst’] = Destination Port for TCP/UDP o policy[‘rulenum’] = Rule Number o policy[‘comment’] = Comment 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-dstaddress’]/[policy[‘ip-dst-subnet’] in your implementation. o You will need to assume that all traffic is IPV4. o Do not hard code your solution.

By hardcoding, it is meant that you should not be using actual IP/MAC Addresses and Ports in your coding. Use the policy[] dictionary items above to make the implementation generic. Failure to heed this instruction may result in the loss of 1/3 of your points when the project is graded with an alternate topology and/or ruleset. 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. A null (or empty) action also has an effect on the firewall. o Outputting extra print debug lines will not adversely impact the autograder. o You will need to rewrite the rule = None to reference your Flow Modification object. Part 5c: How to Test This is the process to manually test your code while implementing your code and firewall configuration file.

An unofficial testing script that will find most errors will be posted to EdStem that can be used after implementation of the code and the configure.pol file is complete. However, in implementation, the manual process is the way to test. To start out, consider testing the first example rule, which is: 17 1,Block,-,-,,,6,-,80,Block from accessing a web server on the network 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 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 may 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 is where most of the work will occur. At the mininet prompt, you can use all of the standard mininet commands as described in Part 3.

o Step 4: 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 & 18 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, which will then let you discover which xterm belongs to which host.

o Step 5: 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 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 80

This will start up a client that will connect to the TCP Port 80 on the server (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. If traffic should have passed between the two systems, you would see the following (the top xterm is hq1 and in this case, the bottom xterm is us5, which shows the implementation of example rule 2): 19 o Repeat this process to test other combinations.

Make sure to test for conditions that should and should not be blocked. 20 When you are done testing, go to the second terminal and type in quit at the mininet window and then press Control-C on the first terminal window that was used to start the firewall. Part 6: 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). You work for GT-SDN Corporation that has offices in the US, China, India, and the UK, with a US headquarters that acts as the datacenter for the company. Your task is to implement a firewall that accomplishes the following goals:

• Task 1: On the headquarters network, you have two active DNS servers (using both the standard UDP service as well as the newer DNS-over-TLS standard). hq1 provides DNS service to the public (the world) and hq2 provides a private DNS service that should be accessible only to the 5 corporate networks (i.e., the US, China, India, UK, and Headquarters network). Write a series of firewall rules to achieve those rules. (Use UDP Port 53 and UDP Port 853 only)

• Task 2: On the headquarters network, the host hq3 acts as a VPN server that connects to each of the other sites (hosts us3, uk3, in3, and cn3) using the OpenVPN server (standard ports – both TCP and UDP Ports 1194). Create a set of firewall rules that will only allow the 4 offsite hosts (us3, uk3, in3, and cn3) access to the hq3 OpenVPN server.

No other hosts in the world should be able to access the OpenVPN server on hq3.

• Task 3: Allow the hosts on the Headquarters network to be reachable via an ICMP ping from the world (including from the us, uk, in, and cn networks). However, the us, uk, in, and cn networks should not be reachable from the world (due to firewall implementation limitations, the hq network would be able to ping the us, uk, in, and cn network. Why? What changes could be made to the implementation requirements to allow this?)

• Task 4: One of the main routes for ransomware to enter a corporate network is through a remote desktop connection with either an insecure version of a server protocol or via leaked or weak credentials (using either the Microsoft Remote Desktop protocol or the Virtual Networking Computing (VNC) protocols as the remote desktop server).

For this task, write a set of rules that will block the internet from connecting to a remote desktop server on the five corporate networks. Allow the uk, us, 21 in, and cn to connect to a remote desktop server on the headquarters network. (Use TCP Port 3389 for Remote Desktop and TCP Port 5900 for VNC)

• Task 5: The servers located on hosts us3 and us4 run a micro webservice on TCP and UDP Port 8500 that processes financial information. Access to this service should be blocked from hosts uk2, uk3, uk4, uk5, in4, in5, us5, and hq5. (Hint: Note the IP Addresses for these hosts and you may use the smallest subnet mask that handles the listed hosts using CIDR notation).

• Task 6: Block the ingress of the GRE IP Protocol into any of the 5 corporate networks (i.e., the US, China, India, UK, and Headquarters network). GRE is an encapsulation protocol primarily used in PPTP-based VPN connections. You only need to block the IP protocol and not any ports associated to PPTP-based VPN connections.

Part 7: Error Conditions and Helpful Tips and Advice from Past Students

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. o Pay attention to the Fields ignored due to unspecified prerequisites warning. If you see this rule, you have issues with missing prerequisites in your implementation code and that the specified field will be ignored.

See Appendix A Match Structure for a discussion of the prerequisites 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 Priorities do not need to be unique. A higher priority has priority over a lower value. 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” o If you get a struct.pack or struct.unpack error message, take a look at https://github.com/att/pox/blob/7f76c9e3c9bc999fcc97961d408ab0b71cbc186d/pox /OpenFlow/libOpenFlow_01.py for more information. Also, the struct.pack error might reference how to fix (i.e., not an integer, EthAddr(), etc). 22 o A struct.unpack error may be ignored if it occurs on your GRE rule. o It is recommended that you do NOT implement a full block rule like: 1,Block,-,-,-,-,-,-,-, Block Everything This may have unintended consequences (like blocking ARP requests).

o The following error means that you should check your output action: “TypeError: ord() expected string of length 1, but int found” 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 If you use Visual Studio code, add the following to your workspace settings: “python.autoComplete.extraPaths”: [ “/home/mininet/pox/” ], Also “When using rule.match.dl_src (and dl_dst and dl_type), Visual Studio Code would not turn blue unless using rule.match._dl_src. I tested both ways and they passed, but ultimately left it how it looked in the appendix.”

The accepted way is to not use the _ preceding dl_src. What to Turn In You need to submit your copy of packetcapture.pcap, sdn-firewall.py and configure.pol from you project directory using the instructions from the Piazza Post “How to Submit / Zip Our Projects” (#33 for Spring 2021). 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 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. 23 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. Do not post your code in public repositories on Github. Rubric For the Summer 2021 Semester, this project is worth a total of 150 points which is distributed in the following fashion: • 10 points for submitting a version of sdn-firewall.py that indicates effort was done. • 10 points for submitting a version of configure.pol that indicates effort was done. • 30 points for submitting a version of packetcapture.pcap that indicates effort was done. • 35 points – Your configure.pol file will be tested by a known good firewall implementation and by your sdn-firewall.py file with a series of unit tests to make sure that the rules were implemented.

The higher of the two grades will be used (thus, you will not be penalized if your sdn-firewall.py file is not complete or has issues). • 15 points – This is just a test of your provided sdn-firewall.py and configure.pol to ensure that your code is working (i.e., it tests your implementation, and not your ruleset) • 50 points – The final portion of the grade consists of testing your sdn-firewall.py file with a different grading ruleset and/or topology to make sure that your code is robust enough to handle any valid configuration file.

If your code crashes from a simple clerical error, a deduction of 20 points for each edit (that resolves the crash) will be done vs losing most of the 15, 35, or 50 points for your code. 24 Appendix A: 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 from: https://noxrepo.github.io/pox-doc/html/ ofp_flow_mod – Flow table modification class ofp_flow_mod (ofp_header): def __init__ (self, **kw): ofp_header.__init__(self) self.header_type = OFPT_FLOW_MOD if ‘match’ in kw: self.match = None else: self.match = ofp_match() self.cookie = 0 self.priority = OFP_DEFAULT_PRIORITY self.buffer_id = None self.out_port = OFPP_NONE self.flags = 0 self.actions = [] • cookie (int) – identifier for this flow rule. (optional) • priority (int) – the priority at which a rule will match, higher numbers higher priority. Note: Exact matches will have highest priority. In general, you will need to adjust this to make Allow work. Maximum is 32767.

Do NOT use the Rulenum as the priority. • buffer_id (int) – A buffer on the datapath that the new flow will be applied to. Use None for none. Not meaningful for flow deletion. • out_port (int) – This field is used to match for DELETE commands.OFPP_NONE may be used to indicate that there is no restriction. DO NOT USE THIS. • flags (int) – Integer bitfield in which the following flag bits may be set: • OFPFF_SEND_FLOW_REM – Send flow removed message to the controller when rule expires • OFPFF_CHECK_OVERLAP – Check for overlapping entries when installing. If one exists, then an error is send to controller • OFPFF_EMERG – Consider this flow as an emergency flow and only use it when the switch controller connection is down. • actions (list) – actions are defined below, each desired action object is then appended to this list and they are executed in order. • match (ofp_match) – the match structure for the rule to match on (see below). See section of 5.3.3 of OpenFlow 1.0 spec.

This class is defined in pox/OpenFlow/libOpenFlow_01.py. 25 TA Note: In the example below, the self.connection.send(msg) is already done for you in the project. Example: Installing a table entry # Traffic to should be sent out switch port 4 # One thing at a time… rule = of.ofp_flow_mod() rule.priority = 42 rule.match.dl_type = 0x800 rule.match.nw_dst = IPAddr(“”) rule.match.tp_dst = 80 rule.actions.append(of.ofp_action_output(port = 4)) Match Structure OpenFlow defines a match structure – ofp_match – which enables you to define a set of headers for packets to match against.

You can either build a match from scratch, or use a factory method to create one based on an existing packet. 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. ofp_match attributes: Attribute Meaning in_port Switch port number the packet arrived on – NOT APPLICATION PORT!!! dl_src Ethernet/MAC source address (Type of EthAddr) dl_dst Ethernet/MAC destination address (Type of EthAddr) dl_vlan VLAN ID (Type of Integer) dl_vlan_pcp VLAN priority (Type of Integer) dl_type Ethertype / length (e.g. 0x0800 = IPv4) (Type of Integer) nw_tos IP TOS/DS bits (Type of Integer) nw_proto IP protocol (e.g., 6 = TCP) or lower 8 bits of ARP opcode nw_src IP source address (Type of String or IP Address) nw_dst IP destination address (Type of String or IP Address) tp_src TCP/UDP source port (Type of Integer) tp_dst TCP/UDP destination port (Type of Integer)

Attributes may be specified either on a match object or during its initialization. That is, the following are equivalent: 26 my_match = of.ofp_match(tp_src = 5, dl_dst = EthAddr(“01:02:03:04:05:06”)) #.. or .. my_match = of.ofp_match() my_match.tp_src = 5 my_match.dl_dst = EthAddr(“01:02:03:04:05:06”) Partial Matches and Wildcards Unspecified fields are wildcarded and will match any packet. You can explicitly set a field to be wildcarded by setting it to None. Note Info: While the OpenFlow ofp_match structure is defined as having a wildcards attribute, you will probably never need to explicitly set it when using POX – simply don’t assign values to fields you want wildcarded (or set them to None).

LIKEWISE, NEVER TRY TO DO THE FOLLOWING: my.match.nw_src = “-“ IP address fields are a bit trickier, as they can be wildcarded completely like the other fields, but can also be partially wildcarded. This allows you to match entire subnets. There are a number of ways to do this. Here are some equivalent ones: my_match.nw_src = “” my_match.nw_src = (IPAddr(“”), 24) my_match.nw_src = “” my_match.set_nw_src(IPAddr(“”), 24) In particular, note that the nw_src and nw_dst attributes can be ambiguous when working with partial matches – especially when reading a match structure (e.g., as returned in a flow_removed message or flow_stats reply).

To account for this, you may use the unambiguous .get_nw_src(), .set_nw_src(), and the destination equivalents. These return a tuple such as (IPAddr(“”), 24) which includes the number of matched bits – the number that would follow the slash in CIDR-style representation ( 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, it is an acceptable response to nw_*.

However, if you are calling out a subnet like, 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 For a /25 network, there would be two networks in the 10.0.1.x space – a and a

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‘. For more information on this subject, see the FAQ entry “I tried to install a table entry but got a different one. Why?” in the full POX documentation. FAQ Entry: 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 in order to match on TCP, you must also match on IP (dl_type=0x800). Example: Matching Web Traffic As an example, the following code will create a match for traffic to web servers: import pox.OpenFlow.libOpenFlow_01 as of # POX convention import pox.lib.packet as pkt # POX convention my_match = of.ofp_match(dl_type = pkt.ethernet.IP_TYPE, nw_proto = pkt.ipv4.TCP_PROTOCOL, tp_dst = 80) OpenFlow Actions OpenFlow actions are applied to packets that match a rule installed at the datapath. The code snippets found here can be found in libOpenFlow_01.py in pox/OpenFlow.

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: 28 class ofp_action_output (object): def __init__ (self, **kw): self.port = None # Purposely bad — require specification • port (int) the output port for this packet. Value could be an actual port number or one of the following virtual ports expressed as constants: (Actual port number refers to the physical port number on the switch and not the application port) • OFPP_IN_PORT – Send back out the physical switch port the packet was received on. Except possibly OFPP_NORMAL, this is the only way to send a packet back out its incoming port. • OFPP_NORMAL – Process via normal L2/L3 legacy switch configuration (if available – switch dependent)

• OFPP_FLOOD – output all OpenFlow ports except the input port and those with flooding disabled via the OFPPC_NO_FLOOD port config bit (generally, this is done for STP) • OFPP_ALL – output all OpenFlow ports except the in port. • OFPP_CONTROLLER – Send to the controller. • OFPP_LOCAL – Output to local OpenFlow port. • OFPP_NONE – Output to no where. Think carefully about the definitions given above for output actions. Remember that if you match a packet, no action will be done unless you set an output action.

Example: Sending a FlowMod To send a flow mod you must define a match structure (discussed above) and set some flow mod specific parameters as shown here: msg = ofp_flow_mod() msg.match = match msg.actions.append(of.ofp_action_output(port = port)) msg.buffer_id = connection.send(msg) Using the connection variable obtained when the datapath joined, we can send the flowmod to the switch.