- Send Barrier Request msg before we set the routing rules for the next iteration.
 - ICMP handler flush out any buffered packets when it receives the ICMP packets.

Change-Id: Id18b5fff37ddeb5cd56f14090a2b3dc99416bf6e
diff --git a/src/main/java/net/onrc/onos/apps/segmentrouting/IcmpHandler.java b/src/main/java/net/onrc/onos/apps/segmentrouting/IcmpHandler.java
index b35f402..3b65d55 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/IcmpHandler.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/IcmpHandler.java
@@ -143,12 +143,29 @@
                         /* TODO: We should not have come here as ARP itself
                          * would have installed a Route to the host. See if
                          * we can remove this code
+                         *  - It can happen when the rule is set later in switches
+                         *  (Ex: Ping reply arrives before the rules is set in the table)
+                         *  - The rule must be set by ARP handler. But, we set the rule
+                         *  again just in case and flush any pending packets to the host.
                          */
                         log.debug("ICMPHandler: ICMP request for known host {}",
                                          hostIpAddress);
                         byte[] destinationMacAddress = host.getMacAddress().toBytes();
                         srManager.addRouteToHost(sw,
                                 destinationAddress.getInt(), destinationMacAddress);
+
+                        byte[] destIp = destinationAddress.getBytes();
+                        for (IPv4 ipPacket: srManager.getIpPacketFromQueue(destIp)) {
+                            if (ipPacket != null && !inSameSubnet(sw, ipPacket)) {
+                                Ethernet eth = new Ethernet();
+                                eth.setDestinationMACAddress(payload.getSourceMACAddress());
+                                eth.setSourceMACAddress(sw.getStringAttribute("routerMac"));
+                                eth.setEtherType(Ethernet.TYPE_IPV4);
+                                eth.setPayload(ipPacket);
+                                sendPacketOut(sw, eth, inPort.getSwitchPort(), false);
+                            }
+                        }
+
                         return;
                     }
                 }
@@ -411,4 +428,54 @@
 
     }
 
+    /**
+     * Check if the source IP and destination IP are in the same subnet
+     *
+     * @param sw Switch
+     * @param ipv4 IP address to check
+     * @return return true if the IP packet is within the same subnet
+     */
+    private boolean inSameSubnet(Switch sw, IPv4 ipv4) {
+
+        String gwIpSrc = getGwIpForSubnet(ipv4.getSourceAddress());
+        String gwIpDest = getGwIpForSubnet(ipv4.getDestinationAddress());
+
+        if (gwIpSrc.equals(gwIpDest)) {
+            return true;
+        }
+        else
+            return false;
+    }
+
+    /**
+     * Get router IP address for the given IP address
+     *
+     * @param sourceAddress
+     * @return
+     */
+    private String getGwIpForSubnet(int sourceAddress) {
+
+        String gwIp = null;
+        IPv4Address srcIp = IPv4Address.of(sourceAddress);
+
+        for (Switch sw: mutableTopology.getSwitches()) {
+
+            String subnets = sw.getStringAttribute("subnets");
+            try {
+                JSONArray arry = new JSONArray(subnets);
+                for (int i = 0; i < arry.length(); i++) {
+                    String subnetIpSlash = (String) arry.getJSONObject(i).get("subnetIp");
+                    if (srManager.netMatch(subnetIpSlash, srcIp.toString())) {
+                        gwIp = subnetIpSlash;
+                    }
+                }
+            } catch (JSONException e) {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+            }
+        }
+
+        return gwIp;
+    }
+
 }
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 cb8e5b7..2f5bff5 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
@@ -12,12 +12,15 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Queue;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import net.floodlightcontroller.core.IFloodlightProviderService;
 import net.floodlightcontroller.core.IOF13Switch;
 import net.floodlightcontroller.core.IOF13Switch.NeighborSet;
+import net.floodlightcontroller.core.internal.OFBarrierReplyFuture;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
@@ -78,7 +81,6 @@
     private ArpHandler arpHandler;
     private GenericIpHandler ipHandler;
     private IcmpHandler icmpHandler;
-    private boolean networkConverged;
     private IThreadPoolService threadPool;
     private SingletonTask discoveryTask;
     private IFloodlightProviderService floodlightProvider;
@@ -130,18 +132,7 @@
 
     @Override
     public void startUp(FloodlightModuleContext context) throws FloodlightModuleException {
-        networkConverged = false;
 
-        ScheduledExecutorService ses = threadPool.getScheduledExecutor();
-
-        discoveryTask = new SingletonTask(ses, new Runnable() {
-            @Override
-            public void run() {
-                populateEcmpRoutingRules();
-            }
-        });
-
-        discoveryTask.reschedule(15, TimeUnit.SECONDS);
     }
 
     @Override
