CORD-378 Validate ARP SPA in segment routing ARP handler

Check if the source protocol address of an ARP packet belongs to the
same subnet configured on the port it is seen.

Additional changes:
- Improve human-readability of ARP.toString()

Change-Id: Ie21c1edb43aa0ec636e9a927ca164a46ab2256fc
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ArpHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
index 9b554e3..c1a51514 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
@@ -18,6 +18,7 @@
 import org.onlab.packet.ARP;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
@@ -82,12 +83,17 @@
      * @param pkt incoming packet
      */
     public void processPacketIn(InboundPacket pkt) {
-
         Ethernet ethernet = pkt.parsed();
         ARP arp = (ARP) ethernet.getPayload();
-
         ConnectPoint connectPoint = pkt.receivedFrom();
         DeviceId deviceId = connectPoint.deviceId();
+
+        if (!validateArpSpa(connectPoint, arp)) {
+            log.warn("Ignore ARP packet discovered on {} with unexpected src protocol address {}.",
+                    connectPoint, Ip4Address.valueOf(arp.getSenderProtocolAddress()));
+            return;
+        }
+
         if (arp.getOpCode() == ARP.OP_REQUEST) {
             handleArpRequest(deviceId, connectPoint, ethernet);
         } else {
@@ -146,6 +152,20 @@
         }
     }
 
+    /**
+     * Check if the source protocol address of an ARP packet belongs to the same
+     * subnet configured on the port it is seen.
+     *
+     * @param connectPoint connect point where the ARP packet is seen
+     * @param arpPacket ARP packet
+     * @return true if the source protocol address belongs to the configured subnet
+     */
+    private boolean validateArpSpa(ConnectPoint connectPoint, ARP arpPacket) {
+        Ip4Address spa = Ip4Address.valueOf(arpPacket.getSenderProtocolAddress());
+        Ip4Prefix subnet = config.getPortSubnet(connectPoint.deviceId(), connectPoint.port());
+        return subnet.contains(spa);
+    }
+
 
     private boolean isArpForRouter(DeviceId deviceId, ARP arpMsg) {
         Ip4Address targetProtocolAddress = Ip4Address.valueOf(
diff --git a/utils/misc/src/main/java/org/onlab/packet/ARP.java b/utils/misc/src/main/java/org/onlab/packet/ARP.java
index 8aac4e9..80584ba 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ARP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ARP.java
@@ -422,10 +422,10 @@
                 .add("hardwareAddressLength", Byte.toString(hardwareAddressLength))
                 .add("protocolAddressLength", Byte.toString(protocolAddressLength))
                 .add("opCode", Short.toString(opCode))
-                .add("senderHardwareAddress", Arrays.toString(senderHardwareAddress))
-                .add("senderProtocolAddress", Arrays.toString(senderProtocolAddress))
-                .add("targetHardwareAddress", Arrays.toString(targetHardwareAddress))
-                .add("targetProtocolAddress", Arrays.toString(targetProtocolAddress))
+                .add("senderHardwareAddress", MacAddress.valueOf(senderHardwareAddress))
+                .add("senderProtocolAddress", Ip4Address.valueOf(senderProtocolAddress))
+                .add("targetHardwareAddress", MacAddress.valueOf(targetHardwareAddress))
+                .add("targetProtocolAddress", Ip4Address.valueOf(targetProtocolAddress))
                 .toString();
     }
 }
diff --git a/utils/misc/src/test/java/org/onlab/packet/ArpTest.java b/utils/misc/src/test/java/org/onlab/packet/ArpTest.java
index 7aacb7b..e67424e 100644
--- a/utils/misc/src/test/java/org/onlab/packet/ArpTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/ArpTest.java
@@ -99,9 +99,9 @@
         String str = arp.toString();
         assertTrue(StringUtils.contains(str, "hardwareAddressLength=" + hwAddressLength));
         assertTrue(StringUtils.contains(str, "protocolAddressLength=" + protoAddressLength));
-        assertTrue(StringUtils.contains(str, "senderHardwareAddress=" + Arrays.toString(srcMac.toBytes())));
-        assertTrue(StringUtils.contains(str, "senderProtocolAddress=" + Arrays.toString(srcIp.toOctets())));
-        assertTrue(StringUtils.contains(str, "targetHardwareAddress=" + Arrays.toString(targetMac.toBytes())));
-        assertTrue(StringUtils.contains(str, "targetProtocolAddress=" + Arrays.toString(targetIp.toOctets())));
+        assertTrue(StringUtils.contains(str, "senderHardwareAddress=" + srcMac));
+        assertTrue(StringUtils.contains(str, "senderProtocolAddress=" + srcIp));
+        assertTrue(StringUtils.contains(str, "targetHardwareAddress=" + targetMac));
+        assertTrue(StringUtils.contains(str, "targetProtocolAddress=" + targetIp));
     }
 }