Added policy routing feature including segment stiching feature in Segment Routing app and driver.

Change-Id: I56c185eeb208ba9117f33fc37a6da43f11ecb686
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 a9446ef..429eadc 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
@@ -27,11 +27,10 @@
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.core.util.SingletonTask;
-import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
+import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.api.packet.IPacketListener;
 import net.onrc.onos.api.packet.IPacketService;
-import net.onrc.onos.apps.segmentrouting.web.SegmentRoutingWebRoutable;
 import net.onrc.onos.core.flowprogrammer.IFlowPusherService;
 import net.onrc.onos.core.intent.Path;
 import net.onrc.onos.core.main.config.IConfigInfoService;
@@ -51,6 +50,8 @@
 import net.onrc.onos.core.matchaction.match.Ipv4Match;
 import net.onrc.onos.core.matchaction.match.Match;
 import net.onrc.onos.core.matchaction.match.MplsMatch;
+import net.onrc.onos.core.matchaction.match.PacketMatch;
+import net.onrc.onos.core.matchaction.match.PacketMatchBuilder;
 import net.onrc.onos.core.packet.ARP;
 import net.onrc.onos.core.packet.Ethernet;
 import net.onrc.onos.core.packet.IPv4;
@@ -83,7 +84,6 @@
             .getLogger(SegmentRoutingManager.class);
 
     private ITopologyService topologyService;
-    private IRestApiService restApi;
     private IPacketService packetService;
     private MutableTopology mutableTopology;
     private ConcurrentLinkedQueue<IPv4> ipPacketQueue;
@@ -95,18 +95,22 @@
     private IThreadPoolService threadPool;
     private SingletonTask discoveryTask;
     private SingletonTask linkAddTask;
+    private SingletonTask testTask;
     private IFloodlightProviderService floodlightProvider;
 
     private HashMap<Switch, ECMPShortestPathGraph> graphs;
     private HashMap<String, LinkData> linksDown;
     private HashMap<String, LinkData> linksToAdd;
     private ConcurrentLinkedQueue<TopologyEvents> topologyEventQueue;
+    private HashMap<Integer, HashMap<String, PolicyRouteInfo>> stitchInfo;
+    private HashMap<Integer, HashMap<String, Integer>> tunnelGroupMap;
 
     private int numOfEvents = 0;
     private int numOfEventProcess = 0;
     private int numOfPopulation = 0;
     private long matchActionId = 0L;
     private final int DELAY_TO_ADD_LINK = 10;
+    private final int MAX_NUM_LABELS = 3;
 
     @Override
     public Collection<Class<? extends IFloodlightService>> getModuleServices() {
@@ -130,7 +134,6 @@
         l.add(IPacketService.class);
         l.add(IFlowPusherService.class);
         l.add(ITopologyService.class);
-        l.add(IRestApiService.class);
 
         return l;
 
@@ -146,17 +149,17 @@
         topologyService = context.getServiceImpl(ITopologyService.class);
         threadPool = context.getServiceImpl(IThreadPoolService.class);
         mutableTopology = topologyService.getTopology();
-        topologyService.addListener(this, false);
         ipPacketQueue = new ConcurrentLinkedQueue<IPv4>();
         graphs = new HashMap<Switch, ECMPShortestPathGraph>();
         linksDown = new HashMap<String, LinkData>();
         linksToAdd = new HashMap<String, LinkData>();
-        //topologyLinks = new HashSet<LinkData>();
-        restApi = context.getServiceImpl(IRestApiService.class);
         topologyEventQueue = new ConcurrentLinkedQueue<TopologyEvents>();
+        stitchInfo = new HashMap<Integer, HashMap<String, PolicyRouteInfo>>();
+        packetService = context.getServiceImpl(IPacketService.class);
+        tunnelGroupMap = new HashMap<Integer, HashMap<String, Integer>>();
 
-        this.packetService = context.getServiceImpl(IPacketService.class);
         packetService.registerPacketListener(this);
+        topologyService.addListener(this, false);
 
 
     }
@@ -164,7 +167,6 @@
     @Override
     public void startUp(FloodlightModuleContext context) throws FloodlightModuleException {
         ScheduledExecutorService ses = threadPool.getScheduledExecutor();
-        restApi.addRestletRoutable(new SegmentRoutingWebRoutable());
 
         discoveryTask = new SingletonTask(ses, new Runnable() {
             @Override
@@ -180,8 +182,18 @@
             }
         });
 
