Driver hooks for making Dell specific changes to push Router MPLS/IP MACs depending on the outgoing packet type
diff --git a/src/main/java/net/floodlightcontroller/core/IOF13Switch.java b/src/main/java/net/floodlightcontroller/core/IOF13Switch.java
index 8360374..e3cd0d8 100644
--- a/src/main/java/net/floodlightcontroller/core/IOF13Switch.java
+++ b/src/main/java/net/floodlightcontroller/core/IOF13Switch.java
@@ -7,7 +7,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.core.matchaction.MatchActionOperationEntry;
 import net.onrc.onos.core.util.Dpid;
 import net.onrc.onos.core.util.PortNumber;
@@ -62,8 +61,14 @@
      * neighbors in this set.
      */
     public class NeighborSet {
+        public enum groupPktType {
+            IP_OUTGOING,
+            MPLS_OUTGOING
+        };
+
         Set<Dpid> dpids;
         int edgeLabel;
+        groupPktType outPktType;
 
         /**
          * Constructor
@@ -73,6 +78,7 @@
          */
         public NeighborSet(Dpid... dpids) {
             this.edgeLabel = -1;
+            this.outPktType = groupPktType.IP_OUTGOING;
             this.dpids = new HashSet<Dpid>();
             for (Dpid d : dpids) {
                 this.dpids.add(d);
@@ -89,6 +95,8 @@
 
         public void setEdgeLabel(int edgeLabel) {
             this.edgeLabel = edgeLabel;
+            if (edgeLabel > 0)
+                this.outPktType = groupPktType.MPLS_OUTGOING;
         }
 
         public Set<Dpid> getDpids() {
@@ -99,13 +107,22 @@
             return edgeLabel;
         }
 
+        public groupPktType getOutPktType() {
+            return outPktType;
+        }
+
+        public void setOutPktType(groupPktType outPktType) {
+            this.outPktType = outPktType;
+        }
+
         @Override
         public boolean equals(Object o) {
             if (!(o instanceof NeighborSet)) {
                 return false;
             }
             NeighborSet that = (NeighborSet) o;
-            return (this.dpids.equals(that.dpids) && (this.edgeLabel == that.edgeLabel));
+            return (this.dpids.equals(that.dpids) &&
+                    (this.edgeLabel == that.edgeLabel) && (this.outPktType == that.outPktType));
         }
 
         @Override
@@ -115,6 +132,7 @@
                 result = 31 * result + Longs.hashCode(d.value());
             }
             result = 31 * result + Longs.hashCode(edgeLabel);
+            result = 31 * result + outPktType.hashCode();
             return result;
         }
 
diff --git a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
index 8f7bc48..10324ff 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
@@ -50,8 +50,6 @@
 import net.onrc.onos.core.matchaction.action.DecMplsTtlAction;
 import net.onrc.onos.core.matchaction.action.DecNwTtlAction;
 import net.onrc.onos.core.matchaction.action.GroupAction;
-import net.onrc.onos.core.matchaction.action.ModifyDstMacAction;
-import net.onrc.onos.core.matchaction.action.ModifySrcMacAction;
 import net.onrc.onos.core.matchaction.action.OutputAction;
 import net.onrc.onos.core.matchaction.action.PopMplsAction;
 import net.onrc.onos.core.matchaction.action.PushMplsAction;
@@ -767,8 +765,8 @@
 
         // Output action
         if (srcMac != null && dstMac != null) {
-            ModifyDstMacAction setDstAction = new ModifyDstMacAction(MACAddress.valueOf(dstMac));
-            ModifySrcMacAction setSrcAction = new ModifySrcMacAction(MACAddress.valueOf(srcMac));
+            SetDAAction setDstAction = new SetDAAction(MacAddress.of(dstMac));
+            SetSAAction setSrcAction = new SetSAAction(MacAddress.of(srcMac));
             OutputAction outportAction = new OutputAction(PortNumber.uint32(num));
 
             actions.add(setDstAction);
diff --git a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplDellOSR.java b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplDellOSR.java
index 7327c14..d5a0ddb 100644
--- a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplDellOSR.java
+++ b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplDellOSR.java
@@ -1,11 +1,23 @@
 package net.onrc.onos.core.drivermanager;
 
 import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
 
+import net.floodlightcontroller.core.IOF13Switch.NeighborSet.groupPktType;
+import net.onrc.onos.core.matchaction.MatchAction;
+import net.onrc.onos.core.matchaction.MatchActionOperationEntry;
+import net.onrc.onos.core.matchaction.action.Action;
+import net.onrc.onos.core.matchaction.action.GroupAction;
+import net.onrc.onos.core.matchaction.action.OutputAction;
+import net.onrc.onos.core.matchaction.action.PopMplsAction;
+import net.onrc.onos.core.matchaction.action.SetDAAction;
+import net.onrc.onos.core.matchaction.action.SetMplsBosAction;
 import net.onrc.onos.core.matchaction.match.Ipv4Match;
 import net.onrc.onos.core.matchaction.match.Match;
 import net.onrc.onos.core.util.Dpid;
 import net.onrc.onos.core.util.IPv4Net;
+import net.onrc.onos.core.util.PortNumber;
 
 import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFFactory;
@@ -31,6 +43,10 @@
     private static final int DELL_TABLE_MPLS = 25;
     private static final int DELL_TABLE_ACL = 40;
 
+    private static final int MPLS_SWAP_FLOW = 0x00;
+    private static final int MPLS_POP_NO_BOS_FLOW = 0x10;
+    private static final int MPLS_POP_BOS_FLOW = 0x11;
+
     public OFSwitchImplDellOSR(OFDescStatsReply desc, boolean usePipeline13) {
         super(desc, usePipeline13);
         setVlanTableId(DELL_TABLE_VLAN);
@@ -73,6 +89,14 @@
         populateTableMissEntry(aclTableId, true, false, false, -1);
     }
 
+    protected MacAddress getNeighborRouterMacAddress(Dpid ndpid,
+            groupPktType outPktType) {
+        if (outPktType == groupPktType.MPLS_OUTGOING)
+            return getRouterMPLSMac(ndpid);
+        else
+            return super.getNeighborRouterMacAddress(ndpid,
+                    groupPktType.IP_OUTGOING);
+    }
     /* Dell Open Segment Router specific Implementation .
      * Gets the specified Router's MAC address to be used for MPLS flows
      * For Dell OSR, the MPLS MAC is IP MAC + 1
@@ -82,4 +106,71 @@
         return MacAddress.of(super.getRouterIPMac(dpid).getLong() + 1);
     }
 
+    protected void createGroupsAtTransitRouter(Set<Dpid> dpids) {
+        /* Create all possible Neighbor sets from this router
+         * NOTE: Avoid any pairings of edge routers only
+         */
+        Set<Set<Dpid>> sets = getPowerSetOfNeighbors(dpids);
+        sets = filterEdgeRouterOnlyPairings(sets);
+        log.debug("createGroupsAtTransitRouter: The size of neighbor powerset "
+                + "for sw {} is {}", getStringId(), sets.size());
+        Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+        for (Set<Dpid> combo : sets) {
+            if (combo.isEmpty())
+                continue;
+            for (groupPktType gType : groupPktType.values()) {
+                NeighborSet ns = new NeighborSet();
+                ns.addDpids(combo);
+                ns.setOutPktType(gType);
+                log.debug("createGroupsAtTransitRouter: sw {} combo {} ns {}",
+                        getStringId(), combo, ns);
+                nsSet.add(ns);
+            }
+        }
+        log.debug("createGroupsAtTransitRouter: The neighborset with label "
+                + "for sw {} is {}", getStringId(), nsSet);
+
+        for (NeighborSet ns : nsSet) {
+            updatePortNeighborSetMap(ns);
+            int gid = createGroupForANeighborSet(ns);
+            if (gid == -1) {
+                log.warn("Create Group failed with -1");
+            }
+        }
+    }
+
+    protected void analyzeAndUpdateMplsActions(
+            MatchActionOperationEntry mao) {
+        MatchAction ma = mao.getTarget();
+        int flowType = 0x00;
+        PortNumber outPort = null;
+        for (Action action : ma.getActions()) {
+            if (action instanceof PopMplsAction)
+                flowType |= 0x10;
+            else if (action instanceof SetMplsBosAction)
+                flowType |= 0x01;
+            else if (action instanceof OutputAction)
+                outPort = ((OutputAction) action).getPortNumber();
+        }
+
+        groupPktType outPktType = groupPktType.IP_OUTGOING;
+        if ((flowType == MPLS_SWAP_FLOW) ||
+                (flowType == MPLS_POP_NO_BOS_FLOW))
+            outPktType = groupPktType.MPLS_OUTGOING;
+
+        for (Action action : ma.getActions()) {
+            if ((action instanceof GroupAction)
+                    && (((GroupAction) action).getGroupId() == -1)) {
+                ((GroupAction) action).getDpids().setOutPktType(outPktType);
+            }
+            else if (action instanceof SetDAAction) {
+                if ((outPort != null) &&
+                        (portToNeighbors.get(outPort) != null)) {
+                    Dpid neighborDpid = portToNeighbors.get(outPort);
+                    ((SetDAAction) action).setAddress(
+                            getNeighborRouterMacAddress(neighborDpid, outPktType));
+                }
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplSpringOpenTTP.java b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplSpringOpenTTP.java
index 2f92d49..382513e 100644
--- a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplSpringOpenTTP.java
+++ b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplSpringOpenTTP.java
@@ -17,6 +17,7 @@
 
 import net.floodlightcontroller.core.IFloodlightProviderService.Role;
 import net.floodlightcontroller.core.IOF13Switch;
+import net.floodlightcontroller.core.IOF13Switch.NeighborSet.groupPktType;
 import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.SwitchDriverSubHandshakeAlreadyStarted;
 import net.floodlightcontroller.core.SwitchDriverSubHandshakeCompleted;
@@ -112,7 +113,7 @@
 public class OFSwitchImplSpringOpenTTP extends OFSwitchImplBase implements IOF13Switch {
     private AtomicBoolean driverHandshakeComplete;
     private AtomicBoolean haltStateMachine;
-    private OFFactory factory;
+    protected OFFactory factory;
     private static final int OFPCML_NO_BUFFER = 0xffff;
     // Configuration of asynch messages to controller. We need different
     // asynch messages depending on role-equal or role-master.
@@ -143,8 +144,8 @@
     private DriverState driverState;
     private final boolean usePipeline13;
     private SegmentRouterConfig srConfig;
-    private ConcurrentMap<Dpid, Set<PortNumber>> neighbors;
-    private ConcurrentMap<PortNumber, Dpid> portToNeighbors;
+    protected ConcurrentMap<Dpid, Set<PortNumber>> neighbors;
+    protected ConcurrentMap<PortNumber, Dpid> portToNeighbors;
     private List<Integer> segmentIds;
     private boolean isEdgeRouter;
     private ConcurrentMap<NeighborSet, EcmpInfo> ecmpGroups;
@@ -347,7 +348,7 @@
             }
             BucketInfo b = new BucketInfo(neighborDpid,
                     MacAddress.of(srConfig.getRouterMac()),
-                    getNeighborRouterMacAddress(neighborDpid),
+                    getNeighborRouterMacAddress(neighborDpid, ns.getOutPktType()),
                     port,
                     ns.getEdgeLabel(), true, -1);
             buckets.add(b);
@@ -800,9 +801,15 @@
         }
     }
 
+    /* Base implementation returns the configured router MAC only */
+    protected MacAddress getNeighborRouterMacAddress(Dpid ndpid,
+            groupPktType outPktType) {
+        return getNeighborRouterMacAddress(ndpid);
+    }
+
     /* Default implementation of this interface.
      * Gets the specified Router's MAC address to be used for IP flows
-     * To be overridden depending on the underlying switch type 
+     * To be overridden depending on the underlying switch type
      */
     public MacAddress getRouterIPMac(Dpid dpid) {
         return getNeighborRouterMacAddress(dpid);
@@ -810,7 +817,7 @@
 
     /* Default implementation of this interface.
      * Gets the specified Router's MAC address to be used for MPLS flows
-     * To be overridden depending on the underlying switch type 
+     * To be overridden depending on the underlying switch type
      */
     public MacAddress getRouterMPLSMac(Dpid dpid) {
         return getNeighborRouterMacAddress(dpid);
@@ -873,7 +880,7 @@
         }
     }
 
-    private Set<Set<Dpid>> getAllNeighborSets(Set<Dpid> neighbors) {
+    protected Set<Set<Dpid>> getPowerSetOfNeighbors(Set<Dpid> neighbors) {
         List<Dpid> list = new ArrayList<Dpid>(neighbors);
         Set<Set<Dpid>> sets = new HashSet<Set<Dpid>>();
         /* get the number of elements in the neighbors */
@@ -893,27 +900,43 @@
                     dpidSubSet.add(list.get(j));
                 }
             }
-            /* NOTE: Avoid any pairings of edge routers only
-             * at a backbone router */
-            boolean avoidEdgeRouterPairing = true;
-            if ((!isEdgeRouter) && (dpidSubSet.size() > 1)) {
+            sets.add(dpidSubSet);
+        }
+        return sets;
+    }
+
+    protected Set<Set<Dpid>> filterEdgeRouterOnlyPairings(Set<Set<Dpid>> sets) {
+        Set<Set<Dpid>> fiteredSets = new HashSet<Set<Dpid>>();
+        for (Set<Dpid> dpidSubSet : sets) {
+            if (dpidSubSet.size() > 1) {
+                boolean avoidEdgeRouterPairing = true;
                 for (Dpid dpid : dpidSubSet) {
                     if (!isEdgeRouter(dpid)) {
                         avoidEdgeRouterPairing = false;
                         break;
                     }
                 }
+                if (!avoidEdgeRouterPairing)
+                    fiteredSets.add(dpidSubSet);
             }
             else
-                avoidEdgeRouterPairing = false;
+                fiteredSets.add(dpidSubSet);
 
-            if (!avoidEdgeRouterPairing)
-                sets.add(dpidSubSet);
+        }
+        return fiteredSets;
+    }
+
+    private Set<Set<Dpid>> getAllNeighborSets(Set<Dpid> neighbors) {
+        Set<Set<Dpid>> sets = getPowerSetOfNeighbors(neighbors);
+        if (!isEdgeRouter) {
+            /* NOTE: Avoid any pairings of edge routers only
+             * at a backbone router */
+            sets = filterEdgeRouterOnlyPairings(sets);
         }
         return sets;
     }
 
-    private int createGroupForANeighborSet(NeighborSet ns) {
+    protected int createGroupForANeighborSet(NeighborSet ns) {
         int groupId = -1;
         List<BucketInfo> buckets = new ArrayList<BucketInfo>();
         for (Dpid d : ns.getDpids()) {
@@ -921,8 +944,8 @@
                 if (portEnabled((int) sp.value())) {
                     BucketInfo b = new BucketInfo(d,
                             MacAddress.of(srConfig.getRouterMac()),
-                            getNeighborRouterMacAddress(d), sp,
-                            ns.getEdgeLabel(), true, -1);
+                            getNeighborRouterMacAddress(d, ns.getOutPktType()),
+                            sp, ns.getEdgeLabel(), true, -1);
                     buckets.add(b);
                 }
             }
@@ -964,6 +987,107 @@
         }
     }
 
