- Fix a bug of losing first a few pings
- Add a simple logic to recompute a path when links are removed

Change-Id: I76462dcc1eaf23cd8ae09a69f61f0bb5116f9b16
diff --git a/src/main/java/net/onrc/onos/apps/segmentrouting/ECMPShortestPathGraph.java b/src/main/java/net/onrc/onos/apps/segmentrouting/ECMPShortestPathGraph.java
index 0a66cdd..9451beb 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/ECMPShortestPathGraph.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/ECMPShortestPathGraph.java
@@ -34,7 +34,7 @@
 
     /**
      * Constructor
-     * 
+     *
      * @param rootSwitch root of the BFS tree
      */
     public ECMPShortestPathGraph(Switch rootSwitch) {
@@ -140,9 +140,18 @@
     }
 
     /**
+     * Return root switch for the graph
+     *
+     * @return root switch
+     */
+    public Switch getRootSwitch() {
+        return rootSwitch;
+    }
+
+    /**
      * Return the computed ECMP paths from the root switch to a given switch in
      * the network
-     * 
+     *
      * @param targetSwitch the target switch
      * @return the list of ECMP Paths from the root switch to the target switch
      */
@@ -162,7 +171,7 @@
     /**
      * Return the complete info of the computed ECMP paths for each switch
      * learned in multiple iterations from the root switch
-     * 
+     *
      * @return the hash table of Switches learned in multiple Dijkstra
      *         iterations and corresponding ECMP paths to it from the root
      *         switch
@@ -192,7 +201,7 @@
      * Switch<>} Iteration2, Switch<> via {Switch<>, Switch<>, Switch<>}
      * {Switch<>, Switch<>, Switch<>} Switch<> via {Switch<>, Switch<>,
      * Switch<>} }
-     * 
+     *
      * @return the hash table of Switches learned in multiple Dijkstra
      *         iterations and corresponding ECMP paths in terms of Switches to
      *         be traversed to it from the root switch
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 778658a..56a402b 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/IcmpHandler.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/IcmpHandler.java
@@ -96,7 +96,7 @@
      * hosts in subnets of the switches, but if the MAC address is not known,
      * then send an ARP request to the subent. If the MAC address is known, then
      * set the routing rule to the switch
-     * 
+     *
      * @param sw
      * @param inPort
      * @param payload
@@ -151,18 +151,18 @@
                         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.setDestinationMACAddress(destinationMacAddress);
                                 eth.setSourceMACAddress(sw
                                         .getStringAttribute("routerMac"));
                                 eth.setEtherType(Ethernet.TYPE_IPV4);
                                 eth.setPayload(ipPacket);
-                                sendPacketOut(sw, eth, inPort.getSwitchPort(), false);
+                                for (Port port: host.getAttachmentPoints())
+                                    sendPacketOut(sw, eth, new SwitchPort(
+                                            port.getDpid(),port.getNumber()), false);
                             }
                         }
 
@@ -180,7 +180,7 @@
 
     /**
      * Retrieve Gateway IP address of all subnets defined in net config file
-     * 
+     *
      * @param sw Switch to retrieve subnet GW IPs for
      * @return list of GW IP addresses for all subnets
      */
@@ -209,7 +209,7 @@
 
     /**
      * Send ICMP reply back
-     * 
+     *
      * @param sw Switch
      * @param inPort Port the ICMP packet is forwarded from
      * @param icmpRequest the ICMP request to handle
@@ -251,7 +251,7 @@
      * action, it sends out packet to TABLE port Otherwise, it sends the packet
      * to the port the packet came from (in this case, MPLS label is added if
      * the packet needs go through transit switches)
-     * 
+     *
      * @param sw Switch the packet came from
      * @param packet Ethernet packet to send
      * @param switchPort port to send the packet
@@ -328,7 +328,7 @@
 
     /**
      * Get MPLS label for the target address from the network config file
-     * 
+     *
      * @param targetAddress - IP address of the target host
      * @return MPLS label of the switch to send packets to the target address
      */