+        testTask = new SingletonTask(ses, new Runnable() {
+            @Override
+            public void run() {
+                runTest();
+            }
+        });
+
+        // policy routing test task
+        //testTask.reschedule(20, TimeUnit.SECONDS);
     }
 
+
     @Override
     public void receive(Switch sw, Port inPort, Ethernet payload) {
         if (payload.getEtherType() == Ethernet.TYPE_ARP)
@@ -447,7 +459,7 @@
                     getSwId(port.getDpid().toString()));
             if (sw != null) {
                 sw.addPortToGroups(port.getPortNumber());
-                log.debug("Add port {} to switch {}", port, dpid);
+                //log.debug("Add port {} to switch {}", port, dpid);
             }
         }
     }
@@ -499,10 +511,10 @@
             srcSw.addPortToGroups(srcPort.getPortNumber());
             dstSw.addPortToGroups(dstPort.getPortNumber());
 
-            log.debug("Add a link port {} to switch {} to add link {}", srcPort, srcSw,
-                    link);
-            log.debug("Add a link port {} to switch {} to add link {}", dstPort, dstSw,
-                    link);
+            //log.debug("Add a link port {} to switch {} to add link {}", srcPort, srcSw,
+            //        link);
+            //log.debug("Add a link port {} to switch {} to add link {}", dstPort, dstSw,
+            //        link);
 
         }
         populateEcmpRoutingRules(false);
@@ -890,7 +902,6 @@
 
         // If destination SW is the same as the fwd SW, then do not push MPLS
         // label