+    protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(
+            Set<Dpid> dpids) {
+
+        List<Integer> nsSegmentIds = new ArrayList<Integer>();
+
+        /* Add one entry for "no label" (-1) to the list if
+         * dpid list has not more than one node/neighbor as
+         * there will never be a case a packet going to more than one
+         * neighbor without a label at an edge router
+         */
+        if (dpids.size() == 1) {
+            nsSegmentIds.add(-1);
+        }
+        if (!segmentIds.isEmpty()) {
+            /* Filter out SegmentIds matching with the
+             * nodes in the combo
+             */
+            for (Integer sId : segmentIds) {
+                boolean filterOut = false;
+                /* Check if the edge label being set is of
+                 * any node in the Neighbor set
+                 */
+                for (Dpid dpid : dpids) {
+                    if (isSegmentIdSameAsNodeSegmentId(dpid, sId)) {
+                        filterOut = true;
+                        break;
+                    }
+                }
+                if (!filterOut)
+                    nsSegmentIds.add(sId);
+            }
+        }
+
+        return nsSegmentIds;
+    }
+
+    protected void createGroupsAtEdgeRouter(Set<Dpid> dpids) {
+        /* Create all possible Neighbor sets from this router
+         */
+        Set<Set<Dpid>> powerSet = getPowerSetOfNeighbors(dpids);
+        log.debug("createGroupsAtEdgeRouter: The size of neighbor powerset "
+                + "for sw {} is {}", getStringId(), powerSet.size());
+        Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+        for (Set<Dpid> combo : powerSet) {
+            if (combo.isEmpty())
+                continue;
+            List<Integer> groupSegmentIds =
+                    getSegmentIdsTobePairedWithNeighborSet(combo);
+            if (!groupSegmentIds.isEmpty()) {
+                for (Integer sId : groupSegmentIds) {
+                    NeighborSet ns = new NeighborSet();
+                    ns.addDpids(combo);
+                    ns.setEdgeLabel(sId);
+                    log.debug("createGroupsAtEdgeRouter: sw {} combo {} sId {} ns {}",
+                            getStringId(), combo, sId, ns);
+                    nsSet.add(ns);
+                }
+            }
+        }
+        log.debug("createGroupsAtEdgeRouter: The neighborset with label for sw {} is {}",
+                getStringId(), nsSet);
+
+        for (NeighborSet ns : nsSet) {
+            updatePortNeighborSetMap(ns);
+            int gid = createGroupForANeighborSet(ns);
+            if (gid == -1) {
+                log.warn("Create Group failed with -1");
+            }
+        }
+    }
+
+    protected void createGroupsAtTransitRouter(Set<Dpid> dpids) {
+        /* Create all possible Neighbor sets from this router
+         * NOTE: Avoid any pairings of edge routers only
+         */
+        Set<Set<Dpid>> sets = getPowerSetOfNeighbors(dpids);
+        sets = filterEdgeRouterOnlyPairings(sets);
+        log.debug("createGroupsAtTransitRouter: The size of neighbor powerset "
+                + "for sw {} is {}", getStringId(), sets.size());
+        Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+        for (Set<Dpid> combo : sets) {
+            if (combo.isEmpty())
+                continue;
+            NeighborSet ns = new NeighborSet();
+            ns.addDpids(combo);
+            log.debug("createGroupsAtTransitRouter: sw {} combo {} ns {}",
+                    getStringId(), combo, ns);
+            nsSet.add(ns);
+        }
+        log.debug("createGroupsAtTransitRouter: The neighborset with label "
+                + "for sw {} is {}", getStringId(), nsSet);
+
+        for (NeighborSet ns : nsSet) {
+            updatePortNeighborSetMap(ns);
+            int gid = createGroupForANeighborSet(ns);
+            if (gid == -1) {
+                log.warn("Create Group failed with -1");
+            }
+        }
+    }
+
     /**
      * createGroups creates ECMP groups for all ports on this router connected
      * to other routers (in the OF network). The information for ports is
@@ -991,63 +1115,12 @@
         if (dpids == null || dpids.isEmpty()) {
             return;
         }
-        /* Create all possible Neighbor sets from this router
-         * NOTE: Avoid any pairings of edge routers only
-         */
-        Set<Set<Dpid>> powerSet = getAllNeighborSets(dpids);
-        log.debug("createGroups: The size of neighbor powerset for sw {} is {}",
-                getStringId(), powerSet.size());
-        Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
-        for (Set<Dpid> combo : powerSet) {
-            if (combo.isEmpty())
-                continue;
-            if (isEdgeRouter && !segmentIds.isEmpty()) {
-                /* Filter out SegmentIds matching with the
-                 * nodes in the combo
-                 * Add one entry for "no label" (-1) to the list
-                 */
-                List<Integer> groupSegmentIds = new ArrayList<Integer>();
-                groupSegmentIds.add(-1);
-                for (Integer sId : segmentIds) {
-                    boolean filterOut = false;
-                    /* Check if the edge label being set is of
-                     * any node in the Neighbor set
-                     */
-                    for (Dpid dpid : combo) {
-                        if (isSegmentIdSameAsNodeSegmentId(dpid, sId)) {
-                            filterOut = true;
-                            break;
-                        }
-                    }
-                    if (!filterOut)
-                        groupSegmentIds.add(sId);
-                }
-                for (Integer sId : groupSegmentIds) {
-                    NeighborSet ns = new NeighborSet();
-                    ns.addDpids(combo);
-                    ns.setEdgeLabel(sId);
-                    log.debug("createGroups: sw {} combo {} sId {} ns {}",
-                            getStringId(), combo, sId, ns);
-                    nsSet.add(ns);
-                }
-            } else {
-                NeighborSet ns = new NeighborSet();
-                ns.addDpids(combo);
-                log.debug("createGroups: sw {} combo {} ns {}",
-                        getStringId(), combo, ns);
-                nsSet.add(ns);
-            }
-        }
-        log.debug("createGroups: The neighborset with label for sw {} is {}",
-                getStringId(), nsSet);
 
