46 | | '''Network Setup''' [[BR]] |
47 | | [[Image(OpenFlowAssignment1.png, 50%, nolink)]] [[BR]] |
48 | | Follow instructions in the [http://groups.geni.net/geni/wiki/GENIEducation/SampleAssignments/OpenFlowAssignment/ExerciseLayout/DesignSetup DesignSetup] step to build a firewall experiment topology. The specific host names allocated for your experiment will be different, but the topology will be isomorphic. The host labeled ''left'' in the figure is “behind” the firewall, implemented by the Open vSwitch host labeled ''switch''. The host labeled ''router'' handles IP routing for the firewalled network, and every host on the other side of this router (the host labeled ''right'' being the only example on this topology; you may wish to add others for your testing and experimentation) are “outside” of the firewall. [[BR]] |
49 | | The provided RSpec and the files it installs on the hosts it allocates will configure a complete, working network with an Open vSwitch running on the host labeled ''switch''. The Open vSwitch switch is configured to connect to a controller on localhost (that is, the switch host), but no controller is started; until a controller is started on localhost, the Open vSwitch will act like a normal learning switch, forwarding all packets to the appropriate interface based on MAC address. ''Trema'' is installed in ''/opt/trema-trema-8e97343.'' Once you have implemented your switch, you can simply use this ''Trema'' install to run it and the Open vSwitch will obey its configuration. [[BR]] |
50 | | You can test that the network configured correctly by waiting a few moments after Flack or Omni (or whatever GENI tool you are using) suggests that the sliver is ready, and running ping right from the host allocated for left or vice-versa. Since the fallback switch configuration will act like a normal learning switch, the ping packets should go through. [[BR]] |
51 | | |
52 | | '''Firewall Configuration''' [[BR]] |
53 | | 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: |
54 | | {{{ |
55 | | <ip>[/<netmask>] <port> <ip>[/<netmask>] <port> |
56 | | }}} |
57 | | Items in angle brackets (<>) represent variable values, items in square brackets ([]) represent optional syntax, and unquoted characters (e.g., the slash characters) represent themselves. 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 missing (the IP address for a given subnet is not followed by a slash and an integer), 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]] |
58 | | 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: |
59 | | {{{ |
60 | | any any 192.168.1.1 80 |
61 | | 192.168.1.0/24 any any any |
62 | | }}} |
63 | | 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. [[BR]] |
64 | | 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: |
65 | | {{{ |
66 | | boolean subnet_match(IP subnet, int bits, IP addr) { |
67 | | int32 bitmask = ̃((1 << 32 - bits) - 1); |
68 | | IP addrnet = addr & bitmask; |
69 | | return addrnet ˆ subnet == 0; |
70 | | } |
71 | | }}} |
72 | | Note that rules are not bidirectional; the presence of the first rule in this set does not imply the second: |
73 | | {{{ |
74 | | 192.168.1.0/24 any any any |
75 | | any any 192.168.1.0/24 any |
76 | | }}} |
77 | | This means that the first packet the controller sees that matches a flow causes the flow to be allowed. Packets that would trigger a reply that would be allowed are not necessarily allowed. |
78 | | The name of a firewall configuration file will be provided on the controller command line. To provide an argument to your 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: |
79 | | {{{ |
80 | | trema run ’firewall.rb fw.conf’ |
81 | | }}} |
82 | | You will then find [’firewall.rb’, ’fw.conf’] in ARGV when your controller’s start method is invoked. [[BR]] |
83 | | |
84 | | '''Firewall Semantics''' [[BR]] |
85 | | When an OpenFlow device connects to your controller (that is, you receive a switch_ready controller event), your controller should send it instructions to: |
86 | | - |
87 | | - Pass all packets matching allowed connections to your controller |
88 | | * Drop all other packets [[BR]] |
89 | | 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]] |
90 | | Upon receiving a packet from the OpenFlow device (via a ''OFPT_PACKET_IN'' message), your controller should: |
91 | | - |
92 | | - Ensure that the packet matches a rule in the configuration |
93 | | - Insert a flow match in the OpenFlow device for the complete four-tuple matching the incoming packet |
94 | | - Insert a flow match in the OpenFlow device for the complete four-tuple matching the opposite direction of the same connection |
95 | | - Instruct the OpenFlow device to forward the incoming packet normally (using ''OFPP_NORMAL'') |
96 | | 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]] |
97 | | 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]] |
98 | | |
99 | | '''Limitations of this Approach''' [[BR]] |
100 | | 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]] |
101 | | |
102 | | '''To get you started, you can download an incomplete version of `firewall.rb` from [http://www.gpolab.bbn.com/experiment-support/OpenFlowExampleExperiment/firewall.rb Here], as well as a sample fw.conf [http://www.gpolab.bbn.com/experiment-support/OpenFlowExampleExperiment/fw.conf Here] ''' [[BR]] |
153 | | |
154 | | '''Connection Limiting Semantics''' [[BR]] |
155 | | The extended firewall will perform flow matching as before, with one added check: if the number of existing flows allowed by a given rule exceeds the limit specified in the configuration, a new flow matching that rule will be denied. The number of existing flows matching a given rule is computed as the number of currently active flow matches in the OpenFlow device for that rule. You may wish to look into the ''OFPT_FLOW_REMOVED'' message for help in implementing this. If a connection rule specifies any as the flow limit, no limiting will be performed by the controller. [[BR]] |
156 | | Note that the timeout-based nature of flow removal dictates that small connection limits will be quite limiting. Keep this in mind when testing your firewall! |
157 | | |
158 | | '''Hints: Want to get the complete firewall.rb? ask your instructor or visit here (you need a password to get it), or send an email (the solution code may be full of bugs, feel free to tweak it and report bugs/ask questions)''' [[BR]] |
159 | | You probably want to change the `rule` structure defined in function `add_rule` to add two members: `limit` and `count` to store the maximum number of active flows allowed and the current number of active flows. [[BR]] |
160 | | You should also over-ride the function `flow_removed` to subtract `count` when-ever a flow rule expired. [[BR]] |
161 | | You might also set the idle_timeout to a smaller number (e.g., 30) so that you do not need to wait for too long before they get removed :-) [[BR]] |
162 | | To verify your implementation, use `-P` option in iperf to add TCP flows. |
| 81 | In this OpenFlow controller, I set the idle_timeout to 30 seconds so that you will not need to wait for too long before the connection times out and the flow entries got removed. [[BR]] |
| 82 | I set the maximum number of allowed flows for port 5002 to be 10 in fw.conf. [[BR]] |
| 83 | When the controller is up, run the following on right node: |
| 84 | {{{ |
| 85 | /usr/local/etc/emulab/emulab-iperf -s -p 5002 |
| 86 | }}} |
| 87 | Run the following on the left node to create 10 flows sending to port 5002: |
| 88 | {{{ |
| 89 | /usr/local/etc/emulab/emulab-iperf -c 10.10.11.1 -p 5002 -P 10 |
| 90 | }}} |
| 91 | Iperf would go through since it allows 10 flows to pass. At the same time, the OpenFlow Controller (firewall.rb) should output the following: |
| 92 | {{{ |
| 93 | action=allow, datapath_id=0x2b3861f8b, count=1, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35288, tp_dst = 5002} |
| 94 | action=allow, datapath_id=0x2b3861f8b, count=2, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35289, tp_dst = 5002} |
| 95 | action=allow, datapath_id=0x2b3861f8b, count=3, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35290, tp_dst = 5002} |
| 96 | action=allow, datapath_id=0x2b3861f8b, count=4, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35291, tp_dst = 5002} |
| 97 | action=allow, datapath_id=0x2b3861f8b, count=5, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35292, tp_dst = 5002} |
| 98 | action=allow, datapath_id=0x2b3861f8b, count=6, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35293, tp_dst = 5002} |
| 99 | action=allow, datapath_id=0x2b3861f8b, count=7, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35294, tp_dst = 5002} |
| 100 | action=allow, datapath_id=0x2b3861f8b, count=8, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35295, tp_dst = 5002} |
| 101 | action=allow, datapath_id=0x2b3861f8b, count=9, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35296, tp_dst = 5002} |
| 102 | action=allow, datapath_id=0x2b3861f8b, count=10, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35297, tp_dst = 5002} |
| 103 | }}} |
| 104 | Pay attention to the `count` and `limit` in the output. [[BR]] |
| 105 | now if you quickly do the following on the left node to add another flow before the current 10 flows expire: |
| 106 | {{{ |
| 107 | /usr/local/etc/emulab/emulab-iperf -c 10.10.11.1 -p 5002 |
| 108 | }}} |
| 109 | You may find that iperf will not go through, and the controller outputs the following: |
| 110 | {{{ |
| 111 | action=block, datapath_id=0x2b3861f8b, count=10, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 36399, tp_dst = 5002} |
| 112 | }}} |
| 113 | This indicates that this additional flow is blocked. |
| 114 | Next, if you wait enough time, in our case here, 30 seconds, you will find the controller outputting the following: |
| 115 | {{{ |
| 116 | Flow Entry Expired or Removed!!!!!!!!!!!action=allow, datapath_id=0x2b3861f8b, count=9, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35290, tp_dst = 5002} |
| 117 | Flow Entry Expired or Removed!!!!!!!!!!!action=allow, datapath_id=0x2b3861f8b, count=8, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35293, tp_dst = 5002} |
| 118 | Flow Entry Expired or Removed!!!!!!!!!!!action=allow, datapath_id=0x2b3861f8b, count=7, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35295, tp_dst = 5002} |
| 119 | Flow Entry Expired or Removed!!!!!!!!!!!action=allow, datapath_id=0x2b3861f8b, count=6, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35292, tp_dst = 5002} |
| 120 | Flow Entry Expired or Removed!!!!!!!!!!!action=allow, datapath_id=0x2b3861f8b, count=5, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35291, tp_dst = 5002} |
| 121 | Flow Entry Expired or Removed!!!!!!!!!!!action=allow, datapath_id=0x2b3861f8b, count=4, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35296, tp_dst = 5002} |
| 122 | Flow Entry Expired or Removed!!!!!!!!!!!action=allow, datapath_id=0x2b3861f8b, count=3, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35288, tp_dst = 5002} |
| 123 | Flow Entry Expired or Removed!!!!!!!!!!!action=allow, datapath_id=0x2b3861f8b, count=2, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35289, tp_dst = 5002} |
| 124 | Flow Entry Expired or Removed!!!!!!!!!!!action=allow, datapath_id=0x2b3861f8b, count=1, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35297, tp_dst = 5002} |
| 125 | Flow Entry Expired or Removed!!!!!!!!!!!action=allow, datapath_id=0x2b3861f8b, count=0, limit=10, message={wildcards = 0(none), in_port = 1, dl_src = 00:02:b3:65:d1:2b, dl_dst = 00:03:47:94:c7:fd, dl_vlan = 65535, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 6, nw_src = 10.10.10.1/32, nw_dst = 10.10.11.1/32, tp_src = 35294, tp_dst = 5002} |
| 126 | }}} |
| 127 | This means that the controller caught the flow entry removed event from the OpenFlow switch and it updated the count value for the corresponding rule. [[BR]] |
| 128 | Next, if you try to send a TCP flow using iperf again, it would go through. |