blob: 3e262af37ac7628fbe0e0a652d322264790b1142 [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
2* Copyright 2011, Big Switch Networks, Inc.
3* Originally created by David Erickson, Stanford University
4*
5* Licensed under the Apache License, Version 2.0 (the "License"); you may
6* not use this file except in compliance with the License. You may obtain
7* a copy of the License at
8*
9* http://www.apache.org/licenses/LICENSE-2.0
10*
11* Unless required by applicable law or agreed to in writing, software
12* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14* License for the specific language governing permissions and limitations
15* under the License.
16**/
17
18package net.floodlightcontroller.forwarding;
19
20import static org.easymock.EasyMock.*;
21
22import java.util.ArrayList;
23import java.util.Date;
24import java.util.HashMap;
25import java.util.List;
26import java.util.Map;
27
28import net.floodlightcontroller.core.FloodlightContext;
29import net.floodlightcontroller.core.IFloodlightProviderService;
30import net.floodlightcontroller.core.IOFSwitch;
31import net.floodlightcontroller.core.module.FloodlightModuleContext;
32import net.floodlightcontroller.core.test.MockFloodlightProvider;
33import net.floodlightcontroller.core.test.MockThreadPoolService;
34import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier;
35import net.floodlightcontroller.devicemanager.test.MockDeviceManager;
36import net.floodlightcontroller.counter.CounterStore;
37import net.floodlightcontroller.counter.ICounterStoreService;
38import net.floodlightcontroller.devicemanager.IDevice;
39import net.floodlightcontroller.devicemanager.IDeviceService;
40import net.floodlightcontroller.devicemanager.IEntityClassifierService;
41import net.floodlightcontroller.packet.Data;
42import net.floodlightcontroller.packet.Ethernet;
43import net.floodlightcontroller.packet.IPacket;
44import net.floodlightcontroller.packet.IPv4;
45import net.floodlightcontroller.packet.UDP;
46import net.floodlightcontroller.routing.IRoutingService;
47import net.floodlightcontroller.routing.Route;
48import net.floodlightcontroller.test.FloodlightTestCase;
49import net.floodlightcontroller.threadpool.IThreadPoolService;
50import net.floodlightcontroller.topology.ITopologyListener;
51import net.floodlightcontroller.topology.ITopologyService;
52import net.floodlightcontroller.topology.NodePortTuple;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080053import net.floodlightcontroller.forwarding.Forwarding;
54
55import org.easymock.Capture;
56import org.easymock.CaptureType;
57import org.easymock.EasyMock;
58import org.junit.Test;
59import org.openflow.protocol.OFFeaturesReply;
60import org.openflow.protocol.OFFlowMod;
61import org.openflow.protocol.OFMatch;
62import org.openflow.protocol.OFMessage;
63import org.openflow.protocol.OFPacketIn;
64import org.openflow.protocol.OFPacketOut;
65import org.openflow.protocol.OFPort;
66import org.openflow.protocol.OFType;
67import org.openflow.protocol.OFPacketIn.OFPacketInReason;
68import org.openflow.protocol.action.OFAction;
69import org.openflow.protocol.action.OFActionOutput;
70import org.openflow.util.HexString;
71
72public class ForwardingTest extends FloodlightTestCase {
73 protected MockFloodlightProvider mockFloodlightProvider;
74 protected FloodlightContext cntx;
75 protected MockDeviceManager deviceManager;
76 protected IRoutingService routingEngine;
77 protected Forwarding forwarding;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080078 protected ITopologyService topology;
79 protected MockThreadPoolService threadPool;
80 protected IOFSwitch sw1, sw2;
81 protected OFFeaturesReply swFeatures;
82 protected IDevice srcDevice, dstDevice1, dstDevice2;
83 protected OFPacketIn packetIn;
84 protected OFPacketOut packetOut;
85 protected OFPacketOut packetOutFlooded;
86 protected IPacket testPacket;
87 protected byte[] testPacketSerialized;
88 protected int expected_wildcards;
89 protected Date currentDate;
90
91 @Override
92 public void setUp() throws Exception {
93 super.setUp();
94
95 cntx = new FloodlightContext();
96
97 // Module loader setup
98 /*
99 Collection<Class<? extends IFloodlightModule>> mods = new ArrayList<Class<? extends IFloodlightModule>>();
100 Collection<IFloodlightService> mockedServices = new ArrayList<IFloodlightService>();
101 mods.add(Forwarding.class);
102 routingEngine = createMock(IRoutingService.class);
103 topology = createMock(ITopologyService.class);
104 mockedServices.add(routingEngine);
105 mockedServices.add(topology);
106 FloodlightTestModuleLoader fml = new FloodlightTestModuleLoader();
107 fml.setupModules(mods, mockedServices);
108 mockFloodlightProvider =
109 (MockFloodlightProvider) fml.getModuleByName(MockFloodlightProvider.class);
110 deviceManager =
111 (MockDeviceManager) fml.getModuleByName(MockDeviceManager.class);
112 threadPool =
113 (MockThreadPoolService) fml.getModuleByName(MockThreadPoolService.class);
114 forwarding =
115 (Forwarding) fml.getModuleByName(Forwarding.class);
116 */
117 mockFloodlightProvider = getMockFloodlightProvider();
118 forwarding = new Forwarding();
119 threadPool = new MockThreadPoolService();
120 deviceManager = new MockDeviceManager();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800121 routingEngine = createMock(IRoutingService.class);
122 topology = createMock(ITopologyService.class);
123 DefaultEntityClassifier entityClassifier = new DefaultEntityClassifier();
124
125
126 FloodlightModuleContext fmc = new FloodlightModuleContext();
127 fmc.addService(IFloodlightProviderService.class,
128 mockFloodlightProvider);
129 fmc.addService(IThreadPoolService.class, threadPool);
130 fmc.addService(ITopologyService.class, topology);
131 fmc.addService(IRoutingService.class, routingEngine);
132 fmc.addService(ICounterStoreService.class, new CounterStore());
133 fmc.addService(IDeviceService.class, deviceManager);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800134 fmc.addService(IEntityClassifierService.class, entityClassifier);
135
136 topology.addListener(anyObject(ITopologyListener.class));
137 expectLastCall().anyTimes();
138 replay(topology);
139 threadPool.init(fmc);
140 forwarding.init(fmc);
141 deviceManager.init(fmc);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800142 entityClassifier.init(fmc);
143 threadPool.startUp(fmc);
144 deviceManager.startUp(fmc);
145 forwarding.startUp(fmc);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800146 entityClassifier.startUp(fmc);
147 verify(topology);
148
149 swFeatures = new OFFeaturesReply();
150 swFeatures.setBuffers(1000);
151 // Mock switches
152 sw1 = EasyMock.createMock(IOFSwitch.class);
153 expect(sw1.getId()).andReturn(1L).anyTimes();
154 expect(sw1.getBuffers()).andReturn(swFeatures.getBuffers()).anyTimes();
155 expect(sw1.getStringId())
156 .andReturn(HexString.toHexString(1L)).anyTimes();
157
158 sw2 = EasyMock.createMock(IOFSwitch.class);
159 expect(sw2.getId()).andReturn(2L).anyTimes();
160 expect(sw2.getBuffers()).andReturn(swFeatures.getBuffers()).anyTimes();
161 expect(sw2.getStringId())
162 .andReturn(HexString.toHexString(2L)).anyTimes();
163
164 //fastWilcards mocked as this constant
165 int fastWildcards =
166 OFMatch.OFPFW_IN_PORT |
167 OFMatch.OFPFW_NW_PROTO |
168 OFMatch.OFPFW_TP_SRC |
169 OFMatch.OFPFW_TP_DST |
170 OFMatch.OFPFW_NW_SRC_ALL |
171 OFMatch.OFPFW_NW_DST_ALL |
172 OFMatch.OFPFW_NW_TOS;
173
174 expect(sw1.getAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn((Integer)fastWildcards).anyTimes();
175 expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
176
177 expect(sw2.getAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn((Integer)fastWildcards).anyTimes();
178 expect(sw2.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
179
180 // Load the switch map
181 Map<Long, IOFSwitch> switches = new HashMap<Long, IOFSwitch>();
182 switches.put(1L, sw1);
183 switches.put(2L, sw2);
184 mockFloodlightProvider.setSwitches(switches);
185
186 // Build test packet
187 testPacket = new Ethernet()
188 .setDestinationMACAddress("00:11:22:33:44:55")
189 .setSourceMACAddress("00:44:33:22:11:00")
190 .setEtherType(Ethernet.TYPE_IPv4)
191 .setPayload(
192 new IPv4()
193 .setTtl((byte) 128)
194 .setSourceAddress("192.168.1.1")
195 .setDestinationAddress("192.168.1.2")
196 .setPayload(new UDP()
197 .setSourcePort((short) 5000)
198 .setDestinationPort((short) 5001)
199 .setPayload(new Data(new byte[] {0x01}))));
200
201
202
203 currentDate = new Date();
204
205 // Mock Packet-in
206 testPacketSerialized = testPacket.serialize();
207 packetIn =
208 ((OFPacketIn) mockFloodlightProvider.getOFMessageFactory().
209 getMessage(OFType.PACKET_IN))
210 .setBufferId(-1)
211 .setInPort((short) 1)
212 .setPacketData(testPacketSerialized)
213 .setReason(OFPacketInReason.NO_MATCH)
214 .setTotalLength((short) testPacketSerialized.length);
215
216 // Mock Packet-out
217 packetOut =
218 (OFPacketOut) mockFloodlightProvider.getOFMessageFactory().
219 getMessage(OFType.PACKET_OUT);
220 packetOut.setBufferId(this.packetIn.getBufferId())
221 .setInPort(this.packetIn.getInPort());
222 List<OFAction> poactions = new ArrayList<OFAction>();
223 poactions.add(new OFActionOutput((short) 3, (short) 0xffff));
224 packetOut.setActions(poactions)
225 .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
226 .setPacketData(testPacketSerialized)
227 .setLengthU(OFPacketOut.MINIMUM_LENGTH+
228 packetOut.getActionsLength()+
229 testPacketSerialized.length);
230
231 // Mock Packet-out with OFPP_FLOOD action
232 packetOutFlooded =
233 (OFPacketOut) mockFloodlightProvider.getOFMessageFactory().
234 getMessage(OFType.PACKET_OUT);
235 packetOutFlooded.setBufferId(this.packetIn.getBufferId())
236 .setInPort(this.packetIn.getInPort());
237 poactions = new ArrayList<OFAction>();
238 poactions.add(new OFActionOutput(OFPort.OFPP_FLOOD.getValue(),
239 (short) 0xffff));
240 packetOutFlooded.setActions(poactions)
241 .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
242 .setPacketData(testPacketSerialized)
243 .setLengthU(OFPacketOut.MINIMUM_LENGTH+
244 packetOutFlooded.getActionsLength()+
245 testPacketSerialized.length);
246
247 expected_wildcards = fastWildcards;
248 expected_wildcards &= ~OFMatch.OFPFW_IN_PORT &
249 ~OFMatch.OFPFW_DL_VLAN &
250 ~OFMatch.OFPFW_DL_SRC &
251 ~OFMatch.OFPFW_DL_DST;
252 expected_wildcards &= ~OFMatch.OFPFW_NW_SRC_MASK &
253 ~OFMatch.OFPFW_NW_DST_MASK;
254
255 IFloodlightProviderService.bcStore.
256 put(cntx,
257 IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
258 (Ethernet)testPacket);
259 }
260
261 enum DestDeviceToLearn { NONE, DEVICE1 ,DEVICE2 };
262 public void learnDevices(DestDeviceToLearn destDeviceToLearn) {
263 // Build src and dest devices
264 byte[] dataLayerSource = ((Ethernet)testPacket).getSourceMACAddress();
265 byte[] dataLayerDest =
266 ((Ethernet)testPacket).getDestinationMACAddress();
267 int networkSource =
268 ((IPv4)((Ethernet)testPacket).getPayload()).
269 getSourceAddress();
270 int networkDest =
271 ((IPv4)((Ethernet)testPacket).getPayload()).
272 getDestinationAddress();
273
274 reset(topology);
275 expect(topology.isAttachmentPointPort(1L, (short)1))
276 .andReturn(true)
277 .anyTimes();
278 expect(topology.isAttachmentPointPort(2L, (short)3))
279 .andReturn(true)
280 .anyTimes();
281 expect(topology.isAttachmentPointPort(1L, (short)3))
282 .andReturn(true)
283 .anyTimes();
284 replay(topology);
285
286 srcDevice =
287 deviceManager.learnEntity(Ethernet.toLong(dataLayerSource),
288 null, networkSource,
289 1L, 1);
290 IDeviceService.fcStore. put(cntx,
291 IDeviceService.CONTEXT_SRC_DEVICE,
292 srcDevice);
293 if (destDeviceToLearn == DestDeviceToLearn.DEVICE1) {
294 dstDevice1 =
295 deviceManager.learnEntity(Ethernet.toLong(dataLayerDest),
296 null, networkDest,
297 2L, 3);
298 IDeviceService.fcStore.put(cntx,
299 IDeviceService.CONTEXT_DST_DEVICE,
300 dstDevice1);
301 }
302 if (destDeviceToLearn == DestDeviceToLearn.DEVICE2) {
303 dstDevice2 =
304 deviceManager.learnEntity(Ethernet.toLong(dataLayerDest),
305 null, networkDest,
306 1L, 3);
307 IDeviceService.fcStore.put(cntx,
308 IDeviceService.CONTEXT_DST_DEVICE,
309 dstDevice2);
310 }
311 verify(topology);
312 }
313
314 @Test
315 public void testForwardMultiSwitchPath() throws Exception {
316 learnDevices(DestDeviceToLearn.DEVICE1);
317
318 Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL);
319 Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL);
320 Capture<FloodlightContext> bc1 =
321 new Capture<FloodlightContext>(CaptureType.ALL);
322 Capture<FloodlightContext> bc2 =
323 new Capture<FloodlightContext>(CaptureType.ALL);
324
325
326 Route route = new Route(1L, 2L);
327 List<NodePortTuple> nptList = new ArrayList<NodePortTuple>();
328 nptList.add(new NodePortTuple(1L, (short)1));
329 nptList.add(new NodePortTuple(1L, (short)3));
330 nptList.add(new NodePortTuple(2L, (short)1));
331 nptList.add(new NodePortTuple(2L, (short)3));
332 route.setPath(nptList);
333 expect(routingEngine.getRoute(1L, (short)1, 2L, (short)3)).andReturn(route).atLeastOnce();
334
335 // Expected Flow-mods
336 OFMatch match = new OFMatch();
337 match.loadFromPacket(testPacketSerialized, (short) 1);
338 OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
339 List<OFAction> actions = new ArrayList<OFAction>();
340 actions.add(action);
341
342 OFFlowMod fm1 =
343 (OFFlowMod) mockFloodlightProvider.getOFMessageFactory().
344 getMessage(OFType.FLOW_MOD);
345 fm1.setIdleTimeout((short)5)
346 .setMatch(match.clone()
347 .setWildcards(expected_wildcards))
348 .setActions(actions)
349 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
350 .setCookie(2L << 52)
351 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
352 OFFlowMod fm2 = fm1.clone();
353 ((OFActionOutput)fm2.getActions().get(0)).setPort((short) 3);
354
355 sw1.write(capture(wc1), capture(bc1));
356 expectLastCall().anyTimes();
357 sw2.write(capture(wc2), capture(bc2));
358 expectLastCall().anyTimes();
359
360 reset(topology);
361 expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
362 expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
363 expect(topology.isAttachmentPointPort(1L, (short)1)).andReturn(true).anyTimes();
364 expect(topology.isAttachmentPointPort(2L, (short)3)).andReturn(true).anyTimes();
365 expect(topology.isIncomingBroadcastAllowed(anyLong(), anyShort())).andReturn(true).anyTimes();
366
367 // Reset mocks, trigger the packet in, and validate results
368 replay(sw1, sw2, routingEngine, topology);
369 forwarding.receive(sw1, this.packetIn, cntx);
370 verify(sw1, sw2, routingEngine);
371
372 assertTrue(wc1.hasCaptured()); // wc1 should get packetout + flowmod.
373 assertTrue(wc2.hasCaptured()); // wc2 should be a flowmod.
374
375 List<OFMessage> msglist = wc1.getValues();
376
377 for (OFMessage m: msglist) {
378 if (m instanceof OFFlowMod)
379 assertEquals(fm1, m);
380 else if (m instanceof OFPacketOut)
381 assertEquals(packetOut, m);
382 }
383
384 OFMessage m = wc2.getValue();
385 assert (m instanceof OFFlowMod);
386 assertTrue(m.equals(fm2));
387 }
388
389 @Test
390 public void testForwardSingleSwitchPath() throws Exception {
391 learnDevices(DestDeviceToLearn.DEVICE2);
392
393 Route route = new Route(1L, 1L);
394 route.getPath().add(new NodePortTuple(1L, (short)1));
395 route.getPath().add(new NodePortTuple(1L, (short)3));
396 expect(routingEngine.getRoute(1L, (short)1, 1L, (short)3)).andReturn(route).atLeastOnce();
397
398 // Expected Flow-mods
399 OFMatch match = new OFMatch();
400 match.loadFromPacket(testPacketSerialized, (short) 1);
401 OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
402 List<OFAction> actions = new ArrayList<OFAction>();
403 actions.add(action);
404
405 OFFlowMod fm1 =
406 (OFFlowMod) mockFloodlightProvider.getOFMessageFactory().
407 getMessage(OFType.FLOW_MOD);
408 fm1.setIdleTimeout((short)5)
409 .setMatch(match.clone()
410 .setWildcards(expected_wildcards))
411 .setActions(actions)
412 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
413 .setCookie(2L << 52)
414 .setLengthU(OFFlowMod.MINIMUM_LENGTH +
415 OFActionOutput.MINIMUM_LENGTH);
416
417 // Record expected packet-outs/flow-mods
418 sw1.write(fm1, cntx);
419 sw1.write(packetOut, cntx);
420
421 reset(topology);
422 expect(topology.isIncomingBroadcastAllowed(anyLong(), anyShort())).andReturn(true).anyTimes();
423 expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
424 expect(topology.isAttachmentPointPort(1L, (short)1)).andReturn(true).anyTimes();
425 expect(topology.isAttachmentPointPort(1L, (short)3)).andReturn(true).anyTimes();
426
427 // Reset mocks, trigger the packet in, and validate results
428 replay(sw1, sw2, routingEngine, topology);
429 forwarding.receive(sw1, this.packetIn, cntx);
430 verify(sw1, sw2, routingEngine);
431 }
432
433 @Test
434 public void testFlowModDampening() throws Exception {
435 learnDevices(DestDeviceToLearn.DEVICE2);
436
437 reset(topology);
438 expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort()))
439 .andReturn(true).anyTimes();
440 expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
441 replay(topology);
442
443
444 Route route = new Route(1L, 1L);
445 route.getPath().add(new NodePortTuple(1L, (short)1));
446 route.getPath().add(new NodePortTuple(1L, (short)3));
447 expect(routingEngine.getRoute(1L, (short)1, 1L, (short)3)).andReturn(route).atLeastOnce();
448
449 // Expected Flow-mods
450 OFMatch match = new OFMatch();
451 match.loadFromPacket(testPacketSerialized, (short) 1);
452 OFActionOutput action = new OFActionOutput((short)3, (short)0xffff);
453 List<OFAction> actions = new ArrayList<OFAction>();
454 actions.add(action);
455
456 OFFlowMod fm1 =
457 (OFFlowMod) mockFloodlightProvider.getOFMessageFactory().
458 getMessage(OFType.FLOW_MOD);
459 fm1.setIdleTimeout((short)5)
460 .setMatch(match.clone()
461 .setWildcards(expected_wildcards))
462 .setActions(actions)
463 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
464 .setCookie(2L << 52)
465 .setLengthU(OFFlowMod.MINIMUM_LENGTH +
466 OFActionOutput.MINIMUM_LENGTH);
467
468 // Record expected packet-outs/flow-mods
469 // We will inject the packet_in 3 times and expect 1 flow mod and
470 // 3 packet outs due to flow mod dampening
471 sw1.write(fm1, cntx);
472 expectLastCall().once();
473 sw1.write(packetOut, cntx);
474 expectLastCall().times(3);
475
476 reset(topology);
477 expect(topology.isIncomingBroadcastAllowed(anyLong(), anyShort())).andReturn(true).anyTimes();
478 expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
479 expect(topology.isAttachmentPointPort(1L, (short)1)).andReturn(true).anyTimes();
480 expect(topology.isAttachmentPointPort(1L, (short)3)).andReturn(true).anyTimes();
481
482 // Reset mocks, trigger the packet in, and validate results
483 replay(sw1, routingEngine, topology);
484 forwarding.receive(sw1, this.packetIn, cntx);
485 forwarding.receive(sw1, this.packetIn, cntx);
486 forwarding.receive(sw1, this.packetIn, cntx);
487 verify(sw1, routingEngine);
488 }
489
490 @Test
491 public void testForwardNoPath() throws Exception {
492 learnDevices(DestDeviceToLearn.NONE);
493
494 // Set no destination attachment point or route
495 // expect no Flow-mod but expect the packet to be flooded
496
497 // Reset mocks, trigger the packet in, and validate results
498 reset(topology);
499 expect(topology.isIncomingBroadcastAllowed(1L, (short)1)).andReturn(true).anyTimes();
500 expect(topology.isAttachmentPointPort(EasyMock.anyLong(),
501 EasyMock.anyShort()))
502 .andReturn(true)
503 .anyTimes();
504 expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD))
505 .andReturn(true).anyTimes();
506 sw1.write(packetOutFlooded, cntx);
507 expectLastCall().once();
508 replay(sw1, sw2, routingEngine, topology);
509 forwarding.receive(sw1, this.packetIn, cntx);
510 verify(sw1, sw2, routingEngine);
511 }
512
513}