-        for (NeighborSet ns : nsSet) {
-            updatePortNeighborSetMap(ns);
-            int gid = createGroupForANeighborSet(ns);
-            if (gid == -1) {
-                log.warn("Create Group failed with -1");
-            }
+        if (isEdgeRouter) {
+            createGroupsAtEdgeRouter(dpids);
         }
+        else
+            createGroupsAtTransitRouter(dpids);
     }
 
     private class EcmpInfo {
@@ -1528,7 +1601,16 @@
         return ipFlow;
     }
 
+    protected void analyzeAndUpdateMplsActions(
+            MatchActionOperationEntry mao) {
+        /* For base switch implementation, use the MPLS actions
+         * as specified by the application
+         */
+        return;
+    }
+
     private OFMessage getMplsEntry(MatchActionOperationEntry mao) {
+        analyzeAndUpdateMplsActions(mao);
         MatchAction ma = mao.getTarget();
         Operator op = mao.getOperator();
         MplsMatch mplsm = (MplsMatch) ma.getMatch();
diff --git a/src/main/java/net/onrc/onos/core/matchaction/action/SetDAAction.java b/src/main/java/net/onrc/onos/core/matchaction/action/SetDAAction.java
index 6a28e7b..8209ec0 100644
--- a/src/main/java/net/onrc/onos/core/matchaction/action/SetDAAction.java
+++ b/src/main/java/net/onrc/onos/core/matchaction/action/SetDAAction.java
@@ -12,4 +12,8 @@
     public MacAddress getAddress() {
         return this.macAddress;
     }
+
+    public void setAddress(MacAddress macAddress) {
+        this.macAddress = macAddress;
+    }
 }