@@ -155,6 +146,9 @@
         	else
         		ipHandler.processPacketIn(sw, inPort, payload);
         }
+        else {
+            log.debug("{}", payload.toString());
+        }
     }
     /**
      * Update ARP Cache using ARP packets
@@ -235,71 +229,21 @@
      */
     public void topologyEvents(TopologyEvents topologyEvents)
     {
-    	/**
-    	 * Any Link update events, compute the ECMP path graph for all switch nodes
-
-    	if ((topologyEvents.getAddedLinkDataEntries() != null) ||
-    		(topologyEvents.getRemovedLinkDataEntries() != null))
-    	{
-    		Iterable<Switch> switches= mutableTopology.getSwitches();
-            for (Switch sw : switches) {
-            	ECMPShortestPathGraph ecmpSPG = new ECMPShortestPathGraph(sw);
-                log.debug("ECMPShortestPathGraph is computed for switch {}",
-                		HexString.toHexString(sw.getDpid().value()));
-
-
-                HashMap<Integer, HashMap<Switch,ArrayList<Path>>> pathGraph =
-                                    ecmpSPG.getCompleteLearnedSwitchesAndPaths();
-                for (Integer itrIdx: pathGraph.keySet()){
-
-                    HashMap<Switch, ArrayList<Path>> swPathsMap =
-                                                pathGraph.get(itrIdx);
-                    for (Switch targetSw: swPathsMap.keySet()){
-                        log.debug("ECMPShortestPathGraph:Paths in Pass{} from "
-                                + "             switch {} to switch {}:****",
-                                itrIdx,
-                                HexString.toHexString(sw.getDpid().value()),
-                                HexString.toHexString(targetSw.getDpid().value()));
-                        int i=0;
-                        for (Path path:swPathsMap.get(targetSw)){
-                            log.debug("****ECMPShortestPathGraph:Path{} is {}",i++,path);
-                        }
-                    }
-                }
-
-                HashMap<Integer, HashMap<Switch,ArrayList<ArrayList<Dpid>>>> switchVia =
-                        ecmpSPG.getAllLearnedSwitchesAndVia();
-                for (Integer itrIdx: switchVia.keySet()){
-                    log.debug("ECMPShortestPathGraph:Switches learned in "
-                            + "Iteration{} from switch {}:",
-                            itrIdx,
-                            HexString.toHexString(sw.getDpid().value()));
-
-                    HashMap<Switch, ArrayList<ArrayList<Dpid>>> swViaMap =
-                                    switchVia.get(itrIdx);
-                    for (Switch targetSw: swViaMap.keySet()){
-                        log.debug("ECMPShortestPathGraph:****switch {} via:",
-                                HexString.toHexString(targetSw.getDpid().value()));
-                        int i=0;
-                        for (ArrayList<Dpid> via:swViaMap.get(targetSw)){
-                            log.debug("ECMPShortestPathGraph:******{}) {}",++i,via);
-                        }
-                    }
-                }
-
-            }
-    	}
-    	*/
-
         if ((topologyEvents.getAddedLinkDataEntries() != null) ||
                 (topologyEvents.getRemovedLinkDataEntries() != null))
         {
 
-            if (networkConverged) {
-                populateEcmpRoutingRules();
-            }
-        }
+            ScheduledExecutorService ses = threadPool.getScheduledExecutor();
 
+            discoveryTask = new SingletonTask(ses, new Runnable() {
+                @Override
+                public void run() {
+                    populateEcmpRoutingRules();
+                }
+            });
+
+            discoveryTask.reschedule(1, TimeUnit.SECONDS);
+        }
     }
 
     /**
@@ -341,10 +285,24 @@
                     }
                     setRoutingRule(targetSw, destSw, fwdToSw);
                 }
+
+                // Send Barrier Message and make sure all rules are set
+                // before we set the rules to next routers
+                IOF13Switch sw13 = (IOF13Switch)floodlightProvider.getMasterSwitch(
+                        getSwId(sw.getDpid().toString()));
+                try {
+                    OFBarrierReplyFuture replyFuture = sw13.sendBarrier();
+                    replyFuture.get(10, TimeUnit.SECONDS);
+                } catch (IOException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }
+                catch (InterruptedException | ExecutionException | TimeoutException e) {
+                    log.error("Barrier message not received for sw: {}", sw.getDpid());
+                    e.printStackTrace();
+                }
             }
         }
-
-        networkConverged = true;
     }
 
     /**
@@ -374,27 +332,18 @@
             // We assume that there is at least one transit router b/w edge routers
             Switch destSwitch = mutableTopology.getSwitch(new Dpid(destSw));
             String subnets = destSwitch.getStringAttribute("subnets");
-            try {
-                JSONArray arry = new JSONArray(subnets);
-                for (int i = 0; i < arry.length(); i++) {
-                    String subnetIp = (String) arry.getJSONObject(i).get("subnetIp");
-                    setIpTableRouter(targetSw, subnetIp, getMplsLabel(destSw)
-                            ,fwdToSw);
+            setIpTableRouterSubnet(targetSw, subnets, getMplsLabel(destSw)
+                    ,fwdToSw);
 
-                }
-            } catch (JSONException e) {
-                // TODO Auto-generated catch block
-                e.printStackTrace();
-            }
             String routerIp = destSwitch.getStringAttribute("routerIp");
-            setIpTableRouter(targetSw, routerIp, getMplsLabel(destSw), fwdToSw);
+            setIpTableRouter(targetSw, routerIp, getMplsLabel(destSw), fwdToSw, null);
         }
         // Only if the target switch is the edge router, then set the IP rules
         else if (IsEdgeRouter(targetSw.getDpid().toString())) {
             // We assume that there is at least one transit router b/w edge routers
             Switch destSwitch = mutableTopology.getSwitch(new Dpid(destSw));
             String routerIp = destSwitch.getStringAttribute("routerIp");
-            setIpTableRouter(targetSw, routerIp, getMplsLabel(destSw), fwdToSw);
+            setIpTableRouter(targetSw, routerIp, getMplsLabel(destSw), fwdToSw, null);
         }
         // if it is a transit router, then set rules in the MPLS table
         else {
@@ -403,6 +352,37 @@
 
     }
 
+    private void setIpTableRouterSubnet(Switch targetSw, String subnets,
+            String mplsLabel, List<String> fwdToSw) {
+
+        Collection <MatchActionOperationEntry> entries =
+                new ArrayList<MatchActionOperationEntry>();
+
+        try {
+            JSONArray arry = new JSONArray(subnets);
+            for (int i = 0; i < arry.length(); i++) {
+                String subnetIp = (String) arry.getJSONObject(i).get("subnetIp");
+                setIpTableRouter(targetSw, subnetIp, mplsLabel, fwdToSw, entries);
+            }
+        } catch (JSONException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+
+        if (!entries.isEmpty()) {
+            IOF13Switch sw13 = (IOF13Switch)floodlightProvider.getMasterSwitch(
+                    getSwId(targetSw.getDpid().toString()));
+
+            try {
+                sw13.pushFlows(entries);
+            } catch (IOException e) {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+            }
+        }
+
+    }
+
     /**
      * Check if the switch is the edge router or not
      * If any subnet information is defined in the config file, the we assume
@@ -438,9 +418,10 @@
      * @param subnetIp Match IP address
      * @param mplsLabel MPLS label of final destination router
      * @param fwdToSws next hop routers
+     * @param entries
      */
     private void setIpTableRouter(Switch sw, String subnetIp, String mplsLabel,
-            List<String> fwdToSws) {
+            List<String> fwdToSws, Collection<MatchActionOperationEntry> entries) {
 
         Ipv4Match ipMatch = new Ipv4Match(subnetIp);
         List<Action> actions = new ArrayList<>();
@@ -498,7 +479,10 @@
 
         try {
             printMatchActionOperationEntry(sw, maEntry);
-            sw13.pushFlow(maEntry);
+            if (entries != null)
+                entries.add(maEntry);
+            else
+                sw13.pushFlow(maEntry);
         } catch (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
@@ -751,12 +735,14 @@
         List<IPv4> bufferedPackets = new ArrayList<IPv4>();
 
         synchronized (ipPacketQueue) {
-            for (IPv4 ip: ipPacketQueue) {
-                int dest = ip.getDestinationAddress();
-                IPv4Address ip1 = IPv4Address.of(dest);
-                IPv4Address ip2 = IPv4Address.of(destIp);
-                if (ip1.equals(ip2)) {
-                    bufferedPackets.add(ipPacketQueue.poll());
+            if (!ipPacketQueue.isEmpty()) {
+                for (IPv4 ip: ipPacketQueue) {
+                    int dest = ip.getDestinationAddress();
+                    IPv4Address ip1 = IPv4Address.of(dest);
+                    IPv4Address ip2 = IPv4Address.of(destIp);
+                    if (ip1.equals(ip2)) {
+                        bufferedPackets.add((IPv4)(ipPacketQueue.poll()).clone());
+                    }
                 }
             }
         }