- decrease the TTL correctly
 - buffer IP packets and send out them when ARP responses arrive unless they are ping to routers

Change-Id: Ib2f0f7a28198cc1092ef45a91a3b2feedaa7a2eb
diff --git a/src/main/java/net/onrc/onos/apps/segmentrouting/ArpHandler.java b/src/main/java/net/onrc/onos/apps/segmentrouting/ArpHandler.java
index 5befa5b..b724e27 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/ArpHandler.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/ArpHandler.java
@@ -92,20 +92,43 @@
 
     }
 
+    /**
+     * process ARP packets from switches
+     * It add a IP routing rule to the host
+     * If it is an ARP response, then flush out all pending packets to the host
+     *
+     * @param sw
+     * @param inPort
+     * @param payload
+     */
     public void processPacketIn(Switch sw, Port inPort, Ethernet payload){
 
     	log.debug("ArpHandler: Received a ARP packet from sw {} ", sw.getDpid());
 
         ARP arp = (ARP)payload.getPayload();
 
+        byte[] senderMacAddressByte = arp.getSenderHardwareAddress();
+        IPv4Address hostIpAddress = IPv4Address.of(arp.getSenderProtocolAddress());
+        log.debug("ArpHandler: Add IP route to Host {} ", hostIpAddress);
+        srManager.addRouteToHost(sw,hostIpAddress.getInt(), senderMacAddressByte);
+
         if (arp.getOpCode() == ARP.OP_REQUEST) {
         	log.debug("ArpHandler: Received a ARP Requestfrom sw {} ", sw.getDpid());
             handleArpRequest(sw, inPort, payload);
         }
-        byte[] senderMacAddressByte = arp.getSenderHardwareAddress();
-        IPv4Address hostIpAddress = IPv4Address.of(arp.getSenderProtocolAddress());
-    	log.debug("ArpHandler: Add IP route to Host {} ", hostIpAddress);
-        srManager.addRouteToHost(sw,hostIpAddress.getInt(), senderMacAddressByte);
+        else {
+            byte[] destIp = arp.getSenderProtocolAddress();
+            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.getNumber().shortValue());
+                }
+            }
+        }
     }
 
     /**
@@ -172,6 +195,12 @@
     	}
     }
 
+    /**
+     * Check if the ARP request is to known hosts
+     *
+     * @param sw  Switch
+     * @param arpRequest  ARP request to check
+     */
     private Host isArpReqForKnownHost(Switch sw, ARP arpRequest) {
     	Host knownHost = null;
 
@@ -187,6 +216,15 @@
         return knownHost;
 
     }
+
+    /**
+     *
+     * Check if the ARP is for the switch
+     *
+     * @param sw Switch
+     * @param arpRequest ARP request to check
+     * @return true if the ARP is for the switch
+     */
     private boolean isArpReqForSwitch(Switch sw, ARP arpRequest) {
         List<String> subnetGatewayIPs = getSubnetGatewayIps(sw);
         boolean isArpForSwitch = false;
@@ -198,6 +236,7 @@
         }
         return isArpForSwitch;
     }
+
     /**
      * Retrieve Gateway IP address of all subnets defined in net config file
      *
@@ -333,5 +372,55 @@
         flowPusher.add(sw.getDpid(), po);
     }
 
+    /**
+     * 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;
+    }
+
 }