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