diff --git a/build.xml b/build.xml index 61017da6f4543e6b90f1b95622e0fd304c41c636..b141553d79e18362ce9fc1b1a839bf3ea53c12ec 100644 --- a/build.xml +++ b/build.xml @@ -180,7 +180,7 @@ <batchtest todir="${test-output}"> <fileset dir="${source-test}"> <exclude name="**/storage/tests/StorageTest.java"/> - <exclude name="**/test/Mock*"/> + <include name="**/*Test*.java"/> <exclude name="**/core/test/**"/> <exclude name="**/core/module/**"/> </fileset> diff --git a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java index eb9eca3552d2b4ed5d0fbe944e1dafdfa5828392..20c62bdc2d666b71d024343932faa1da634da00a 100644 --- a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java +++ b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java @@ -152,7 +152,7 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { log.debug("write drop flow-mod sw={} match={} flow-mod={}", new Object[] { sw, match, fm }); } - sw.write(fm, cntx); + messageDamper.write(sw, fm, cntx); } catch (IOException e) { log.error("Failure writing drop flow mod", e); } @@ -309,9 +309,11 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT); List<OFAction> actions = new ArrayList<OFAction>(); if (sw.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)) { - actions.add(new OFActionOutput(OFPort.OFPP_FLOOD.getValue(), (short)0)); + actions.add(new OFActionOutput(OFPort.OFPP_FLOOD.getValue(), + (short)0xFFFF)); } else { - actions.add(new OFActionOutput(OFPort.OFPP_ALL.getValue(), (short)0)); + actions.add(new OFActionOutput(OFPort.OFPP_ALL.getValue(), + (short)0xFFFF)); } po.setActions(actions); po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH); @@ -332,7 +334,7 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { log.trace("Writing flood PacketOut switch={} packet-in={} packet-out={}", new Object[] {sw, pi, po}); } - sw.write(po, cntx); + messageDamper.write(sw, po, cntx); } catch (IOException e) { log.error("Failure writing PacketOut switch={} packet-in={} packet-out={}", new Object[] {sw, pi, po}, e); @@ -396,6 +398,7 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { "properties file."), }) public void init(FloodlightModuleContext context) throws FloodlightModuleException { + super.init(); this.floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class); this.deviceManager = context.getServiceImpl(IDeviceService.class); this.routingEngine = context.getServiceImpl(IRoutingService.class); diff --git a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java index 1a7d69a61b071a7c134a224cb67cf4f85661de90..df6ab7c7e1278652e955a98a5b485c70e9b15aa7 100644 --- a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java +++ b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java @@ -18,6 +18,7 @@ package net.floodlightcontroller.routing; import java.io.IOException; +import java.util.EnumSet; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; @@ -44,6 +45,7 @@ import net.floodlightcontroller.routing.IRoutingDecision; import net.floodlightcontroller.routing.Route; import net.floodlightcontroller.topology.ITopologyService; import net.floodlightcontroller.topology.NodePortTuple; +import net.floodlightcontroller.util.OFMessageDamper; import net.floodlightcontroller.util.TimedCache; import org.openflow.protocol.OFFlowMod; @@ -68,7 +70,10 @@ public abstract class ForwardingBase protected static Logger log = LoggerFactory.getLogger(ForwardingBase.class); - + + protected static int OFMESSAGE_DAMPER_CAPACITY = 50000; // TODO: find sweet spot + protected static int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms + public static short FLOWMOD_DEFAULT_IDLE_TIMEOUT = 5; // in seconds public static short FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite @@ -78,6 +83,8 @@ public abstract class ForwardingBase protected ITopologyService topology; protected ICounterStoreService counterStore; + protected OFMessageDamper messageDamper; + // for broadcast loop suppression protected boolean broadcastCacheFeature = true; public final int prime1 = 2633; // for hash calculation @@ -102,6 +109,16 @@ public abstract class ForwardingBase return d1ClusterId.compareTo(d2ClusterId); } }; + + /** + * init data structures + * + */ + protected void init() { + messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY, + EnumSet.of(OFType.FLOW_MOD), + OFMESSAGE_DAMPER_TIMEOUT); + } /** * Adds a listener for devicemanager and registers for PacketIns. @@ -257,7 +274,7 @@ public abstract class ForwardingBase fm.getMatch().getInputPort(), outPort }); } - sw.write(fm, cntx); + messageDamper.write(sw, fm, cntx); if (doFlush) { sw.flush(); } @@ -365,7 +382,7 @@ public abstract class ForwardingBase try { counterStore.updatePktOutFMCounterStore(sw, po); - sw.write(po, cntx); + messageDamper.write(sw, po, cntx); } catch (IOException e) { log.error("Failure writing packet out", e); } @@ -448,7 +465,7 @@ public abstract class ForwardingBase try { counterStore.updatePktOutFMCounterStore(sw, po); - sw.write(po, cntx); + messageDamper.write(sw, po, cntx); } catch (IOException e) { log.error("Failure writing packet out", e); } @@ -505,7 +522,7 @@ public abstract class ForwardingBase "interfaces={} packet-out={}", new Object[] {sw.getId(), outPorts, po}); } - sw.write(po, cntx); + messageDamper.write(sw, po, cntx); } catch (IOException e) { log.error("Failure writing packet out", e); @@ -624,6 +641,7 @@ public abstract class ForwardingBase try { log.debug("write drop flow-mod sw={} match={} flow-mod={}", new Object[] { sw, match, fm }); + // TODO: can't use the message damper sine this method is static sw.write(fm, null); } catch (IOException e) { log.error("Failure writing deny flow mod", e); diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java index ec1fbb3ce3a9417f0845391207ab5a43dc0b35d9..5460ea3a91f42c22065ecaca793ba8b23ece87f8 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java @@ -80,4 +80,12 @@ public class MockDevice extends Device { } return vals.toArray(new SwitchPort[vals.size()]); } + + @Override + public String toString() { + String rv = "MockDevice[entities=+"; + rv += entities.toString(); + rv += "]"; + return rv; + } } diff --git a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java index 4f474da897c47413da975f05a8711d5bb9482e95..ef6a2b0535ee0471fffc01a606bb04f0f72829d3 100644 --- a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java +++ b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java @@ -17,15 +17,7 @@ package net.floodlightcontroller.forwarding; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.expectLastCall; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.reset; -import static org.easymock.EasyMock.verify; -import static org.easymock.EasyMock.capture; -import static org.easymock.EasyMock.anyLong; -import static org.easymock.EasyMock.anyShort; +import static org.easymock.EasyMock.*; import java.util.ArrayList; import java.util.Date; @@ -55,6 +47,7 @@ import net.floodlightcontroller.routing.IRoutingService; import net.floodlightcontroller.routing.Route; import net.floodlightcontroller.test.FloodlightTestCase; import net.floodlightcontroller.threadpool.IThreadPoolService; +import net.floodlightcontroller.topology.ITopologyListener; import net.floodlightcontroller.topology.ITopologyService; import net.floodlightcontroller.topology.NodePortTuple; import net.floodlightcontroller.flowcache.FlowReconcileManager; @@ -71,10 +64,12 @@ import org.openflow.protocol.OFMatch; import org.openflow.protocol.OFMessage; import org.openflow.protocol.OFPacketIn; import org.openflow.protocol.OFPacketOut; +import org.openflow.protocol.OFPort; import org.openflow.protocol.OFType; import org.openflow.protocol.OFPacketIn.OFPacketInReason; import org.openflow.protocol.action.OFAction; import org.openflow.protocol.action.OFActionOutput; +import org.openflow.util.HexString; public class ForwardingTest extends FloodlightTestCase { protected MockFloodlightProvider mockFloodlightProvider; @@ -90,6 +85,7 @@ public class ForwardingTest extends FloodlightTestCase { protected IDevice srcDevice, dstDevice1, dstDevice2; protected OFPacketIn packetIn; protected OFPacketOut packetOut; + protected OFPacketOut packetOutFlooded; protected IPacket testPacket; protected byte[] testPacketSerialized; protected int expected_wildcards; @@ -142,6 +138,9 @@ public class ForwardingTest extends FloodlightTestCase { fmc.addService(IFlowReconcileService.class, flowReconcileMgr); fmc.addService(IEntityClassifierService.class, entityClassifier); + topology.addListener(anyObject(ITopologyListener.class)); + expectLastCall().anyTimes(); + replay(topology); threadPool.init(fmc); forwarding.init(fmc); deviceManager.init(fmc); @@ -152,19 +151,22 @@ public class ForwardingTest extends FloodlightTestCase { forwarding.startUp(fmc); flowReconcileMgr.startUp(fmc); entityClassifier.startUp(fmc); + verify(topology); swFeatures = new OFFeaturesReply(); swFeatures.setBuffers(1000); // Mock switches - sw1 = EasyMock.createNiceMock(IOFSwitch.class); + sw1 = EasyMock.createMock(IOFSwitch.class); expect(sw1.getId()).andReturn(1L).anyTimes(); expect(sw1.getFeaturesReply()).andReturn(swFeatures).anyTimes(); - expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes(); + expect(sw1.getStringId()) + .andReturn(HexString.toHexString(1L)).anyTimes(); - sw2 = EasyMock.createNiceMock(IOFSwitch.class); + sw2 = EasyMock.createMock(IOFSwitch.class); expect(sw2.getId()).andReturn(2L).anyTimes(); expect(sw2.getFeaturesReply()).andReturn(swFeatures).anyTimes(); - expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes(); + expect(sw2.getStringId()) + .andReturn(HexString.toHexString(2L)).anyTimes(); //fastWilcards mocked as this constant int fastWildcards = @@ -203,34 +205,9 @@ public class ForwardingTest extends FloodlightTestCase { .setDestinationPort((short) 5001) .setPayload(new Data(new byte[] {0x01})))); - // Build src and dest devices - byte[] dataLayerSource = ((Ethernet)testPacket).getSourceMACAddress(); - byte[] dataLayerDest = - ((Ethernet)testPacket).getDestinationMACAddress(); - int networkSource = - ((IPv4)((Ethernet)testPacket).getPayload()). - getSourceAddress(); - int networkDest = - ((IPv4)((Ethernet)testPacket).getPayload()). - getDestinationAddress(); - currentDate = new Date(); - expect(topology.isAttachmentPointPort(1L, (short)1)) - .andReturn(true) - .anyTimes(); - expect(topology.isAttachmentPointPort(2L, (short)3)) - .andReturn(true) - .anyTimes(); - replay(topology); - srcDevice = - deviceManager.learnEntity(Ethernet.toLong(dataLayerSource), - null, networkSource, - 1L, 1); - dstDevice1 = - deviceManager.learnEntity(Ethernet.toLong(dataLayerDest), - null, networkDest, - 2L, 3); + currentDate = new Date(); // Mock Packet-in testPacketSerialized = testPacket.serialize(); @@ -257,6 +234,22 @@ public class ForwardingTest extends FloodlightTestCase { .setLengthU(OFPacketOut.MINIMUM_LENGTH+ packetOut.getActionsLength()+ testPacketSerialized.length); + + // Mock Packet-out with OFPP_FLOOD action + packetOutFlooded = + (OFPacketOut) mockFloodlightProvider.getOFMessageFactory(). + getMessage(OFType.PACKET_OUT); + packetOutFlooded.setBufferId(this.packetIn.getBufferId()) + .setInPort(this.packetIn.getInPort()); + poactions = new ArrayList<OFAction>(); + poactions.add(new OFActionOutput(OFPort.OFPP_FLOOD.getValue(), + (short) 0xffff)); + packetOutFlooded.setActions(poactions) + .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH) + .setPacketData(testPacketSerialized) + .setLengthU(OFPacketOut.MINIMUM_LENGTH+ + packetOutFlooded.getActionsLength()+ + testPacketSerialized.length); expected_wildcards = fastWildcards; expected_wildcards &= ~OFMatch.OFPFW_IN_PORT & @@ -266,19 +259,68 @@ public class ForwardingTest extends FloodlightTestCase { expected_wildcards &= ~OFMatch.OFPFW_NW_SRC_MASK & ~OFMatch.OFPFW_NW_DST_MASK; - // Add the packet to the context store IFloodlightProviderService.bcStore. put(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD, (Ethernet)testPacket); - IDeviceService.fcStore. - put(cntx, - IDeviceService.CONTEXT_SRC_DEVICE, - srcDevice); + } + + enum DestDeviceToLearn { NONE, DEVICE1 ,DEVICE2 }; + public void learnDevices(DestDeviceToLearn destDeviceToLearn) { + // Build src and dest devices + byte[] dataLayerSource = ((Ethernet)testPacket).getSourceMACAddress(); + byte[] dataLayerDest = + ((Ethernet)testPacket).getDestinationMACAddress(); + int networkSource = + ((IPv4)((Ethernet)testPacket).getPayload()). + getSourceAddress(); + int networkDest = + ((IPv4)((Ethernet)testPacket).getPayload()). + getDestinationAddress(); + + reset(topology); + expect(topology.isAttachmentPointPort(1L, (short)1)) + .andReturn(true) + .anyTimes(); + expect(topology.isAttachmentPointPort(2L, (short)3)) + .andReturn(true) + .anyTimes(); + expect(topology.isAttachmentPointPort(1L, (short)3)) + .andReturn(true) + .anyTimes(); + replay(topology); + + srcDevice = + deviceManager.learnEntity(Ethernet.toLong(dataLayerSource), + null, networkSource, + 1L, 1); + IDeviceService.fcStore. put(cntx, + IDeviceService.CONTEXT_SRC_DEVICE, + srcDevice); + if (destDeviceToLearn == DestDeviceToLearn.DEVICE1) { + dstDevice1 = + deviceManager.learnEntity(Ethernet.toLong(dataLayerDest), + null, networkDest, + 2L, 3); + IDeviceService.fcStore.put(cntx, + IDeviceService.CONTEXT_DST_DEVICE, + dstDevice1); + } + if (destDeviceToLearn == DestDeviceToLearn.DEVICE2) { + dstDevice2 = + deviceManager.learnEntity(Ethernet.toLong(dataLayerDest), + null, networkDest, + 1L, 3); + IDeviceService.fcStore.put(cntx, + IDeviceService.CONTEXT_DST_DEVICE, + dstDevice2); + } + verify(topology); } @Test public void testForwardMultiSwitchPath() throws Exception { + learnDevices(DestDeviceToLearn.DEVICE1); Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL); Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL); @@ -287,11 +329,6 @@ public class ForwardingTest extends FloodlightTestCase { Capture<FloodlightContext> bc2 = new Capture<FloodlightContext>(CaptureType.ALL); - // Set destination as sw2 and Mock route - IDeviceService.fcStore. - put(cntx, - IDeviceService.CONTEXT_DST_DEVICE, - dstDevice1); Route route = new Route(1L, 2L); List<NodePortTuple> nptList = new ArrayList<NodePortTuple>(); @@ -358,38 +395,8 @@ public class ForwardingTest extends FloodlightTestCase { @Test public void testForwardSingleSwitchPath() throws Exception { - // Set destination as local and Mock route - byte[] dataLayerSource = ((Ethernet)testPacket).getSourceMACAddress(); - byte[] dataLayerDest = - ((Ethernet)testPacket).getDestinationMACAddress(); - int networkSource = - ((IPv4)((Ethernet)testPacket).getPayload()). - getSourceAddress(); - int networkDest = - ((IPv4)((Ethernet)testPacket).getPayload()). - getDestinationAddress(); - deviceManager.startUp(null); - - reset(topology); - expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())) - .andReturn(true).anyTimes(); - expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes(); - replay(topology); - - srcDevice = - deviceManager.learnEntity(Ethernet.toLong(dataLayerSource), - null, networkSource, - 1L, 1); - dstDevice2 = - deviceManager.learnEntity(Ethernet.toLong(dataLayerDest), - null, networkDest, - 1L, 3); + learnDevices(DestDeviceToLearn.DEVICE2); - IDeviceService.fcStore. - put(cntx, - IDeviceService.CONTEXT_DST_DEVICE, - dstDevice2); - Route route = new Route(1L, 1L); route.getPath().add(new NodePortTuple(1L, (short)1)); route.getPath().add(new NodePortTuple(1L, (short)3)); @@ -430,11 +437,67 @@ public class ForwardingTest extends FloodlightTestCase { verify(sw1, sw2, routingEngine); } + @Test + public void testFlowModDampening() throws Exception { + learnDevices(DestDeviceToLearn.DEVICE2); + + reset(topology); + expect(topology.isAttachmentPointPort(EasyMock.anyLong(), EasyMock.anyShort())) + .andReturn(true).anyTimes(); + expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes(); + replay(topology); + + + Route route = new Route(1L, 1L); + route.getPath().add(new NodePortTuple(1L, (short)1)); + route.getPath().add(new NodePortTuple(1L, (short)3)); + expect(routingEngine.getRoute(1L, (short)1, 1L, (short)3)).andReturn(route).atLeastOnce(); + + // Expected Flow-mods + OFMatch match = new OFMatch(); + match.loadFromPacket(testPacketSerialized, (short) 1); + OFActionOutput action = new OFActionOutput((short)3, (short)0xffff); + List<OFAction> actions = new ArrayList<OFAction>(); + actions.add(action); + + OFFlowMod fm1 = + (OFFlowMod) mockFloodlightProvider.getOFMessageFactory(). + getMessage(OFType.FLOW_MOD); + fm1.setIdleTimeout((short)5) + .setMatch(match.clone() + .setWildcards(expected_wildcards)) + .setActions(actions) + .setBufferId(OFPacketOut.BUFFER_ID_NONE) + .setCookie(2L << 52) + .setLengthU(OFFlowMod.MINIMUM_LENGTH + + OFActionOutput.MINIMUM_LENGTH); + + // Record expected packet-outs/flow-mods + sw1.write(fm1, cntx); + expectLastCall().once(); + sw1.write(packetOut, cntx); + expectLastCall().times(3); + + reset(topology); + expect(topology.isIncomingBroadcastAllowed(anyLong(), anyShort())).andReturn(true).anyTimes(); + expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes(); + expect(topology.isAttachmentPointPort(1L, (short)1)).andReturn(true).anyTimes(); + expect(topology.isAttachmentPointPort(1L, (short)3)).andReturn(true).anyTimes(); + + // Reset mocks, trigger the packet in, and validate results + replay(sw1, routingEngine, topology); + forwarding.receive(sw1, this.packetIn, cntx); + forwarding.receive(sw1, this.packetIn, cntx); + forwarding.receive(sw1, this.packetIn, cntx); + verify(sw1, routingEngine); + } + @Test public void testForwardNoPath() throws Exception { + learnDevices(DestDeviceToLearn.NONE); // Set no destination attachment point or route - // expect no Flow-mod or packet out + // expect no Flow-mod but expect the packet to be flooded // Reset mocks, trigger the packet in, and validate results reset(topology); @@ -443,6 +506,10 @@ public class ForwardingTest extends FloodlightTestCase { EasyMock.anyShort())) .andReturn(true) .anyTimes(); + expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)) + .andReturn(true).anyTimes(); + sw1.write(packetOutFlooded, cntx); + expectLastCall().once(); replay(sw1, sw2, routingEngine, topology); forwarding.receive(sw1, this.packetIn, cntx); verify(sw1, sw2, routingEngine);