| 92 | |
| 93 | |
| 94 | |
| 95 | '''Firewall Configuration''' [[BR]] |
| 96 | The firewall configuration language is very simple. All flows not specified in the configuration are assumed to be forbidden, and the default packet processing policy on the OpenFlow device you are managing should be to drop packets. The configuration language will specify, one flow to a line, the TCP flows that should be permitted to pass the firewall. The syntax is: |
| 97 | {{{ |
| 98 | <ip>[/<netmask>] <port> <ip>[/<netmask>] <port> |
| 99 | }}} |
| 100 | Items in angle brackets (<>) represent variable values and items in square brackets ([]) represent optional syntax. The first subnet (IP address plus mask length) and port number are the subnet and port number of the host initiating the connection (that is, sending the first bare SYN), and the second subnet and port number are those of the host accepting the connection. IP addresses are specified as dotted quads (e.g., 192.168.1.0) and netmasks as bit lengths (e.g., 24). If a netmask is omitted it is equivalent to /32. Port numbers are integers. Either or both of the IP address or port numbers may be replaced by the word any, equivalent to 0.0.0.0/0 in the case of IP address, or to any port number, in the case of port numbers. [[BR]] |
| 101 | |
| 102 | |
| 103 | A sample configuration that implements a firewall permitting inbound connections to a web server at IP address 192.168.1.1 on port 80, and any outbound connections initiated by hosts inside the firewall (protecting 192.168.1.0/24) is as follows: |
| 104 | {{{ |
| 105 | any any 192.168.1.1 80 |
| 106 | 192.168.1.0/24 any any any |
| 107 | }}} |
| 108 | All whitespace will be a single ASCII space character, all newlines will be a single ASCII newline character (0x0a) Empty lines (two newlines back-to-back) are permitted. |
| 109 | |
| 110 | |
| 111 | A connection is allowable if it matches any rule. A connection matches a rule if all four elements of the four-tuple match. Subnet matching uses standard rules, expressed in this pseudocode: |
| 112 | |
| 113 | {{{ |
| 114 | boolean subnet_match(IP subnet, int bits, IP addr) { |
| 115 | int32 bitmask = ̃((1 << 32 - bits) - 1); |
| 116 | IP addrnet = addr & bitmask; |
| 117 | return addrnet ˆ subnet == 0; |
| 118 | } |
| 119 | }}} |
| 120 | |
| 121 | Note that rules are not bidirectional; the presence of the first rule in this set does not imply the second: |
| 122 | {{{ |
| 123 | 192.168.1.0/24 any any any |
| 124 | any any 192.168.1.0/24 any |
| 125 | }}} |
| 126 | |
| 127 | The name of a firewall configuration file will be provided on the controller command line. To provide an argument to your controller application (in this case we are using 'Trema' as our controller application), it must be included with the controller file name. For example, to configure your firewall found in ''firewall.rb'' to load ''fw.conf'', you would invoke: |
| 128 | {{{ |
| 129 | trema run ’firewall.rb fw.conf’ |
| 130 | }}} |
| 131 | You will then find [’firewall.rb’, ’fw.conf’] in ARGV when your controller’s start method is invoked. [[BR]] |
| 132 | |
| 133 | '''Firewall Semantics''' [[BR]] |
| 134 | |
| 135 | When an OpenFlow device connects to your controller (that is, you receive a switch_ready controller event), your controller should send it instructions to: |
| 136 | - |
| 137 | - |
| 138 | - Pass all packets matching allowed connections to your controller |
| 139 | * Drop all other packets [[BR]] |
| 140 | |
| 141 | Priorities are going to be critical to the correct operation of your controller, so set them carefully. Higher priority rules match before lower priority rules, and the first matching rule is followed. See Section 3.4 of the [http://www.openflow.org/documents/openflow-spec-v1.1.0.pdf OpenFlow specification] for more details on flow matching. [[BR]] |
| 142 | |
| 143 | Upon receiving a packet from the OpenFlow device (via a ''OFPT_PACKET_IN'' message), your controller should: |
| 144 | - |
| 145 | - |
| 146 | - Ensure that the packet matches a rule in the configuration |
| 147 | - Insert a flow match in the OpenFlow device for the complete four-tuple matching the incoming packet |
| 148 | - Insert a flow match in the OpenFlow device for the complete four-tuple matching the opposite direction of the same connection |
| 149 | - Instruct the OpenFlow device to forward the incoming packet normally (using ''OFPP_NORMAL'') |
| 150 | Packets which do not match a rule on the controller should be denied. Because your initial device configuration eliminates most of these packets outright, your controller should not see a large number of these packets. [[BR]] |
| 151 | Because this firewall implementation cannot track the actual state of the TCP connections it is managing, removing accepted connections from the forwarding tables on the OpenFlow device must be handled by timers. OpenFlow rules can be removed by an idle timer as well as expired a fixed period after insertion. For this firewall, use an idle timer of 300 seconds. [[BR]] |
| 152 | |
| 153 | '''Limitations of this Approach''' [[BR]] |
| 154 | Note that this approach to implementing a firewall has drawbacks. Because the OpenFlow controller does not, and can not efficiently, track the precise state of the TCP flow it is forwarding, the rules are a little bit sloppy. In particular, connections “in progress” when the firewall comes online are not differentiated from new connections created after the firewall is initialized, and connection closings can not be detected by the controller. The former can be managed by inspecting the packet headers included in the ''OFPT_PACKET_IN'' message when a connection is opened, but the latter cannot easily be mitigated. This means that connections with long idle times (and 300 s is not particularly unusual, in the long tail of TCP connection statistics!) will be disconnected unnecessarily, and new connections reusing recent four-tuples may be passed through the firewall without examination by the controller. [[BR]] |
| 155 | |
| 156 | '''Exercises and Questions for 3.1''' |
| 157 | |
| 158 | 1. '''Exercise 1:''' fill up the blanks in function `switch_ready` to insert rules into the openflow switch that allow ICMP and ARP packets to go through |
| 159 | 2. '''Exercise 2:''' fill up the blanks in function `packet_in` to insert a flow match in the OpenFlow device that allows the packets (as well as those in the reverse path) that match rules in the fw.conf to pass |
| 160 | 3. '''Exercise 3:''' fill up the blanks in function `packet_in` to insert rules that drops all other packets that does not match the rules specified in fw.conf |
| 161 | |
| 162 | To verify your implementation, run the following on the switch: |
| 163 | {{{ |
| 164 | /opt/trema-trema-8e97343/trema run 'firewall.rb fw.conf' |
| 165 | }}} |
| 166 | Then try to ping from left to right. Ping should go through since you allowed ICMP packets and ARP packets to pass. |
| 167 | |
| 168 | If you are using the fw.conf we provided, try to run a TCP session from left to right using iperf using port 5001, 5002, 5003. |
| 169 | |
| 170 | Since in the fw.conf file we provided, we specifically allow TCP to go through port 5001 and 5002, but not port 5003, you should be able to see that iperf gives back throughput results for port 5001 and 5002 but not 5003. |
| 171 | |
| 172 | Try play with the code as well as the fw.conf file to setup more rules, then verify your setting via iperf or telnet. |
| 173 | |
| 174 | You can check the flow table on the OpenFlow Switch via: |
| 175 | {{{ |
| 176 | sudo /opt/openvswitch-1.6.1-F15/bin/ovs-ofctl dump-flows tcp:127.0.0.1:6634 |
| 177 | }}} |
| 178 | A sample output should be something like the following: |
| 179 | {{{ |
| 180 | NXST_FLOW reply (xid=0x4): |
| 181 | cookie=0x1, duration=165.561s, table=0, n_packets=6, n_bytes=360, idle_age=17,priority=65535,arp actions=NORMAL |
| 182 | cookie=0xa, duration=43.24s, table=0, n_packets=3, n_bytes=222, idle_timeout=300,idle_age=22,priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=00:02:b3:65:d1:2b,dl_dst=00:03:47:94:c7:fd,nw_src=10.10.10.1,nw_dst=10.10.11.1,nw_tos=0,tp_src=46361,tp_dst=5003 actions=drop |
| 183 | cookie=0x5, duration=147.156s, table=0, n_packets=18289, n_bytes=27682198, idle_timeout=300,idle_age=137,priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=00:02:b3:65:d1:2b,dl_dst=00:03:47:94:c7:fd,nw_src=10.10.10.1,nw_dst=10.10.11.1,nw_tos=0,tp_src=33385,tp_dst=5001 actions=NORMAL |
| 184 | cookie=0x9, duration=105.294s, table=0, n_packets=4, n_bytes=296, idle_timeout=300,idle_age=60,priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=00:02:b3:65:d1:2b,dl_dst=00:03:47:94:c7:fd,nw_src=10.10.10.1,nw_dst=10.10.11.1,nw_tos=0,tp_src=46360,tp_dst=5003 actions=drop |
| 185 | cookie=0x7, duration=124.764s, table=0, n_packets=17902, n_bytes=27095256, idle_timeout=300,idle_age=114,priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=00:02:b3:65:d1:2b,dl_dst=00:03:47:94:c7:fd,nw_src=10.10.10.1,nw_dst=10.10.11.1,nw_tos=0,tp_src=57908,tp_dst=5002 actions=NORMAL |
| 186 | cookie=0x3, duration=165.561s, table=0, n_packets=1, n_bytes=74, idle_timeout=300,idle_age=124,priority=65535,tcp,nw_src=10.10.10.0/24,nw_dst=10.10.11.0/24,tp_dst=5002 actions=CONTROLLER:65535 |
| 187 | cookie=0x4, duration=165.561s, table=0, n_packets=1, n_bytes=74, idle_timeout=300,idle_age=147,priority=65535,tcp,nw_src=10.10.10.0/24,nw_dst=10.10.11.0/24,tp_dst=5001 actions=CONTROLLER:65535 |
| 188 | cookie=0x2, duration=165.561s, table=0, n_packets=0, n_bytes=0, idle_age=165,priority=65535,icmp actions=NORMAL |
| 189 | cookie=0x6, duration=147.156s, table=0, n_packets=9387, n_bytes=624254, idle_timeout=300,idle_age=137,priority=65535,tcp,nw_src=10.10.11.1,nw_dst=10.10.10.1,tp_src=5001,tp_dst=33385 actions=NORMAL |
| 190 | cookie=0x8, duration=124.764s, table=0, n_packets=9257, n_bytes=617666, idle_timeout=300,idle_age=114,priority=65535,tcp,nw_src=10.10.11.1,nw_dst=10.10.10.1,tp_src=5002,tp_dst=57908 actions=NORMAL |
| 191 | }}} |