@@ -361,7 +361,7 @@
     /**
      * Get Router MAC Address for the target address from the network config
      * file
-     * 
+     *
      * @param targetAddress - IP address of the target host
      * @return Router MAC of the switch to send packets to the target address
      */
@@ -392,7 +392,7 @@
     /**
      * Add a new rule to VLAN table to forward packets from any port to the next
      * table It is required to forward packets from controller to pipeline
-     * 
+     *
      * @param sw Switch the packet came from
      */
     private void addControlPortInVlanTable(Switch sw) {
@@ -435,7 +435,7 @@
 
     /**
      * 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
@@ -454,7 +454,7 @@
 
     /**
      * Get router IP address for the given IP address
-     * 
+     *
      * @param sourceAddress
      * @return
      */
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 a8f4f1c..513b3d6 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
@@ -29,6 +29,7 @@
 import net.onrc.onos.api.packet.IPacketListener;
 import net.onrc.onos.api.packet.IPacketService;
 import net.onrc.onos.core.flowprogrammer.IFlowPusherService;
+import net.onrc.onos.core.intent.Path;
 import net.onrc.onos.core.main.config.IConfigInfoService;
 import net.onrc.onos.core.matchaction.MatchAction;
 import net.onrc.onos.core.matchaction.MatchActionId;
@@ -86,6 +87,8 @@
     private SingletonTask discoveryTask;
     private IFloodlightProviderService floodlightProvider;
 
+    private HashMap<Switch, ECMPShortestPathGraph> graphs;
+
     @Override
     public Collection<Class<? extends IFloodlightService>> getModuleServices() {
         // TODO Auto-generated method stub
@@ -125,6 +128,7 @@
         mutableTopology = topologyService.getTopology();
         topologyService.addListener(this, false);
         ipPacketQueue = new ConcurrentLinkedQueue<IPv4>();
+        graphs = new HashMap<Switch, ECMPShortestPathGraph>();
 
         this.packetService = context.getServiceImpl(IPacketService.class);
         packetService.registerPacketListener(this);
@@ -163,7 +167,7 @@
      * Update ARP Cache using ARP packets It is used to set destination MAC
      * address to forward packets to known hosts. But, it will be replace with
      * Host information of Topology service later.
-     * 
+     *
      * @param arp APR packets to use for updating ARP entries
      */
     public void updateArpCache(ARP arp) {
@@ -176,7 +180,7 @@
 
     /**
      * Get MAC address to known hosts
-     * 
+     *
      * @param destinationAddress IP address to get MAC address
      * @return MAC Address to given IP address
      */
@@ -207,11 +211,11 @@
 
     /**
      * Send an ARP request via ArpHandler
-     * 
+     *
      * @param destinationAddress
      * @param sw
      * @param inPort
-     * 
+     *
      */
     public void sendArpRequest(Switch sw, int destinationAddress, Port inPort) {
         arpHandler.sendArpRequest(sw, destinationAddress, inPort);
@@ -219,7 +223,7 @@
 
     /**
      * Temporary class to to keep ARP entry
-     * 
+     *
      */
     private class ArpEntry {
 
@@ -234,7 +238,7 @@
 
     /**
      * Topology events that have been generated.
-     * 
+     *
      * @param topologyEvents the generated Topology Events
      * @see TopologyEvents
      */
@@ -268,25 +272,25 @@
 
     /**
      * Report ports newly added to driver
-     * 
+     *
      * @param portEntries
      */
     private void processPortAdd(Collection<PortData> portEntries) {
         // TODO: do we need to add ports slowly?
         for (PortData port : portEntries) {
             Dpid dpid = port.getDpid();
-            int portNo = (int) port.getPortNumber().value();
 
-            IOF13Switch sw13 = (IOF13Switch) floodlightProvider.getMasterSwitch(
+            IOF13Switch sw = (IOF13Switch) floodlightProvider.getMasterSwitch(
                     getSwId(port.getDpid().toString()));
-            // sw13.addPort(portNo);
-            log.debug("Add port {} to switch {}", portNo, dpid.toString());
+            sw.addPortToGroups(port.getPortNumber());
+
+            log.debug("Add port {} to switch {}", port, dpid);
         }
     }
 
     /**
      * Reports ports of new links to driver and recalculate ECMP SPG
-     * 
+     *
      * @param linkEntries
      */
     private void processLinkAdd(Collection<LinkData> linkEntries) {
@@ -298,13 +302,13 @@
             SwitchPort srcPort = link.getSrc();
             SwitchPort dstPort = link.getDst();
 
-            IOF13Switch sw13src = (IOF13Switch) floodlightProvider.getMasterSwitch(
+            IOF13Switch srcSw = (IOF13Switch) floodlightProvider.getMasterSwitch(
                     getSwId(srcPort.getDpid().toString()));
-            IOF13Switch sw13dst = (IOF13Switch) floodlightProvider.getMasterSwitch(
+            IOF13Switch dstSw = (IOF13Switch) floodlightProvider.getMasterSwitch(
                     getSwId(srcPort.getDpid().toString()));
-            // sw13src.addPort(srcPort);
-            // sw13dst.addPort(dstPort);
 
+            srcSw.addPortToGroups(srcPort.getPortNumber());
+            dstSw.addPortToGroups(dstPort.getPortNumber());
         }
         discoveryTask.reschedule(1, TimeUnit.SECONDS);
     }
@@ -313,7 +317,7 @@
      * Check if all links are gone b/w the two switches. If all links are gone,
      * then we need to recalculate the path. Otherwise, just report link failure
      * to the driver.
-     * 
+     *
      * @param linkEntries
      */
     private void processLinkRemoval(Collection<LinkData> linkEntries) {
@@ -321,16 +325,21 @@
             SwitchPort srcPort = link.getSrc();
             SwitchPort dstPort = link.getDst();
 
-            IOF13Switch sw13src = (IOF13Switch) floodlightProvider.getMasterSwitch(
+            IOF13Switch srcSw = (IOF13Switch) floodlightProvider.getMasterSwitch(
                     getSwId(srcPort.getDpid().toString()));
-            IOF13Switch sw13dst = (IOF13Switch) floodlightProvider.getMasterSwitch(
+            IOF13Switch dstSw = (IOF13Switch) floodlightProvider.getMasterSwitch(
                     getSwId(srcPort.getDpid().toString()));
-            // sw13src.addPort(srcPort);
-            // sw13dst.addPort(dstPort);
+
+            // TODO: Why do we get null for the master switch??
+            if (srcSw != null)
+                srcSw.removePortFromGroups(srcPort.getPortNumber());
+            if (dstSw != null)
+                dstSw.removePortFromGroups(dstPort.getPortNumber());
 
             Switch srcSwitch = mutableTopology.getSwitch(srcPort.getDpid());
             if (srcSwitch.getLinkToNeighbor(dstPort.getDpid()) == null) {
-                discoveryTask.reschedule(1, TimeUnit.SECONDS);
+                //discoveryTask.reschedule(1, TimeUnit.SECONDS);
+                modifyEcmpRoutingRules(link);
                 log.debug("All links are gone b/w {} and {}", srcPort.getDpid(),
                         srcPort.getDpid());
             }
@@ -339,80 +348,166 @@
 
     /**
      * report ports removed to the driver immediately
-     * 
+     *
      * @param portEntries
      */
     private void processPortRemoval(Collection<PortData> portEntries) {
         for (PortData port : portEntries) {
             Dpid dpid = port.getDpid();
-            int portNo = (int) port.getPortNumber().value();
 
-            IOF13Switch sw13 = (IOF13Switch) floodlightProvider.getMasterSwitch(
+            IOF13Switch sw = (IOF13Switch) floodlightProvider.getMasterSwitch(
                     getSwId(port.getDpid().toString()));
-            // sw13.removePort(portNo);
-            log.debug("Remove port {} from switch {}", portNo, dpid.toString());
+            if (sw != null)
+                sw.removePortFromGroups(port.getPortNumber());
+            log.debug("Remove port {} from switch {}", port, dpid);
         }
     }
 
     /**
      * Populate routing rules walking through the ECMP shortest paths
-     * 
+     *
      */
     private void populateEcmpRoutingRules() {
 
         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()));
+            graphs.put(sw, ecmpSPG);
+            //log.debug("ECMPShortestPathGraph is computed for switch {}",
+            //        HexString.toHexString(sw.getDpid().value()));
+            populateEcmpRoutingRulesForPath(sw, ecmpSPG);
+        }
+    }
 
-            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()));
-                    String destSw = sw.getDpid().toString();
-                    List<String> fwdToSw = new ArrayList<String>();
+    private void populateEcmpRoutingRulesForPath(Switch sw,
+            ECMPShortestPathGraph ecmpSPG) {
 
-                    int i = 0;
-                    for (ArrayList<Dpid> via : swViaMap.get(targetSw)) {
-                        log.debug("ECMPShortestPathGraph:******{}) {}", ++i, via);
-                        if (via.isEmpty()) {
-                            fwdToSw.add(destSw);
-                        }
-                        else {
-                            fwdToSw.add(via.get(0).toString());
-                        }
+        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()));
+                String destSw = sw.getDpid().toString();
+                List<String> fwdToSw = new ArrayList<String>();
+
+                int i = 0;
+                for (ArrayList<Dpid> via : swViaMap.get(targetSw)) {
+                    log.debug("ECMPShortestPathGraph:******{}) {}", ++i, via);
+                    if (via.isEmpty()) {
+                        fwdToSw.add(destSw);
                     }
-                    setRoutingRule(targetSw, destSw, fwdToSw);
+                    else {
+                        fwdToSw.add(via.get(0).toString());
+                    }
                 }
-
-                // 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) {
-                    e.printStackTrace();
-                } catch (InterruptedException | ExecutionException | TimeoutException e) {
-                    log.error("Barrier message not received for sw: {}", sw.getDpid());
-                    e.printStackTrace();
-                }
+                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) {
+                e.printStackTrace();
+            } catch (InterruptedException | ExecutionException | TimeoutException e) {
+                log.error("Barrier message not received for sw: {}", sw.getDpid());
+                e.printStackTrace();
+            }
+        }
+
+    }
+
+
+    private class SwitchPair {
+        private Switch src;
+        private Switch dst;
+
+        public SwitchPair(Switch src, Switch dst) {
+            this.src = src;
+            this.dst = dst;
+        }
+
+        public Switch getSource() {
+            return src;
+        }
+
+        public Switch getDestination() {
+            return dst;
         }
     }
 
     /**
-     * 
+     * Modify the routing rules for the lost links
+     * - Recompute the path if the link failed is included in the path
+     * (including src and dest).
+     *
+     * @param newLink
+     */
+    private void modifyEcmpRoutingRules(LinkData linkRemoved) {
+
+        //HashMap<Switch, SwitchPair> linksToRecompute = new HashMap<Switch, SwitchPair>();
+        List<SwitchPair> linksToRecompute = new ArrayList<SwitchPair>();
+
+        for (ECMPShortestPathGraph ecmpSPG : graphs.values()) {
+            Switch rootSw = ecmpSPG.getRootSwitch();
+            HashMap<Integer, HashMap<Switch, ArrayList<Path>>> paths =
+                    ecmpSPG.getCompleteLearnedSwitchesAndPaths();
+            for (HashMap<Switch, ArrayList<Path>> p: paths.values()) {
+                for (Switch destSw: p.keySet()) {
+                    ArrayList<Path> path = p.get(destSw);
+                    if  (checkPath(path, linkRemoved)) {
+                        linksToRecompute.add(new SwitchPair(rootSw, destSw));
+                    }
+                }
+            }
+        }
+
+        // Recompute the path for the specific route
+        for (SwitchPair pair: linksToRecompute) {
+
+            log.debug("Recompute path from {} to {}", pair.getSource(), pair.getDestination());
+            // TODO : we need the following function
+            //ECMPShortestPathGraph ecmpSPG =
+            //     new ECMPShortestPathGraph(pair.getSource(), pair.getDestination());
+            ECMPShortestPathGraph ecmpSPG =
+                    new ECMPShortestPathGraph(pair.getSource());
+            // TODO: we need to use different function to MODIFY the rules
+            populateEcmpRoutingRulesForPath(pair.getSource(), ecmpSPG);
+        }
+    }
+
+    /**
+     * Check if the path is affected from the link removed
+     *
+     * @param path Path to check
+     * @param linkRemoved link removed
+     * @return true if the path contains the link removed
+     */
+    private boolean checkPath(ArrayList<Path> path, LinkData linkRemoved) {
+
+        for (Path ppp: path) {
+            // TODO: need to check if this is a bidirectional or
+            // unidirectional
+            if (ppp.contains(linkRemoved)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     *
      * Set routing rules in targetSw {forward packets to fwdToSw switches in
      * order to send packets to destSw} - If the target switch is an edge router
      * and final destnation switch is also an edge router, then set IP
@@ -420,7 +515,7 @@
      * router, then set IP forwarding rule to the transit router loopback IP
      * address - If the target is a transit router, then just set the MPLS
      * forwarding rule
-     * 
+     *
      * @param targetSw Switch to set the rules
      * @param destSw Final destination switches
      * @param fwdToSw next hop switches
@@ -459,6 +554,14 @@
 
     }
 
+    /**
+     * Set IP forwarding rule to the gateway of each subnet of switches
+     *
+     * @param targetSw Switch to set rules
+     * @param subnets  subnet information
+     * @param mplsLabel destination MPLS label
+     * @param fwdToSw  router to forward packets to
+     */
     private void setIpTableRouterSubnet(Switch targetSw, String subnets,
             String mplsLabel, List<String> fwdToSw) {
 
@@ -491,7 +594,7 @@
     /**
      * Check if the switch is the edge router or not If any subnet information
      * is defined in the config file, the we assume it is an edge router
-     * 
+     *
      * @param dpid Dpid of the switch to check
      * @return true if it is an edge router, otherwise false
      */
@@ -516,7 +619,7 @@
      * Set IP forwarding rule - If the destination is the next hop, then do not
      * push MPLS, just decrease the NW TTL - Otherwise, push MPLS label and set
      * the MPLS ID
-     * 
+     *
      * @param sw target switch to set rules
      * @param subnetIp Match IP address
      * @param mplsLabel MPLS label of final destination router
@@ -596,7 +699,7 @@
 
     /**
      * Convert a string DPID to its Switch Id (integer)
-     * 
+     *
      * @param dpid
      * @return
      */
@@ -616,7 +719,7 @@
      * as the next hop to forward packets then, pop the MPLS label according to
      * PHP rule - Otherwise, just forward packets to next hops using Group
      * action
-     * 
+     *
      * @param sw Switch to set the rules
      * @param mplsLabel destination MPLS label
      * @param fwdSws next hop switches
@@ -675,7 +778,7 @@
 
     /**
      * Debugging function to print out the Match Action Entry
-     * 
+     *
      * @param maEntry
      */
     private void printMatchActionOperationEntry(Switch sw,
@@ -736,7 +839,7 @@
 
     /**
      * Get MPLS label reading the config file
-     * 
+     *
      * @param dipid DPID of the switch
      * @return MPLS label for the switch
      */
@@ -757,7 +860,7 @@
 
     /**
      * The function checks if given IP matches to the given subnet mask
-     * 
+     *
      * @param addr - subnet address to match
      * @param addr1 - IP address to check
      * @return true if the IP address matches to the subnet, otherwise false
@@ -811,7 +914,7 @@
 
     /**
      * Add a routing rule for the host
-     * 
+     *
      * @param sw - Switch to add the rule
      * @param hostIpAddress Destination host IP address
      * @param hostMacAddress Destination host MAC address
@@ -823,7 +926,7 @@
 
     /**
      * Add IP packet to a buffer queue
-     * 
+     *
      * @param ipv4
      */
     public void addPacket(IPv4 ipv4) {
@@ -832,7 +935,7 @@
 
     /**
      * Retrieve all packets whose destination is the given address.
-     * 
+     *
      * @param destIp Destination address of packets to retrieve
      */
     public List<IPv4> getIpPacketFromQueue(byte[] destIp) {