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