blob: 959bb49becea5c2347560101c7d712c10fcc3607 [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001package net.floodlightcontroller.firewall;
2
3import static org.easymock.EasyMock.expect;
4import static org.easymock.EasyMock.replay;
5import static org.easymock.EasyMock.verify;
6
7import java.util.HashMap;
8import java.util.List;
9import java.util.Map;
10
11import net.floodlightcontroller.core.FloodlightContext;
12import net.floodlightcontroller.core.IFloodlightProviderService;
13import net.floodlightcontroller.core.IOFSwitch;
14import net.floodlightcontroller.core.module.FloodlightModuleContext;
15import net.floodlightcontroller.core.module.FloodlightModuleException;
16import net.floodlightcontroller.core.test.MockFloodlightProvider;
17import net.floodlightcontroller.packet.ARP;
18import net.floodlightcontroller.packet.Data;
19import net.floodlightcontroller.packet.Ethernet;
20import net.floodlightcontroller.packet.IPacket;
21import net.floodlightcontroller.packet.IPv4;
22import net.floodlightcontroller.packet.TCP;
23import net.floodlightcontroller.packet.UDP;
24import net.floodlightcontroller.restserver.IRestApiService;
25import net.floodlightcontroller.restserver.RestApiServer;
26import net.floodlightcontroller.routing.IRoutingDecision;
27import net.floodlightcontroller.storage.IStorageSourceService;
28import net.floodlightcontroller.storage.memory.MemoryStorageSource;
29import net.floodlightcontroller.test.FloodlightTestCase;
30import net.floodlightcontroller.util.MACAddress;
31
32import org.easymock.EasyMock;
33import org.junit.Before;
34import org.junit.Test;
35import org.openflow.protocol.OFPacketIn;
36import org.openflow.protocol.OFPacketIn.OFPacketInReason;
37import org.openflow.protocol.OFType;
38import org.openflow.util.HexString;
39
40/**
41 * Unit test for stateless firewall implemented as a Google Summer of Code project.
42 *
43 * @author Amer Tahir
44 */
45public class FirewallTest extends FloodlightTestCase {
46 protected MockFloodlightProvider mockFloodlightProvider;
47 protected FloodlightContext cntx;
48 protected OFPacketIn packetIn;
49 protected IOFSwitch sw;
50 protected IPacket tcpPacket;
51 protected IPacket broadcastARPPacket;
52 protected IPacket ARPReplyPacket;
53 protected IPacket broadcastIPPacket;
54 protected IPacket tcpPacketReply;
55 protected IPacket broadcastMalformedPacket;
56 private Firewall firewall;
57 public static String TestSwitch1DPID = "00:00:00:00:00:00:00:01";
58
59 @Before
60 public void setUp() throws Exception {
61 super.setUp();
62 cntx = new FloodlightContext();
63 mockFloodlightProvider = getMockFloodlightProvider();
64 firewall = new Firewall();
65 IStorageSourceService storageService = new MemoryStorageSource();
66 RestApiServer restApi = new RestApiServer();
67
68 // Mock switches
69 long dpid = HexString.toLong(TestSwitch1DPID);
70 sw = EasyMock.createNiceMock(IOFSwitch.class);
71 expect(sw.getId()).andReturn(dpid).anyTimes();
72 expect(sw.getStringId()).andReturn(TestSwitch1DPID).anyTimes();
73 replay(sw);
74 // Load the switch map
75 Map<Long, IOFSwitch> switches = new HashMap<Long, IOFSwitch>();
76 switches.put(dpid, sw);
77 mockFloodlightProvider.setSwitches(switches);
78
79 FloodlightModuleContext fmc = new FloodlightModuleContext();
80 fmc.addService(IFloodlightProviderService.class,
81 mockFloodlightProvider);
82 fmc.addService(IFirewallService.class, firewall);
83 fmc.addService(IStorageSourceService.class, storageService);
84 fmc.addService(IRestApiService.class, restApi);
85
86 try {
87 restApi.init(fmc);
88 } catch (FloodlightModuleException e) {
89 e.printStackTrace();
90 }
91
92 firewall.init(fmc);
93 firewall.startUp(fmc);
94
95 // Build our test packet
96 this.tcpPacket = new Ethernet()
97 .setDestinationMACAddress("00:11:22:33:44:55")
98 .setSourceMACAddress("00:44:33:22:11:00")
99 .setVlanID((short) 42)
100 .setEtherType(Ethernet.TYPE_IPv4)
101 .setPayload(
102 new IPv4()
103 .setTtl((byte) 128)
104 .setSourceAddress("192.168.1.1")
105 .setDestinationAddress("192.168.1.2")
106 .setPayload(new TCP()
107 .setSourcePort((short) 81)
108 .setDestinationPort((short) 80)
109 .setPayload(new Data(new byte[] {0x01}))));
110
111 // Build a broadcast ARP packet
112 this.broadcastARPPacket = new Ethernet()
113 .setDestinationMACAddress("FF:FF:FF:FF:FF:FF")
114 .setSourceMACAddress("00:44:33:22:11:00")
115 .setVlanID((short) 42)
116 .setEtherType(Ethernet.TYPE_ARP)
117 .setPayload(
118 new ARP()
119 .setHardwareType(ARP.HW_TYPE_ETHERNET)
120 .setProtocolType(ARP.PROTO_TYPE_IP)
121 .setOpCode(ARP.OP_REQUEST)
122 .setHardwareAddressLength((byte)6)
123 .setProtocolAddressLength((byte)4)
124 .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:00"))
125 .setSenderProtocolAddress(IPv4.toIPv4Address("192.168.1.1"))
126 .setTargetHardwareAddress(Ethernet.toMACAddress("00:00:00:00:00:00"))
127 .setTargetProtocolAddress(IPv4.toIPv4Address("192.168.1.2"))
128 .setPayload(new Data(new byte[] {0x01})));
129
130 // Build a ARP packet
131 this.ARPReplyPacket = new Ethernet()
132 .setDestinationMACAddress("00:44:33:22:11:00")
133 .setSourceMACAddress("00:11:22:33:44:55")
134 .setVlanID((short) 42)
135 .setEtherType(Ethernet.TYPE_ARP)
136 .setPayload(
137 new ARP()
138 .setHardwareType(ARP.HW_TYPE_ETHERNET)
139 .setProtocolType(ARP.PROTO_TYPE_IP)
140 .setOpCode(ARP.OP_REQUEST)
141 .setHardwareAddressLength((byte)6)
142 .setProtocolAddressLength((byte)4)
143 .setSenderHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55"))
144 .setSenderProtocolAddress(IPv4.toIPv4Address("192.168.1.2"))
145 .setTargetHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:00"))
146 .setTargetProtocolAddress(IPv4.toIPv4Address("192.168.1.1"))
147 .setPayload(new Data(new byte[] {0x01})));
148
149 // Build a broadcast IP packet
150 this.broadcastIPPacket = new Ethernet()
151 .setDestinationMACAddress("FF:FF:FF:FF:FF:FF")
152 .setSourceMACAddress("00:44:33:22:11:00")
153 .setVlanID((short) 42)
154 .setEtherType(Ethernet.TYPE_IPv4)
155 .setPayload(
156 new IPv4()
157 .setTtl((byte) 128)
158 .setSourceAddress("192.168.1.1")
159 .setDestinationAddress("192.168.1.255")
160 .setPayload(new UDP()
161 .setSourcePort((short) 5000)
162 .setDestinationPort((short) 5001)
163 .setPayload(new Data(new byte[] {0x01}))));
164
165 // Build a malformed broadcast packet
166 this.broadcastMalformedPacket = new Ethernet()
167 .setDestinationMACAddress("FF:FF:FF:FF:FF:FF")
168 .setSourceMACAddress("00:44:33:22:11:00")
169 .setVlanID((short) 42)
170 .setEtherType(Ethernet.TYPE_IPv4)
171 .setPayload(
172 new IPv4()
173 .setTtl((byte) 128)
174 .setSourceAddress("192.168.1.1")
175 .setDestinationAddress("192.168.1.2")
176 .setPayload(new UDP()
177 .setSourcePort((short) 5000)
178 .setDestinationPort((short) 5001)
179 .setPayload(new Data(new byte[] {0x01}))));
180
181 this.tcpPacketReply = new Ethernet()
182 .setDestinationMACAddress("00:44:33:22:11:00")
183 .setSourceMACAddress("00:11:22:33:44:55")
184 .setVlanID((short) 42)
185 .setEtherType(Ethernet.TYPE_IPv4)
186 .setPayload(
187 new IPv4()
188 .setTtl((byte) 128)
189 .setSourceAddress("192.168.1.2")
190 .setDestinationAddress("192.168.1.1")
191 .setPayload(new TCP()
192 .setSourcePort((short) 80)
193 .setDestinationPort((short) 81)
194 .setPayload(new Data(new byte[] {0x02}))));
195 }
196
197 protected void setPacketIn(IPacket packet) {
198 byte[] serializedPacket = packet.serialize();
199 // Build the PacketIn
200 this.packetIn = ((OFPacketIn) mockFloodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_IN))
201 .setBufferId(-1)
202 .setInPort((short) 1)
203 .setPacketData(serializedPacket)
204 .setReason(OFPacketInReason.NO_MATCH)
205 .setTotalLength((short) serializedPacket.length);
206
207 // Add the packet to the context store
208 IFloodlightProviderService.bcStore.
209 put(cntx,
210 IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
211 (Ethernet)packet);
212 }
213
214 @Test
215 public void testNoRules() throws Exception {
216 // enable firewall first
217 firewall.enableFirewall(true);
218 // simulate a packet-in event
219 this.setPacketIn(tcpPacket);
220 firewall.receive(sw, this.packetIn, cntx);
221 verify(sw);
222
223 assertEquals(0, firewall.rules.size());
224
225 IRoutingDecision decision = IRoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
226 // no rules to match, so firewall should deny
227 assertEquals(decision.getRoutingAction(), IRoutingDecision.RoutingAction.DROP);
228 }
229
230 @Test
231 public void testReadRulesFromStorage() throws Exception {
232 // add 2 rules first
233 FirewallRule rule = new FirewallRule();
234 rule.in_port = 2;
235 rule.dl_src = MACAddress.valueOf("00:00:00:00:00:01").toLong();
236 rule.dl_dst = MACAddress.valueOf("00:00:00:00:00:02").toLong();
237 rule.priority = 1;
238 rule.action = FirewallRule.FirewallAction.DENY;
239 firewall.addRule(rule);
240 rule = new FirewallRule();
241 rule.in_port = 3;
242 rule.dl_src = MACAddress.valueOf("00:00:00:00:00:02").toLong();
243 rule.dl_dst = MACAddress.valueOf("00:00:00:00:00:01").toLong();
244 rule.nw_proto = IPv4.PROTOCOL_TCP;
245 rule.wildcard_nw_proto = false;
246 rule.tp_dst = 80;
247 rule.priority = 2;
248 rule.action = FirewallRule.FirewallAction.ALLOW;
249 firewall.addRule(rule);
250
251 List<FirewallRule> rules = firewall.readRulesFromStorage();
252 // verify rule 1
253 FirewallRule r = rules.get(0);
254 assertEquals(r.in_port, 2);
255 assertEquals(r.priority, 1);
256 assertEquals(r.dl_src, MACAddress.valueOf("00:00:00:00:00:01").toLong());
257 assertEquals(r.dl_dst, MACAddress.valueOf("00:00:00:00:00:02").toLong());
258 assertEquals(r.action, FirewallRule.FirewallAction.DENY);
259 // verify rule 2
260 r = rules.get(1);
261 assertEquals(r.in_port, 3);
262 assertEquals(r.priority, 2);
263 assertEquals(r.dl_src, MACAddress.valueOf("00:00:00:00:00:02").toLong());
264 assertEquals(r.dl_dst, MACAddress.valueOf("00:00:00:00:00:01").toLong());
265 assertEquals(r.nw_proto, IPv4.PROTOCOL_TCP);
266 assertEquals(r.tp_dst, 80);
267 assertEquals(r.wildcard_nw_proto, false);
268 assertEquals(r.action, FirewallRule.FirewallAction.ALLOW);
269 }
270
271 @Test
272 public void testRuleInsertionIntoStorage() throws Exception {
273 // add TCP rule
274 FirewallRule rule = new FirewallRule();
275 rule.nw_proto = IPv4.PROTOCOL_TCP;
276 rule.wildcard_nw_proto = false;
277 rule.priority = 1;
278 firewall.addRule(rule);
279
280 List<Map<String, Object>> rulesFromStorage = firewall.getStorageRules();
281 assertEquals(1, rulesFromStorage.size());
282 assertEquals(Integer.parseInt((String)rulesFromStorage.get(0).get("ruleid")), rule.ruleid);
283 }
284
285 @Test
286 public void testRuleDeletion() throws Exception {
287 // add TCP rule
288 FirewallRule rule = new FirewallRule();
289 rule.nw_proto = IPv4.PROTOCOL_TCP;
290 rule.wildcard_nw_proto = false;
291 rule.priority = 1;
292 firewall.addRule(rule);
293 int rid = rule.ruleid;
294
295 List<Map<String, Object>> rulesFromStorage = firewall.getStorageRules();
296 assertEquals(1, rulesFromStorage.size());
297 assertEquals(Integer.parseInt((String)rulesFromStorage.get(0).get("ruleid")), rid);
298
299 // delete rule
300 firewall.deleteRule(rid);
301 rulesFromStorage = firewall.getStorageRules();
302 assertEquals(0, rulesFromStorage.size());
303 }
304
305 @Test
306 public void testFirewallDisabled() throws Exception {
307 // firewall isn't enabled by default
308 // so, it shouldn't make any decision
309
310 // add TCP rule
311 FirewallRule rule = new FirewallRule();
312 rule.nw_proto = IPv4.PROTOCOL_TCP;
313 rule.wildcard_nw_proto = false;
314 rule.priority = 1;
315 firewall.addRule(rule);
316
317 this.setPacketIn(tcpPacket);
318 firewall.receive(sw, this.packetIn, cntx);
319 verify(sw);
320
321 assertEquals(1, firewall.rules.size());
322
323 IRoutingDecision decision = IRoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
324 assertNull(decision);
325 }
326
327 @Test
328 public void testSimpleAllowRule() throws Exception {
329 // enable firewall first
330 firewall.enableFirewall(true);
331
332 // add TCP rule
333 FirewallRule rule = new FirewallRule();
334 rule.dl_type = Ethernet.TYPE_IPv4;
335 rule.wildcard_dl_type = false;
336 rule.nw_proto = IPv4.PROTOCOL_TCP;
337 rule.wildcard_nw_proto = false;
338 // source is IP 192.168.1.2
339 rule.nw_src_prefix = IPv4.toIPv4Address("192.168.1.2");
340 rule.wildcard_nw_src = false;
341 // dest is network 192.168.1.0/24
342 rule.nw_dst_prefix = IPv4.toIPv4Address("192.168.1.0");
343 rule.nw_dst_maskbits = 24;
344 rule.wildcard_nw_dst = false;
345 rule.priority = 1;
346 firewall.addRule(rule);
347
348 // simulate a packet-in events
349
350 this.setPacketIn(tcpPacketReply);
351 firewall.receive(sw, this.packetIn, cntx);
352 verify(sw);
353
354 IRoutingDecision decision = IRoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
355 assertEquals(decision.getRoutingAction(), IRoutingDecision.RoutingAction.FORWARD_OR_FLOOD);
356
357 // clear decision
358 IRoutingDecision.rtStore.remove(cntx, IRoutingDecision.CONTEXT_DECISION);
359
360 this.setPacketIn(tcpPacket);
361 firewall.receive(sw, this.packetIn, cntx);
362 verify(sw);
363
364 decision = IRoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
365 assertEquals(decision.getRoutingAction(), IRoutingDecision.RoutingAction.DROP);
366 }
367
368 @Test
369 public void testOverlappingRules() throws Exception {
370 firewall.enableFirewall(true);
371
372 // add TCP port 80 (destination only) allow rule
373 FirewallRule rule = new FirewallRule();
374 rule.dl_type = Ethernet.TYPE_IPv4;
375 rule.wildcard_dl_type = false;
376 rule.nw_proto = IPv4.PROTOCOL_TCP;
377 rule.wildcard_nw_proto = false;
378 rule.tp_dst = 80;
379 rule.priority = 1;
380 firewall.addRule(rule);
381
382 // add block all rule
383 rule = new FirewallRule();
384 rule.action = FirewallRule.FirewallAction.DENY;
385 rule.priority = 2;
386 firewall.addRule(rule);
387
388 assertEquals(2, firewall.rules.size());
389
390 // packet destined to TCP port 80 - should be allowed
391
392 this.setPacketIn(tcpPacket);
393 firewall.receive(sw, this.packetIn, cntx);
394 verify(sw);
395
396 IRoutingDecision decision = IRoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
397 assertEquals(decision.getRoutingAction(), IRoutingDecision.RoutingAction.FORWARD_OR_FLOOD);
398
399 // clear decision
400 IRoutingDecision.rtStore.remove(cntx, IRoutingDecision.CONTEXT_DECISION);
401
402 // packet destined for port 81 - should be denied
403
404 this.setPacketIn(tcpPacketReply);
405 firewall.receive(sw, this.packetIn, cntx);
406 verify(sw);
407
408 decision = IRoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
409 assertEquals(decision.getRoutingAction(), IRoutingDecision.RoutingAction.DROP);
410 }
411
412 @Test
413 public void testARP() throws Exception {
414 // enable firewall first
415 firewall.enableFirewall(true);
416
417 // no rules inserted so all traffic other than broadcast and ARP-request-broadcast should be blocked
418
419 // simulate an ARP broadcast packet-in event
420
421 this.setPacketIn(broadcastARPPacket);
422 firewall.receive(sw, this.packetIn, cntx);
423 verify(sw);
424
425 // broadcast-ARP traffic should be allowed
426 IRoutingDecision decision = IRoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
427 assertEquals(IRoutingDecision.RoutingAction.MULTICAST, decision.getRoutingAction());
428
429 // clear decision
430 IRoutingDecision.rtStore.remove(cntx, IRoutingDecision.CONTEXT_DECISION);
431
432 // simulate an ARP reply packet-in event
433
434 this.setPacketIn(ARPReplyPacket);
435 firewall.receive(sw, this.packetIn, cntx);
436 verify(sw);
437
438 // ARP reply traffic should be denied
439 decision = IRoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
440 assertEquals(decision.getRoutingAction(), IRoutingDecision.RoutingAction.DROP);
441 }
442
443 @Test
444 public void testIPBroadcast() throws Exception {
445 // enable firewall first
446 firewall.enableFirewall(true);
447
448 // set subnet mask for IP broadcast
449 firewall.setSubnetMask("255.255.255.0");
450
451 // no rules inserted so all traffic other than broadcast and ARP-request-broadcast should be blocked
452
453 // simulate a packet-in event
454
455 this.setPacketIn(broadcastIPPacket);
456 firewall.receive(sw, this.packetIn, cntx);
457 verify(sw);
458
459 // broadcast traffic should be allowed
460 IRoutingDecision decision = IRoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
461 assertEquals(IRoutingDecision.RoutingAction.MULTICAST, decision.getRoutingAction());
462 }
463
464 @Test
465 public void testMalformedIPBroadcast() throws Exception {
466 // enable firewall first
467 firewall.enableFirewall(true);
468
469 // no rules inserted so all traffic other than broadcast and ARP-request-broadcast should be blocked
470
471 // simulate a packet-in event
472
473 this.setPacketIn(broadcastMalformedPacket);
474 firewall.receive(sw, this.packetIn, cntx);
475 verify(sw);
476
477 // malformed broadcast traffic should NOT be allowed
478 IRoutingDecision decision = IRoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
479 assertEquals(decision.getRoutingAction(), IRoutingDecision.RoutingAction.DROP);
480 }
481
482 @Test
483 public void testLayer2Rule() throws Exception {
484 // enable firewall first
485 firewall.enableFirewall(true);
486
487 // add L2 rule
488 FirewallRule rule = new FirewallRule();
489 rule.dl_src = MACAddress.valueOf("00:44:33:22:11:00").toLong();
490 rule.wildcard_dl_src = false;
491 rule.dl_dst = MACAddress.valueOf("00:11:22:33:44:55").toLong();
492 rule.wildcard_dl_dst = false;
493 rule.priority = 1;
494 firewall.addRule(rule);
495
496 // add TCP deny all rule
497 rule = new FirewallRule();
498 rule.nw_proto = IPv4.PROTOCOL_TCP;
499 rule.wildcard_nw_proto = false;
500 rule.priority = 2;
501 rule.action = FirewallRule.FirewallAction.DENY;
502 firewall.addRule(rule);
503
504 // simulate a packet-in event
505
506 this.setPacketIn(tcpPacket);
507 firewall.receive(sw, this.packetIn, cntx);
508 verify(sw);
509
510 IRoutingDecision decision = IRoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
511 assertEquals(decision.getRoutingAction(), IRoutingDecision.RoutingAction.FORWARD_OR_FLOOD);
512 }
513}