-
         if (fwdToSws.size() > 1) {
             PushMplsAction pushMplsAction = new PushMplsAction();
             SetMplsIdAction setIdAction = new SetMplsIdAction(Integer.parseInt(mplsLabel));
@@ -902,7 +913,6 @@
             //actions.add(decMplsTtlAction);
             // actions.add(setIdAction);
             groupAction.setEdgeLabel(Integer.parseInt(mplsLabel));
-
         }
         else {
             String fwdToSw = fwdToSws.get(0);
@@ -911,7 +921,6 @@
                 actions.add(decTtlAction);
             }
             else {
-                PushMplsAction pushMplsAction = new PushMplsAction();
                 SetMplsIdAction setIdAction = new SetMplsIdAction(
                         Integer.parseInt(mplsLabel));
                 CopyTtlOutAction copyTtlOutAction = new CopyTtlOutAction();
@@ -960,23 +969,6 @@
     }
 
     /**
-     * Convert a string DPID to its Switch Id (integer)
-     *
-     * @param dpid
-     * @return
-     */
-    private long getSwId(String dpid) {
-
-        long swId = 0;
-
-        String swIdHexStr = "0x"+dpid.substring(dpid.lastIndexOf(":") + 1);
-        if (swIdHexStr != null)
-            swId = Integer.decode(swIdHexStr);
-
-        return swId;
-    }
-
-    /**
      * Set MPLS forwarding rules to MPLS table
      * </p>
      * If the destination is the same as the next hop to forward packets then,
@@ -1027,7 +1019,12 @@
             // One rule for Bos = 0
             MplsMatch mplsMatchBos = new MplsMatch(Integer.parseInt(mplsLabel), false);
             List<Action> actionsBos = new ArrayList<Action>();
-            actionsBos.add(popAction);
+            PopMplsAction popActionBos = new PopMplsAction(EthType.MPLS_UNICAST);
+            DecMplsTtlAction decMplsTtlAction = new DecMplsTtlAction(1);
+
+            actionsBos.add(copyTtlInAction);
+            actionsBos.add(popActionBos);
+            actionsBos.add(decMplsTtlAction);
             actionsBos.add(groupAction);
 
             MatchAction matchActionBos = new MatchAction(new MatchActionId(matchActionId++),
@@ -1088,16 +1085,464 @@
         }
     }
 
+    /**
+     * Create a tunnel for policy routing
+     * It delivers the node IDs of tunnels to driver.
+     * Split the node IDs if number of IDs exceeds the limit for stitching.
+     *
+     * @param tunnelId  Node IDs for the tunnel
+     * @param Ids tunnel ID
+     */
+    public boolean createTunnel(int tunnelId, List<String> Ids) {
+
+        if (tunnelId < 0) {
+            log.debug("Tunnel ID should be posivtive integer.");
+            return false;
+        }
+
+        if (Ids.isEmpty() || Ids.size() < 2) {
+            log.debug("Wrong tunnel information");
+            return false;
+        }
+
+        HashMap<String, PolicyRouteInfo> stitchingRule = getStitchingRule(Ids);
+        stitchInfo.put(Integer.valueOf(tunnelId), stitchingRule);
+        if (stitchingRule == null) {
+            log.debug("Failed to get the policy rule.");
+            return false;
+        }
+        HashMap<String, Integer> switchGroupPair = new HashMap<String, Integer>();
+        for (String targetDpid: stitchingRule.keySet()) {
+            PolicyRouteInfo route = stitchingRule.get(targetDpid);
+
+            IOF13Switch targetSw = (IOF13Switch) floodlightProvider.getMasterSwitch(
+                    getSwId(targetDpid.toString()));
+
+            if (targetSw == null) {
+                log.debug("Switch {} is gone.", targetDpid);
+                return false;
+            }
+
+            NeighborSet ns = new NeighborSet();
+            for (Dpid dpid: route.getFwdSwDpid())
+                ns.addDpid(dpid);
+
+            printTunnelInfo(targetSw, tunnelId, route.getRoute(), ns);
+            int groupId = targetSw.createTunnel(tunnelId, route.getRoute(), ns);
+            switchGroupPair.put(targetDpid.toString(), groupId);
+
+        }
+
+        tunnelGroupMap.put(Integer.valueOf(tunnelId), switchGroupPair);
+
+        return true;
+    }
+
+    /**
+     * Set policy table for policy routing
+     *
+     * @param sw
+     * @param mplsLabel
+     */
+    private void setPolicyTable(MACAddress srcMac, MACAddress dstMac,
+            Short etherType, IPv4Net srcIp, IPv4Net dstIp, Byte ipProto,
+            Short srcTcpPort, Short dstTcpPort, int tid) {
+
+        HashMap<String, PolicyRouteInfo> routeInfo = stitchInfo.get(Integer.valueOf(tid));
+        HashMap<String, Integer> switchGroupPair = tunnelGroupMap.get(Integer.valueOf(tid));
+        for (String srcDpid: routeInfo.keySet()) {
+
+            PacketMatchBuilder packetBuilder = new PacketMatchBuilder();
+
+            if (srcMac != null)
+                packetBuilder.setSrcMac(srcMac);
+            if (dstMac != null)
+                packetBuilder.setDstMac(dstMac);
+            if (etherType != null) {
+                packetBuilder.setEtherType(etherType);
+            }
+            if (srcIp != null) {
+                packetBuilder.setSrcIp(srcIp.address(), srcIp.prefixLen());
+            }
+            if (dstIp != null) {
+                packetBuilder.setDstIp(dstIp.address(), dstIp.prefixLen());
+            }
+            if (ipProto != null) {
+                packetBuilder.setIpProto(ipProto);
+            }
+            if (srcTcpPort > 0) {
+                packetBuilder.setSrcTcpPort(srcTcpPort);
+            }
+            if (dstTcpPort > 0) {
+                packetBuilder.setDstTcpPort(dstTcpPort);
+            }
+            PacketMatch policyMatch = packetBuilder.build();
+
+            List<Action> actions = new ArrayList<>();
+            GroupAction groupAction = new GroupAction();
+            int gropuId = switchGroupPair.get(srcDpid);
+            groupAction.setGroupId(gropuId);
+            actions.add(groupAction);
+
+            MatchAction matchAction = new MatchAction(new MatchActionId(
+                    matchActionId++),
+                    new SwitchPort((long) 0, (short) 0), policyMatch, actions);
+            MatchActionOperationEntry maEntry =
+                    new MatchActionOperationEntry(Operator.ADD, matchAction);
+
+            IOF13Switch sw13 = (IOF13Switch) floodlightProvider.getMasterSwitch(
+                    getSwId(srcDpid));
+
+            if (sw13 != null) {
+                printMatchActionOperationEntry(sw13, maEntry);
+                try {
+                    sw13.pushFlow(maEntry);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    /**
+     * Get the forwarding Switch DPIDs to send packets to a node
+     *
+     * @param srcSw source switch
+     * @param nodeId destination node Id
+     * @return list of switch DPID to forward packets to
+     */
+
+    private List<Dpid> getForwardingSwitchForNodeId(Switch srcSw, String nodeId) {
+
+        List<Dpid> fwdSws = new ArrayList<Dpid>();
+        Switch destSw = null;
+
+        destSw = getSwitchFromNodeId(nodeId);
+
+        if (destSw == null) {
+            log.debug("Cannot find the switch with ID {}", nodeId);
+            return null;
+        }
+
+        ECMPShortestPathGraph ecmpSPG = new ECMPShortestPathGraph(srcSw);
+
+        HashMap<Integer, HashMap<Switch, ArrayList<ArrayList<Dpid>>>> switchVia =
+                ecmpSPG.getAllLearnedSwitchesAndVia();
+        for (Integer itrIdx : switchVia.keySet()) {
+            HashMap<Switch, ArrayList<ArrayList<Dpid>>> swViaMap =
+                    switchVia.get(itrIdx);
+            for (Switch targetSw : swViaMap.keySet()) {
+                String destSwDpid = destSw.getDpid().toString();
+                if (targetSw.getDpid().toString().equals(destSwDpid)) {
+                    for (ArrayList<Dpid> via : swViaMap.get(targetSw)) {
+                        if (via.isEmpty()) {
+                            fwdSws.add(destSw.getDpid());
+                        }
+                        else {
+                            fwdSws.add(via.get(0));
+                        }
+                    }
+                }
+            }
+        }
+
+        return fwdSws;
+    }
+
+    /**
+     * Get switch for the node Id specified
+     *
+     * @param nodeId node ID for switch
+     * @return Switch
+     */
+    private Switch getSwitchFromNodeId(String nodeId) {
+
+        for (Switch sw : mutableTopology.getSwitches()) {
+            String id = sw.getStringAttribute("nodeSid");
+            if (id.equals(nodeId)) {
+                return sw;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Convert a string DPID to its Switch Id (integer)
+     *
+     * @param dpid
+     * @return
+     */
+    private long getSwId(String dpid) {
+
+        long swId = 0;
+
+        String swIdHexStr = "0x"+dpid.substring(dpid.lastIndexOf(":") + 1);
+        if (swIdHexStr != null)
+            swId = Integer.decode(swIdHexStr);
+
+        return swId;
+    }
+
+    private void runTest() {
+
+        String[] routeArray = {"101", "102", "103", "104", "105", "108", "110"};
+        List<String> routeList = new ArrayList<String>();
+        for (int i = 0; i < routeArray.length; i++)
+            routeList.add(routeArray[i]);
+
+        if (createTunnel(1, routeList)) {
+            IPv4Net srcIp = new IPv4Net("10.0.1.1/24");
+            IPv4Net dstIp = new IPv4Net("10.1.2.1/24");
+
+            this.setPolicyTable(null, null, Ethernet.TYPE_IPV4, srcIp, dstIp, IPv4.PROTOCOL_ICMP, (short)-1, (short)-1, 1);
+        }
+        else {
+            testTask.reschedule(5, TimeUnit.SECONDS);
+        }
+    }
+
+    private void runTest1() {
+
+        String dpid1 = "00:00:00:00:00:00:00:01";
+        String dpid2 = "00:00:00:00:00:00:00:0a";
+        Switch srcSw = mutableTopology.getSwitch(new Dpid(dpid1));
+        Switch dstSw = mutableTopology.getSwitch(new Dpid(dpid2));
+
+        if (srcSw == null || dstSw == null) {
+            testTask.reschedule(1, TimeUnit.SECONDS);
+            log.debug("Switch is gone. Reschedule the test");
+            return;
+        }
+
+        String[] routeArray = {"101", "102", "105", "108", "110"};
+        List<String> routeList = new ArrayList<String>();
+        for (int i = 0; i < routeArray.length; i++)
+            routeList.add(routeArray[i]);
+
+        List<String> optimizedRoute = this.getOptimizedPath(srcSw, dstSw, routeList);
+
+        log.debug("Test set is {}", routeList.toString());
+        log.debug("Result set is {}", optimizedRoute.toString());
+
+
+    }
+
+    /**
+     * Optimize the mpls label
+     * The feature will be used only for policy of "avoid a specific switch".
+     * Check route to each router in route backward.
+     * If there is only one route to the router and the routers are included in
+     * the route, remove the id from the path.
+     * A-B-C-D-E  => A-B-C-D-E -> A-E
+     *   |   |    => A-B-H-I   -> A-I
+     *   F-G-H-I  => A-D-I     -> A-D-I
+     */
+    private List<String> getOptimizedPath(Switch srcSw, Switch dstSw, List<String> route) {
+
+        List<String> optimizedPath = new ArrayList<String>();
+        optimizedPath.addAll(route);
+        ECMPShortestPathGraph ecmpSPG = new ECMPShortestPathGraph(srcSw);
+
+        HashMap<Integer, HashMap<Switch, ArrayList<Path>>> paths =
+                ecmpSPG.getCompleteLearnedSwitchesAndPaths();
+        for (HashMap<Switch, ArrayList<Path>> p: paths.values()) {
+            for (Switch s: p.keySet()) {
+                if (s.getDpid().toString().equals(dstSw.getDpid().toString())) {
+                    ArrayList<Path> ecmpPaths = p.get(s);
+                    if (ecmpPaths!= null && ecmpPaths.size() == 1) {
+                        for (Path path: ecmpPaths) {
+                            for (LinkData link: path) {
+                                String srcId = getMplsLabel(link.getSrc().getDpid().toString());
+                                String dstId = getMplsLabel(link.getSrc().getDpid().toString());
+                                if (optimizedPath.contains(srcId)) {
+                                    optimizedPath.remove(srcId);
+                                }
+                                if (optimizedPath.contains(dstId)) {
+                                    optimizedPath.remove(dstId);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return optimizedPath;
+
+    }
+
+
+    class PolicyRouteInfo {
+
+        String srcSwDpid;
+        List<Dpid> fwdSwDpids;
+        List<String> route;
+
+        PolicyRouteInfo() {
+            fwdSwDpids = new ArrayList<Dpid>();
+            route = new ArrayList<String>();
+        }
+
+        void setSrcDpid(String dpid) {
+            this.srcSwDpid = dpid;
+        }
+
+        void setFwdSwDpid(List<Dpid> dpid) {
+            this.fwdSwDpids = dpid;
+        }
+
+        void addRoute(String id) {
+            route.add(id);
+        }
+
+        void setRoute(List<String> r) {
+            this.route = r;
+        }
+
+        String getSrcSwDpid() {
+            return this.srcSwDpid;
+        }
+
+        List<Dpid> getFwdSwDpid() {
+            return this.fwdSwDpids;
+        }
+
+        List<String> getRoute() {
+            return this.route;
+        }
+    }
+
+
+    /**
+     *
+     *
+     * @param srcSw
+     * @param dstSw
+     * @param route
+     * @return
+     */
+    private HashMap<String, PolicyRouteInfo> getStitchingRule(List<String> route) {
+
+        if (route.isEmpty() || route.size() < 2)
+            return null;
+
+        HashMap<String, PolicyRouteInfo> rules = new HashMap<String, PolicyRouteInfo>();
+
+        Switch srcSw = this.getSwitchFromNodeId(route.get(0));
+        String srcDpid = srcSw.getDpid().toString();
+
+        if (route.size() <= MAX_NUM_LABELS+1) {
+            PolicyRouteInfo info = new PolicyRouteInfo();
+            info.setSrcDpid(srcSw.getDpid().toString());
+            List<Dpid> fwdSwDpids = getForwardingSwitchForNodeId(srcSw, route.get(1));
+            info.setFwdSwDpid(fwdSwDpids);
+            route.remove(0);
+            info.setRoute(route);
+            rules.put(srcDpid, info);
+            return rules;
+        }
+
+        int i = 0;
+        PolicyRouteInfo routeInfo = new PolicyRouteInfo();
+        String prevNodeId = null;
+        boolean checkNeighbor = true;
+
+        for (String nodeId: route) {
+            if (i == 0) {
+                routeInfo.setSrcDpid(srcDpid);
+                srcSw = getSwitchFromNodeId(nodeId);
+                i++;
+            }
+            else if (i == 1) {
+                if (checkNeighbor) {
+                    // Check if next node is the neighbor SW of the source SW
+                    List<Dpid> fwdSwDpids = getForwardingSwitchForNodeId(srcSw, nodeId);
+                    if (fwdSwDpids == null || fwdSwDpids.isEmpty()) {
+                        log.debug("There is no route from node {} to node {}", srcSw.getDpid(), nodeId);
+                        return null;
+                    }
+                    // If first Id is one of the neighbors, do not include it to route, but set it as a fwd SW.
+                    boolean match = false;
+                    for (Dpid dpid: fwdSwDpids) {
+                        if (getMplsLabel(dpid.toString()).toString().equals(nodeId)) {
+                            List<Dpid> fwdSws = new ArrayList<Dpid>();
+                            fwdSws.add(dpid);
+                            routeInfo.setFwdSwDpid(fwdSws);
+                            match = true;
+                            break;
+                        }
+                    }
+                    if (!match) {
+                        routeInfo.addRoute(nodeId);
+                        routeInfo.setFwdSwDpid(fwdSwDpids);
+                        i++;
+                    }
+
+                    checkNeighbor = false;
+                }
+                else {
+                    routeInfo.addRoute(nodeId);
+                    i++;
+                }
+            }
+            else {
+                routeInfo.addRoute(nodeId);
+                i++;
+            }
+
+            if (i == MAX_NUM_LABELS+1) {
+                rules.put(srcDpid, routeInfo);
+                routeInfo = new PolicyRouteInfo();
+                srcSw = getSwitchFromNodeId(nodeId);
+                srcDpid = getSwitchFromNodeId(nodeId).getDpid().toString();
+                routeInfo.setSrcDpid(srcDpid);
+                i = 1;
+                checkNeighbor = true;
+            }
+        }
+
+        if (i < MAX_NUM_LABELS+1) {
+            rules.put(srcDpid, routeInfo);
+        }
+
+        return rules;
+    }
+
+    /**
+     * print tunnel info - used only for debugging.
+     * @param targetSw
+     *
+     * @param fwdSwDpids
+     * @param ids
+     * @param tunnelId
+     */
+    private void printTunnelInfo(IOF13Switch targetSw, int tunnelId,
+            List<String> ids, NeighborSet ns) {
+        StringBuilder logStr = new StringBuilder("In switch " +
+            targetSw.getId() + ", create a tunnel " + tunnelId + " " + " of push ");
+        for (String id: ids)
+            logStr.append(id + "-");
+        logStr.append(" output to ");
+        for (Dpid dpid: ns.getDpids())
+            logStr.append(dpid + " - ");
+
+        log.debug(logStr.toString());
+
+    }
+
+
 
     /**
      * Debugging function to print out the Match Action Entry
+     * @param sw13
      *
      * @param maEntry
      */
-    private void printMatchActionOperationEntry(Switch sw,
-            MatchActionOperationEntry maEntry) {
+    private void printMatchActionOperationEntry(
+            IOF13Switch sw13, MatchActionOperationEntry maEntry) {
 
-        StringBuilder logStr = new StringBuilder("In switch " + sw.getDpid() + ", ");
+        StringBuilder logStr = new StringBuilder("In switch " + sw13.getId() + ", ");
 
         MatchAction ma = maEntry.getTarget();
         Match m = ma.getMatch();
@@ -1115,6 +1560,13 @@
             logStr.append(mplsLabel);
             logStr.append(" then ");
         }
+        else if (m instanceof PacketMatch) {
+            GroupAction ga = (GroupAction)actions.get(0);
+            logStr.append("if the policy match is XXX then go to group " +
+                    ga.getGroupId());
+            log.debug(logStr.toString());
+            return;
+        }
 
         logStr.append(" do { ");
         for (Action action : actions) {
@@ -1142,7 +1594,6 @@
             else if (action instanceof SetMplsIdAction) {
                 int id = ((SetMplsIdAction) action).getMplsId();
                 logStr.append("Set MPLS ID as " + id + ", ");
-
             }
         }
 
@@ -1269,4 +1720,6 @@
         return bufferedPackets;
     }
 
+
+
 }
diff --git a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
index 81586d8..f799617 100644
--- a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
+++ b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
@@ -877,6 +877,23 @@
         return;
     }
 
+
+    private void createGroupForMplsLabel(int groupId, String nodeId,
+            int nextGroupId, boolean bos) {
+        List<BucketInfo> buckets = new ArrayList<BucketInfo>();
+        BucketInfo bucket = new BucketInfo(nextGroupId,
+                Integer.parseInt(nodeId), bos);
+        buckets.add(bucket);
+        EcmpInfo ecmpInfo = new EcmpInfo(groupId, buckets);
+        setPolicyEcmpGroup(ecmpInfo);
+//        ecmpGroups.put(ns, ecmpInfo);
+        log.debug(
+                "createGroupForANeighborSet: Creating ecmp group {} in sw {} "
+                        + "for pushing label {} and group to {}",
+                groupId, getStringId(), nodeId, nextGroupId);
+        return;
+    }
+
     /**
      * createGroups creates ECMP groups for all ports on this router connected
      * to other routers (in the OF network). The information for ports is
@@ -899,6 +916,7 @@
      * <li>7) all ports to R1, R2, and R3
      */
     private void createGroups() {
+
         Set<Dpid> dpids = neighbors.keySet();
         if (dpids == null || dpids.isEmpty()) {
             return;
@@ -961,7 +979,9 @@
         MacAddress srcMac;
         MacAddress dstMac;
         PortNumber outport;
+        int groupNo;
         int mplsLabel;
+        boolean bos;
 
         BucketInfo(Dpid nDpid, MacAddress smac, MacAddress dmac,
                 PortNumber p, int label) {
@@ -970,13 +990,26 @@
             dstMac = dmac;
             outport = p;
             mplsLabel = label;
+            groupNo = -1;
         }
 
+        BucketInfo(int no, int label, boolean b) {
+            neighborDpid = null;
+            srcMac = null;
+            dstMac = null;
+            outport = null;
+            groupNo = no;
+            mplsLabel = label;
+            bos = b;
+        }
+
+
         @Override
         public String toString() {
             return " {neighborDpid: " + neighborDpid + ", dstMac: " + dstMac +
                     ", srcMac: " + srcMac + ", outport: " + outport +
-                    "mplsLabel: " + mplsLabel + "}";
+                    ", groupNo: " + groupNo +
+                    ", mplsLabel: " + mplsLabel + "}";
         }
     }
 
@@ -1042,6 +1075,84 @@
         }
     }
 
+    private void setPolicyEcmpGroup(EcmpInfo ecmpInfo) {
+        List<OFMessage> msglist = new ArrayList<OFMessage>();
+        OFGroup group = OFGroup.of(ecmpInfo.groupId);
+
+        List<OFBucket> buckets = new ArrayList<OFBucket>();
+        List<OFAction> actions = new ArrayList<OFAction>();
+        for (BucketInfo b : ecmpInfo.buckets) {
+            if (b.dstMac != null && b.srcMac != null && b.outport != null) {
+                OFOxmEthDst dmac = factory.oxms()
+                        .ethDst(b.dstMac);
+                OFAction setDA = factory.actions().buildSetField()
+                        .setField(dmac).build();
+                OFOxmEthSrc smac = factory.oxms()
+                        .ethSrc(b.srcMac);
+                OFAction setSA = factory.actions().buildSetField()
+                        .setField(smac).build();
+                OFAction outp = factory.actions().buildOutput()
+                        .setPort(OFPort.of(b.outport.shortValue()))
+                        .build();
+                actions.add(setSA);
+                actions.add(setDA);
+                actions.add(outp);
+            }
+            if (b.groupNo > 0) {
+                OFAction groupTo = factory.actions().buildGroup()
+                        .setGroup(OFGroup.of(b.groupNo))
+                        .build();
+                actions.add(groupTo);
+            }
+            if (b.mplsLabel != -1) {
+                OFAction pushLabel = factory.actions().buildPushMpls()
+                        .setEthertype(EthType.MPLS_UNICAST).build();
+
+                OFBooleanValue bosValue = null;
+                if (b.bos)
+                    bosValue = OFBooleanValue.TRUE;
+                else
+                    bosValue = OFBooleanValue.FALSE;
+                OFOxmMplsBos bosX = factory.oxms()
+                        .mplsBos(bosValue);
+                OFAction setBX = factory.actions().buildSetField()
+                        .setField(bosX).build();
+                OFOxmMplsLabel lid = factory.oxms()
+                        .mplsLabel(U32.of(b.mplsLabel));
+                OFAction setLabel = factory.actions().buildSetField()
+                        .setField(lid).build();
+                OFAction copyTtl = factory.actions().copyTtlOut();
+                OFAction decrTtl = factory.actions().decMplsTtl();
+                actions.add(pushLabel);
+                actions.add(setLabel);
+                actions.add(setBX);
+                actions.add(copyTtl);
+                // decrement TTL only when the first MPLS label is pushed
+                if (b.bos)
+                    actions.add(decrTtl);
+            }
+            OFBucket ofb = factory.buildBucket()
+                    .setWeight(1)
+                    .setActions(actions)
+                    .build();
+            buckets.add(ofb);
+        }
+
+        OFMessage gm = factory.buildGroupAdd()
+                .setGroup(group)
+                .setBuckets(buckets)
+                .setGroupType(OFGroupType.SELECT)
+                .setXid(getNextTransactionId())
+                .build();
+        msglist.add(gm);
+        try {
+            write(msglist);
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+
     private void modifyEcmpGroup(EcmpInfo ecmpInfo) {
         List<OFMessage> msglist = new ArrayList<OFMessage>();
         OFGroup group = OFGroup.of(ecmpInfo.groupId);
@@ -1155,15 +1266,23 @@
             EthType ethertype = ((PopMplsAction) action).getEthType();
             ofAction = factory.actions().popMpls(ethertype);
         } else if (action instanceof GroupAction) {
-            NeighborSet ns = ((GroupAction) action).getDpids();
-            EcmpInfo ei = ecmpGroups.get(ns);
-            if (ei == null) {
-                log.debug("Unable to find ecmp group for neighbors {} at "
-                        + "switch {} and hence creating it", ns, getStringId());
-                createGroupForANeighborSet(ns, groupid.incrementAndGet());
-                ei = ecmpGroups.get(ns);
+            // If group Id can be specified explicitly in case of policy routing.
+            int gid = -1;
+            GroupAction ga = (GroupAction)action;
+            if (ga.getGroupId() > 0) {
+                gid = ga.getGroupId();
             }
-            int gid = ei.groupId;
+            else {
+                NeighborSet ns = ((GroupAction) action).getDpids();
+                EcmpInfo ei = ecmpGroups.get(ns);
+                if (ei == null) {
+                    log.debug("Unable to find ecmp group for neighbors {} at "
+                            + "switch {} and hence creating it", ns, getStringId());
+                    createGroupForANeighborSet(ns, groupid.incrementAndGet());
+                    ei = ecmpGroups.get(ns);
+                }
+                gid = ei.groupId;
+            }
             ofAction = factory.actions().buildGroup()
                     .setGroup(OFGroup.of(gid))
                     .build();
@@ -1420,13 +1539,13 @@
                 .setTableId(TableId.of(TABLE_ACL))
                 .setMatch(matchBuilder.build())
                 .setInstructions(instructions)
-                .setPriority(MAX_PRIORITY / 2) // TODO: wrong - should be MA
-                                               // priority
+                .setPriority(MAX_PRIORITY) // exact match and exclusive
                 .setBufferId(OFBufferId.NO_BUFFER)
                 .setIdleTimeout(0)
                 .setHardTimeout(0)
                 .setXid(getNextTransactionId())
                 .build();
+
         return aclFlow;
     }
 
@@ -1499,10 +1618,35 @@
         }
     }
 
+    public int createTunnel(int tunnelId, List<String> route, NeighborSet ns) {
+
+        // create a last group of the group chaining
+        int finalGroupId = groupid.incrementAndGet();
+        createGroupForANeighborSet(ns, finalGroupId);
+
+        int groupId = 0;
+        int nextGroupId = finalGroupId;
+        boolean bos = false;
+
+        // process the node ID in reverse order
+        for (int i = 0; i < route.size(); i++) {
+            String nodeId = route.get(i);
+            groupId = groupid.incrementAndGet();
+            if (i == route.size()-1)
+                bos = true;
+            createGroupForMplsLabel(groupId, nodeId, nextGroupId, bos);
+            nextGroupId = groupId;
+        }
+
+        return groupId;
+    }
+
+
     // *****************************
     // Unused
     // *****************************
 
+
     @SuppressWarnings("unused")
     private void setAsyncConfig() throws IOException {
         List<OFMessage> msglist = new ArrayList<OFMessage>(3);
diff --git a/src/main/java/net/onrc/onos/core/matchaction/action/GroupAction.java b/src/main/java/net/onrc/onos/core/matchaction/action/GroupAction.java
index 642e87a..9f02135 100644
--- a/src/main/java/net/onrc/onos/core/matchaction/action/GroupAction.java
+++ b/src/main/java/net/onrc/onos/core/matchaction/action/GroupAction.java
@@ -5,6 +5,7 @@
 
 public class GroupAction implements Action {
     NeighborSet fwdSws;
+    int groupId;
 
     public GroupAction() {
         fwdSws = new NeighborSet();
@@ -21,4 +22,12 @@
     public NeighborSet getDpids() {
         return fwdSws;
     }
+
+    public void setGroupId(int id) {
+        this.groupId = id;
+    }
+
+    public int getGroupId() {
+        return this.groupId;
+    }
 }