From 5a6e71953da4c39003d32f31bae0329d780af0d3 Mon Sep 17 00:00:00 2001 From: Alex Reimers <alex@bigswitch.com> Date: Thu, 5 Jan 2012 15:22:31 -0800 Subject: [PATCH] Enable Forwarding. This replaces LearningSwitch and uses L2 shortest path routing to forward Packets. [#21940749] --- .gitignore | 1 + .../core/internal/Controller.java | 64 ++++-- .../forwarding/Forwarding.java | 197 +++++++++++++----- .../routing/ForwardingBase.java | 23 +- .../routing/IRoutingDecision.java | 7 +- 5 files changed, 204 insertions(+), 88 deletions(-) diff --git a/.gitignore b/.gitignore index d1d52f8fd..291274fb4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ .classpath .project .settings +.DS_Store target thrift diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java index 57a9d847c..78f8969fb 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java +++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java @@ -63,10 +63,12 @@ import static net.floodlightcontroller.counter.CounterValue.CounterType; import net.floodlightcontroller.counter.CounterStore; import net.floodlightcontroller.counter.ICounter; import net.floodlightcontroller.counter.CounterStore.NetworkLayer; +import net.floodlightcontroller.devicemanager.IDeviceManagerAware; import net.floodlightcontroller.devicemanager.internal.DeviceManagerImpl; -import net.floodlightcontroller.learningswitch.LearningSwitch; +import net.floodlightcontroller.forwarding.Forwarding; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPv4; +import net.floodlightcontroller.routing.dijkstra.RoutingImpl; import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher; import net.floodlightcontroller.perfmon.PktinProcessingTime; import net.floodlightcontroller.storage.IResultSet; @@ -165,7 +167,8 @@ public class Controller protected IStorageSource storageSource; protected TopologyImpl topology; protected DeviceManagerImpl deviceManager; - protected LearningSwitch learningSwitch; + protected RoutingImpl routingEngine; + protected Forwarding forwarding; protected OFMessageFilterManager messageFilterManager; protected PktinProcessingTime pktinProcTime; private StaticFlowEntryPusher staticFlowEntryPusher; @@ -1393,9 +1396,7 @@ public class Controller topology = new TopologyImpl(); deviceManager = new DeviceManagerImpl(); storageSource = new MemoryStorageSource(); - learningSwitch = new LearningSwitch(); counterStore = new CounterStore(); - messageFilterManager = new OFMessageFilterManager(); pktinProcTime = new PktinProcessingTime(); topology.setFloodlightProvider(this); @@ -1405,26 +1406,49 @@ public class Controller deviceManager.setStorageSource(storageSource); deviceManager.setTopology(topology); - messageFilterManager.init(this); - - staticFlowEntryPusher = new StaticFlowEntryPusher(); - staticFlowEntryPusher.setFloodlightProvider(this); - - learningSwitch.setFloodlightProvider(this); - learningSwitch.setCounterStore(counterStore); + initMessageFilterManager(); + initStaticFlowPusher(); + routingEngine = new RoutingImpl(); + initForwarding(); // call this explicitly because it does setup this.setStorageSource(storageSource); HashSet<ITopologyAware> topologyAware = new HashSet<ITopologyAware>(); topologyAware.add(deviceManager); + topologyAware.add(routingEngine); topology.setTopologyAware(topologyAware); + topology.setRoutingEngine(routingEngine); + HashSet<IDeviceManagerAware> dmAware = + new HashSet<IDeviceManagerAware>(); + dmAware.add(forwarding); + deviceManager.setDeviceManagerAware(dmAware); + restlets.add(new CoreWebRoutable()); restlets.add(new StorageWebRoutable()); JacksonCustomConverter.replaceConverter(); } + protected void initMessageFilterManager() { + messageFilterManager = new OFMessageFilterManager(); + messageFilterManager.init(this); + } + + protected void initStaticFlowPusher() { + staticFlowEntryPusher = new StaticFlowEntryPusher(); + staticFlowEntryPusher.setFloodlightProvider(this); + } + + protected void initForwarding() { + forwarding = new Forwarding(); + forwarding.setFloodlightProvider(this); + forwarding.setCounterStore(counterStore); + forwarding.setDeviceManager(deviceManager); + forwarding.setRoutingEngine(routingEngine); + forwarding.setTopology(topology); + } + /** * Initialize the rest context */ @@ -1434,10 +1458,12 @@ public class Controller context.getAttributes().put("storageSource", storageSource); context.getAttributes().put("deviceManager", deviceManager); context.getAttributes().put("messageFilterManager", - messageFilterManager); + messageFilterManager); context.getAttributes().put("pktinProcessingTime", pktinProcTime); - context.getAttributes().put("staticFlowEntryPusher", - staticFlowEntryPusher); + if (staticFlowEntryPusher != null) { + context.getAttributes().put("staticFlowEntryPusher", + staticFlowEntryPusher); + } context.getAttributes().put("topology", topology); } @@ -1460,19 +1486,19 @@ public class Controller // no need to do storageSource.startUp() log.debug("Starting counterStore service"); counterStore.startUp(); - log.debug("Starting learningSwitch service"); - learningSwitch.startUp(); - + log.debug("Starting routingEngine service"); + routingEngine.startUp(); + log.debug("Starting forwarding service"); + forwarding.startUp(); log.debug("Starting messageFilter service"); messageFilterManager.startUp(); - log.debug("Starting staticFlowEntryPusher service"); staticFlowEntryPusher.startUp(); } /** * Main function entry point; override init() for adding modules - * @param args + * @param args Command line arguments */ public static void main(String args[]) throws Exception { diff --git a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java index 33fc27065..b6322e2f2 100644 --- a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java +++ b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java @@ -18,19 +18,29 @@ package net.floodlightcontroller.forwarding; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + import net.floodlightcontroller.core.FloodlightContext; +import net.floodlightcontroller.core.IFloodlightProvider; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.devicemanager.Device; import net.floodlightcontroller.devicemanager.DeviceAttachmentPoint; +import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.routing.ForwardingBase; import net.floodlightcontroller.routing.IRoutingDecision; import net.floodlightcontroller.routing.Route; +import net.floodlightcontroller.topology.LinkInfo; import net.floodlightcontroller.topology.SwitchPortTuple; import org.openflow.protocol.OFMatch; import org.openflow.protocol.OFPacketIn; import org.openflow.protocol.OFPacketOut; -import org.openflow.util.HexString; +import org.openflow.protocol.OFPort; +import org.openflow.protocol.OFType; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.action.OFActionOutput; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,39 +49,50 @@ public class Forwarding extends ForwardingBase { @Override public Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) { + Ethernet eth = IFloodlightProvider.bcStore.get(cntx, + IFloodlightProvider.CONTEXT_PI_PAYLOAD); + if (eth.isBroadcast() || eth.isMulticast()) { + // For now we treat multicast as broadcast + doFlood(sw, pi, cntx); + } else { + doForwardFlow(sw, pi, cntx); + } + + return Command.CONTINUE; + } + + protected void doForwardFlow(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) { OFMatch match = new OFMatch(); match.loadFromPacket(pi.getPacketData(), pi.getInPort(), sw.getId()); // Check if we have the location of the destination Device dstDevice = deviceManager.getDeviceByDataLayerAddress(match.getDataLayerDestination()); - // TODO Optimizations - - // Keep attachment points sorted by clusterId so we don't need double for loops - // Bidirectional flow setup if (dstDevice != null) { Device srcDevice = deviceManager.getDeviceByDataLayerAddress(match.getDataLayerSource()); Long srcIsland = sw.getSwitchClusterId(); if (srcDevice == null) { log.error("No device entry found for source device {}", dstDevice.getDataLayerAddress()); - return Command.CONTINUE; + return; } if (srcIsland == null) { log.error("No openflow island found for source device {}", dstDevice.getDataLayerAddress()); - return Command.CONTINUE; + return; } - // If we do not find the attachment point on the first hop island we need to flood + // Validate that we have a destination known on the same island + // Validate that the source and destination are not on the same switchport boolean on_same_island = false; boolean on_same_if = false; for (DeviceAttachmentPoint dstDap : dstDevice.getAttachmentPoints()) { - SwitchPortTuple dstSpt = dstDap.getSwitchPort(); - if ((dstSpt != null) && (dstSpt.getSw() != null)) { - Long dstIsland = dstDap.getSwitchPort().getSw().getSwitchClusterId(); + SwitchPortTuple dstTuple = dstDap.getSwitchPort(); + if ((dstTuple != null) && (dstTuple.getSw() != null)) { + Long dstIsland = dstTuple.getSw().getSwitchClusterId(); if ((dstIsland != null) && dstIsland.equals(srcIsland)) { on_same_island = true; - if ((sw.getId() == dstDap.getSwitchPort().getSw().getId()) && - (pi.getInPort() == dstSpt.getPort().shortValue())) { + if ((sw.getId() == dstTuple.getSw().getId()) && + (pi.getInPort() == dstTuple.getPort().shortValue())) { on_same_if = true; } break; @@ -80,63 +101,132 @@ public class Forwarding extends ForwardingBase { } if (!on_same_island) { - log.debug("No first hop island found for destination device {}", dstDevice.getDataLayerAddress()); - return Command.CONTINUE; + // Flood since we don't know the dst device + if (log.isDebugEnabled()) { + log.debug("No first hop island found for destination device {}, Action = flooding", + dstDevice.getDataLayerAddress()); + } + doFlood(sw, pi, cntx); + return; } + if (on_same_if) { - log.debug("Both source and destination are on the same switch/port {}/{}", sw.toString(), pi.getInPort()); - return Command.CONTINUE; + if (log.isDebugEnabled()) { + log.debug("Both source and destination are on the same switch/port {}/{}, Action = NOP", + sw.toString(), pi.getInPort()); + } + return; } - // Find all the routes in the same cluster - for (DeviceAttachmentPoint srcDap : srcDevice.getAttachmentPoints()) { - for (DeviceAttachmentPoint dstDap : dstDevice.getAttachmentPoints()) { - if (srcDap.equals(dstDap)) continue; - - IOFSwitch srcSw = srcDap.getSwitchPort().getSw(); - IOFSwitch dstSw = dstDap.getSwitchPort().getSw(); - Long srcCluster = null; - Long dstCluster = null; - if ((srcSw != null) && (dstSw != null)) { - srcCluster = srcSw.getSwitchClusterId(); - dstCluster = dstSw.getSwitchClusterId(); - } - - if ((srcCluster != null) && (dstCluster != null) && (srcCluster.equals(dstCluster))) { + // Install all the routes where both src and dst have attachment points + // Since the lists are stored in sorted order we can traverse the attachment points in O(m+n) time + DeviceAttachmentPoint[] srcDaps = + srcDevice.getAttachmentPointsSorted(DeviceAttachmentPoint.clusterIdComparator).toArray(new DeviceAttachmentPoint[0]); + DeviceAttachmentPoint[] dstDaps = + dstDevice.getAttachmentPointsSorted(DeviceAttachmentPoint.clusterIdComparator).toArray(new DeviceAttachmentPoint[0]); + int iSrcDaps = 0, iDstDaps = 0; + while ((iSrcDaps < srcDaps.length) && (iDstDaps < dstDaps.length)) { + DeviceAttachmentPoint srcDap = srcDaps[iSrcDaps]; + DeviceAttachmentPoint dstDap = dstDaps[iDstDaps]; + IOFSwitch srcSw = srcDap.getSwitchPort().getSw(); + IOFSwitch dstSw = dstDap.getSwitchPort().getSw(); + Long srcCluster = null; + Long dstCluster = null; + if ((srcSw != null) && (dstSw != null)) { + srcCluster = srcSw.getSwitchClusterId(); + dstCluster = dstSw.getSwitchClusterId(); + } + + int srcVsDest = srcCluster.compareTo(dstCluster); + if (srcVsDest == 0) { + if (!srcDap.equals(dstDap) && (srcCluster != null) && (dstCluster != null)) { Route route = routingEngine.getRoute(srcSw.getId(), dstSw.getId()); - if (route != null || validLocalHop(srcDap.getSwitchPort(), dstDap.getSwitchPort())) { + if ((route != null) || validLocalHop(srcDap.getSwitchPort(), dstDap.getSwitchPort())) { int bufferId = OFPacketOut.BUFFER_ID_NONE; - // Set the bufferId for the original PacketIn switch - /* - // TODO - finalize whether we need to set this or not - if (sw.getId() == srcDaps.getSwitchPort().getSw().getId()) { - bufferId = pi.getBufferId(); + if (log.isTraceEnabled()) { + log.trace("pushRoute match={} route={} destination={}:{}", + new Object[] {match, route, dstDap.getSwitchPort().getSw(), + dstDap.getSwitchPort().getPort()}); } - */ - log.debug("Pushing route match={} route={} destination={}:{}", - new Object[] {match, route, dstDap.getSwitchPort().getSw(), - dstDap.getSwitchPort().getPort()}); - pushRoute(route, match, null, - srcDap.getSwitchPort(), dstDap.getSwitchPort(), - bufferId, sw, pi, cntx); - break; + pushRoute(route, match, 0, + srcDap.getSwitchPort(), dstDap.getSwitchPort(), bufferId, + sw, pi, cntx); } } + iSrcDaps++; + iDstDaps++; + } else if (srcVsDest < 0) { + iSrcDaps++; + } else { + iDstDaps++; } } } else { - // filter multicast destinations - if ((match.getDataLayerDestination()[0] & 0x1) == 0) { - log.debug("Unable to locate device with address {}", - HexString.toHexString(match - .getDataLayerDestination())); + // Flood since we don't know the dst device + doFlood(sw, pi, cntx); + } + } + + /** + * Creates a OFPacketOut with the OFPacketIn data that is flooded on all ports unless + * the port is blocked, in which case the packet will be dropped. + * @param pi The OFPacketIn that came to the switch + * @param decision The Forwarding decision + * @param cntx The FloodlightContext associated with this OFPacketIn + */ + protected void doFlood(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) { + SwitchPortTuple srcSwTuple = new SwitchPortTuple(sw, pi.getInPort()); + LinkInfo linkInfo = topology.getLinkInfo(srcSwTuple, false); + if (log.isTraceEnabled()) { + log.trace("doFlood pi={} srcSwitchTuple={}, link={}", + new Object[] { pi, srcSwTuple, linkInfo}); + } + + if (linkInfo != null && linkInfo.isBroadcastBlocked()) { + if (log.isDebugEnabled()) { + log.debug("doFlood, drop broadcast packet, pi={}, from a blocked port, " + + "srcSwitchTuple={}, linkInfo={}", new Object[] {pi, srcSwTuple, linkInfo}); } - return Command.CONTINUE; + return; + } + + // Set Action to flood + OFPacketOut po = + (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)); + } else { + actions.add(new OFActionOutput(OFPort.OFPP_ALL.getValue(), (short)0)); } + po.setActions(actions); + po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH); - return Command.STOP; - } + // set buffer-id, in-port and packet-data based on packet-in + short poLength = (short)(po.getActionsLength() + OFPacketOut.MINIMUM_LENGTH); + po.setBufferId(pi.getBufferId()); + po.setInPort(pi.getInPort()); + if (pi.getBufferId() == OFPacketOut.BUFFER_ID_NONE) { + byte[] packetData = pi.getPacketData(); + poLength += packetData.length; + po.setPacketData(packetData); + } + po.setLength(poLength); + + try { + if (log.isTraceEnabled()) { + log.trace("Writing flood PacketOut switch={} packet-in={} packet-out={}", + new Object[] {sw, pi, po}); + } + sw.write(po, cntx); + } catch (IOException e) { + log.error("Failure writing PacketOut switch={} packet-in={} packet-out={}", + new Object[] {sw, pi, po}, e); + } + return; + } + @Override protected OFMatch wildcard(OFMatch match, IOFSwitch sw, Integer hints) { // use same wilcarding as the learning switch @@ -151,5 +241,4 @@ public class Forwarding extends ForwardingBase { return srcTuple.getSw().getId() == dstTuple.getSw().getId() && srcTuple.getPort() != dstTuple.getPort(); } - } diff --git a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java index 8052f0784..c28b1423e 100644 --- a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java +++ b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java @@ -103,6 +103,7 @@ public abstract class ForwardingBase implements IOFMessageListener, IDeviceManag IRoutingDecision decision = null; if (cntx != null) decision = IRoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION); + return this.processPacketInMessage(sw, (OFPacketIn) msg, decision, cntx); } log.error("received an unexpected message {} from switch {}", msg, sw); @@ -130,16 +131,13 @@ public abstract class ForwardingBase implements IOFMessageListener, IDeviceManag /** * Push routes from back to front - * @param route - * @param match - * @param srcSwPort - * @param dstSwPort - * @param bufferId - * @param srcSwitch - * @param pi - * @return + * @param route Route to push + * @param match OpenFlow fields to match on + * @param srcSwPort Source switch port for the first hop + * @param dstSwPort Destination switch port for final hop + * @param bufferId BufferId of the original PacketIn + * @return srcSwitchIincluded True if the source switch is included in this route */ - public boolean pushRoute(Route route, OFMatch match, Integer wildcard_hints, SwitchPortTuple srcSwPort, SwitchPortTuple dstSwPort, int bufferId, @@ -187,8 +185,11 @@ public abstract class ForwardingBase implements IOFMessageListener, IDeviceManag fm.getMatch().setInputPort(link.getInPort()); try { updateCounterStore(sw, fm); - log.debug("Pushing Route flowmod routeIndx={} sw={} inPort={} outPort={}", - new Object[] { routeIndx, sw, fm.getMatch().getInputPort(), ((OFActionOutput)fm.getActions().get(0)).getPort() }); + if (log.isDebugEnabled()) { + log.debug("Pushing Route flowmod routeIndx={} sw={} inPort={} outPort={}", + new Object[] { routeIndx, sw, fm.getMatch().getInputPort(), + ((OFActionOutput)fm.getActions().get(0)).getPort() }); + } sw.write(fm, cntx); // Push the packet out the source switch diff --git a/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java b/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java index 535a579ad..47c723f39 100644 --- a/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java +++ b/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java @@ -33,9 +33,10 @@ public interface IRoutingDecision { * if the destination is not known at this time, initiate a discovery action for it (e.g. ARP) * FORWARD_OR_FLOOD: Forward this packet, and this flow, to the first (and only device) in getDestinationDevices(), * if the destination is not known at this time, flood this packet on the source switch - * MULTICAST Multicast this packet to all the interfaces and devices attached + * BROADCAST: Broadcast this packet on all links + * MULTICAST: Multicast this packet to all the interfaces and devices attached */ - NONE, DROP, FORWARD, FORWARD_OR_FLOOD, MULTICAST + NONE, DROP, FORWARD, FORWARD_OR_FLOOD, BROADCAST, MULTICAST } public static final FloodlightContextStore<IRoutingDecision> rtStore = @@ -54,6 +55,4 @@ public interface IRoutingDecision { public void setMulticastInterfaces(List<SwitchPortTuple> lspt); public Integer getWildcards(); public void setWildcards(Integer wildcards); - - } -- GitLab