Improves ping responder

Patch contains several bugfixes and improvements:
- Fixes sid retrieval when the destination leaf is down
- Fixes sid retrieval when ping goes through the spine
- Fixes MPLS deserializer
- Improves Ethernet toString
- Fixes ping to looback for dh host when bond sends to wrong leaf

Change-Id: I05963e74b2976e526826ffd377cadeb462ba0a8d
diff --git a/utils/misc/src/main/java/org/onlab/packet/Ethernet.java b/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
index 5fd07bf..fb249a9 100644
--- a/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
+++ b/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
@@ -514,13 +514,13 @@
      */
     @Override
     public String toString() {
-
         final StringBuilder sb = new StringBuilder("\n");
-
         final IPacket pkt = this.getPayload();
 
         if (pkt instanceof ARP) {
             sb.append("arp");
+        } else if (pkt instanceof MPLS) {
+            sb.append("mpls");
         } else if (pkt instanceof LLDP) {
             sb.append("lldp");
         } else if (pkt instanceof ICMP) {
@@ -530,11 +530,7 @@
         } else if (pkt instanceof DHCP) {
             sb.append("dhcp");
         } else {
-            /*
-             * When we don't know the protocol, we print using
-             * the well known hex format instead of a decimal
-             * value.
-             */
+            // Just print the ethertype
             sb.append(String.format(HEX_PROTO,
                                     Integer.toHexString(this.getEtherType() & 0xffff)));
         }
@@ -567,6 +563,12 @@
             sb.append("\nnw_dst: ");
             sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
                     .getTargetProtocolAddress())));
+        } else if (pkt instanceof MPLS) {
+            final MPLS p = (MPLS) pkt;
+            sb.append("\nmpls: ");
+            sb.append(this.etherType == MPLS_UNICAST ? "unicast" : "multicast");
+            sb.append("\nmpls_label: ");
+            sb.append(p.label);
         } else if (pkt instanceof LLDP) {
             sb.append("lldp packet");
         } else if (pkt instanceof ICMP) {
@@ -593,7 +595,6 @@
                     sb.append(((TCP) payload).getSourcePort());
                     sb.append("\ntp_dst: ");
                     sb.append(((TCP) payload).getDestinationPort());
-
                 } else if (payload instanceof UDP) {
                     sb.append("\ntp_src: ");
                     sb.append(((UDP) payload).getSourcePort());
@@ -705,7 +706,6 @@
         } else {
             sb.append("\nunknown packet");
         }
-
         return sb.toString();
     }
 
diff --git a/utils/misc/src/main/java/org/onlab/packet/MPLS.java b/utils/misc/src/main/java/org/onlab/packet/MPLS.java
index 948075b..07ead0c 100644
--- a/utils/misc/src/main/java/org/onlab/packet/MPLS.java
+++ b/utils/misc/src/main/java/org/onlab/packet/MPLS.java
@@ -30,13 +30,15 @@
     public static final int HEADER_LENGTH = 4;
 
     public static final byte PROTOCOL_IPV4 = 0x1;
+    public static final byte PROTOCOL_IPV6 = 0x2;
     public static final byte PROTOCOL_MPLS = 0x6;
     // mutable for Testing
     static Map<Byte, Deserializer<? extends IPacket>> protocolDeserializerMap =
             ImmutableMap.<Byte, Deserializer<? extends IPacket>>builder()
-                .put(PROTOCOL_IPV4, IPv4.deserializer())
-                .put(PROTOCOL_MPLS, MPLS.deserializer())
-                .build();
+                    .put(PROTOCOL_IPV6, IPv6.deserializer())
+                    .put(PROTOCOL_IPV4, IPv4.deserializer())
+                    .put(PROTOCOL_MPLS, MPLS.deserializer())
+                    .build();
 
     protected int label; //20bits
     protected byte bos; //1bit
@@ -108,6 +110,22 @@
         this.ttl = ttl;
     }
 
+    @Override
+    public IPacket setPayload(final IPacket payload) {
+        // We implicitly assume that traffic can be only of these three types
+        if (payload instanceof MPLS) {
+            this.bos = 0;
+            this.protocol = PROTOCOL_MPLS;
+        } else if (payload instanceof IPv6) {
+            this.bos = 1;
+            this.protocol = PROTOCOL_IPV6;
+        } else {
+            this.bos = 1;
+            this.protocol = PROTOCOL_IPV4;
+        }
+        return super.setPayload(payload);
+    }
+
     /**
      * Deserializer function for MPLS packets.
      *
@@ -124,7 +142,11 @@
             mpls.label = ((mplsheader & 0xfffff000) >>> 12);
             mpls.bos = (byte) ((mplsheader & 0x00000100) >> 8);
             mpls.ttl = (byte) (mplsheader & 0x000000ff);
-            mpls.protocol = (mpls.bos == 1) ? PROTOCOL_IPV4 : PROTOCOL_MPLS;
+
+            ByteBuffer duplicate = bb.duplicate();
+            short protocol = (short) ((duplicate.get() & 0xf0) >> 4);
+            mpls.protocol = (mpls.bos == 1) ? protocol == 4 ?
+                    PROTOCOL_IPV4 : PROTOCOL_IPV6 : PROTOCOL_MPLS;
 
             Deserializer<? extends IPacket> deserializer;
             if (protocolDeserializerMap.containsKey(mpls.protocol)) {
diff --git a/utils/misc/src/test/java/org/onlab/packet/MplsTest.java b/utils/misc/src/test/java/org/onlab/packet/MplsTest.java
index 4c247fa..88dc6bd 100644
--- a/utils/misc/src/test/java/org/onlab/packet/MplsTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/MplsTest.java
@@ -21,10 +21,18 @@
 import org.junit.Test;
 
 import java.nio.ByteBuffer;
-import java.util.HashMap;
 
+import static junit.framework.TestCase.assertNotNull;
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.onlab.packet.ICMP.TYPE_ECHO_REQUEST;
+import static org.onlab.packet.ICMP6.ECHO_REQUEST;
+import static org.onlab.packet.IPv4.PROTOCOL_ICMP;
+import static org.onlab.packet.MPLS.PROTOCOL_IPV4;
+import static org.onlab.packet.MPLS.PROTOCOL_IPV6;
+import static org.onlab.packet.MPLS.PROTOCOL_MPLS;
 
 /**
  * Unit tests for MPLS class.
@@ -36,22 +44,86 @@
     private int label = 1048575;
     private byte bos = 1;
     private byte ttl = 20;
-    private byte protocol = MPLS.PROTOCOL_IPV4;
+    private byte protocol = PROTOCOL_IPV4;
 
     private byte[] bytes;
+    private byte[] truncatedBytes;
+
+    // Define packets to deserialize
+    private static final MacAddress SRC_MAC = MacAddress.valueOf("00:00:00:00:00:01");
+    private static final MacAddress DST_MAC = MacAddress.valueOf("00:00:00:00:00:02");
+
+    private static final ICMP ICMP = new ICMP()
+            .setIcmpType(TYPE_ECHO_REQUEST);
+
+    private static final Ip4Address SRC_IPV4 = Ip4Address.valueOf("10.0.1.1");
+    private static final Ip4Address DST_IPV4 = Ip4Address.valueOf("10.0.0.254");
+
+    private static final IPv4 IPV4 = (IPv4) new IPv4()
+            .setDestinationAddress(DST_IPV4.toInt())
+            .setSourceAddress(SRC_IPV4.toInt())
+            .setTtl((byte) 64)
+            .setProtocol(PROTOCOL_ICMP)
+            .setPayload(ICMP);
+
+    private static final ICMP6 ICMP6 = new ICMP6()
+            .setIcmpType(ECHO_REQUEST);
+
+    private static final Ip6Address SRC_IPV6 = Ip6Address.valueOf("2000::101");
+    private static final Ip6Address DST_IPV6 = Ip6Address.valueOf("2000::ff");
+
+    private static final IPv6 IPV6 = (IPv6) new IPv6()
+            .setDestinationAddress(DST_IPV6.toOctets())
+            .setSourceAddress(SRC_IPV6.toOctets())
+            .setHopLimit((byte) 255)
+            .setNextHeader(IPv6.PROTOCOL_ICMP6)
+            .setPayload(ICMP6);
+
+    private static final MPLS MPLS_IPV4 = new MPLS();
+    private static final MPLS MPLS_BOS_IPV4 = new MPLS();
+    private static final MPLS MPLS_IPV6 = new MPLS();
+    private static final MPLS MPLS_BOS_IPV6 = new MPLS();
+
+    private static final Ethernet ETH_IPV4 = (Ethernet) new Ethernet()
+            .setEtherType(Ethernet.MPLS_UNICAST)
+            .setDestinationMACAddress(DST_MAC)
+            .setSourceMACAddress(SRC_MAC)
+            .setPayload(MPLS_IPV4);
+
+    private static final Ethernet ETH_IPV6 = (Ethernet) new Ethernet()
+            .setEtherType(Ethernet.MPLS_UNICAST)
+            .setDestinationMACAddress(DST_MAC)
+            .setSourceMACAddress(SRC_MAC)
+            .setPayload(MPLS_IPV6);
 
     @Before
     public void setUp() throws Exception {
-        // Replace normal deserializer map with an empty map. This will cause
-        // the DataDeserializer to be used which will silently handle 0-byte input.
-        MPLS.protocolDeserializerMap = new HashMap<>();
-
+        // Setup packets
         deserializer = MPLS.deserializer();
 
-        ByteBuffer bb = ByteBuffer.allocate(MPLS.HEADER_LENGTH);
+        byte[] ipv4 = IPV4.serialize();
+        ByteBuffer bb = ByteBuffer.allocate(MPLS.HEADER_LENGTH + IPV4.getTotalLength());
         bb.putInt(((label & 0x000fffff) << 12) | ((bos & 0x1) << 8 | (ttl & 0xff)));
+        bb.put(ipv4);
 
         bytes = bb.array();
+
+        bb = ByteBuffer.allocate(MPLS.HEADER_LENGTH);
+        bb.putInt(((label & 0x000fffff) << 12) | ((bos & 0x1) << 8 | (ttl & 0xff)));
+
+        truncatedBytes = bb.array();
+
+        MPLS_BOS_IPV4.setLabel(101);
+        MPLS_BOS_IPV4.setPayload(IPV4);
+        MPLS_IPV4.setLabel(1);
+        MPLS_IPV4.setPayload(MPLS_BOS_IPV4);
+        ETH_IPV4.setPayload(MPLS_IPV4);
+
+        MPLS_BOS_IPV6.setLabel(201);
+        MPLS_BOS_IPV6.setPayload(IPV6);
+        MPLS_IPV6.setLabel(2);
+        MPLS_IPV6.setPayload(MPLS_BOS_IPV6);
+        ETH_IPV6.setPayload(MPLS_IPV6);
     }
 
     @Test
@@ -61,7 +133,7 @@
 
     @Test
     public void testDeserializeTruncated() throws Exception {
-        PacketTestUtils.testDeserializeTruncated(deserializer, bytes);
+        PacketTestUtils.testDeserializeTruncated(deserializer, truncatedBytes);
     }
 
     /**
@@ -90,4 +162,78 @@
         assertTrue(StringUtils.contains(str, "ttl=" + ttl));
         assertTrue(StringUtils.contains(str, "protocol=" + protocol));
     }
+
+    @Test
+    public void testIpv4OverMplsDeserialize() throws Exception {
+        // Serialize
+        byte[] packet = ETH_IPV4.serialize();
+        assertThat(MPLS_IPV4.protocol, is(PROTOCOL_MPLS));
+        assertThat(MPLS_IPV4.bos, is((byte) 0));
+        assertThat(MPLS_BOS_IPV4.protocol, is(PROTOCOL_IPV4));
+        assertThat(MPLS_BOS_IPV4.bos, is((byte) 1));
+        // Deserialize
+        Ethernet ethernet;
+        ethernet = Ethernet.deserializer().deserialize(packet, 0, packet.length);
+
+        // Verify
+        assertNotNull(ethernet);
+        assertThat(ethernet.getSourceMAC(), is(ETH_IPV4.getSourceMAC()));
+        assertThat(ethernet.getDestinationMAC(), is(ETH_IPV4.getDestinationMAC()));
+        assertThat(ethernet.getEtherType(), is(Ethernet.MPLS_UNICAST));
+        assertTrue(ethernet.getPayload() instanceof MPLS);
+        MPLS mpls = (MPLS) ethernet.getPayload();
+        assertThat(mpls.getLabel(), is(1));
+        assertThat(mpls.getTtl(), is((byte) 0));
+        assertThat(mpls.protocol, is(PROTOCOL_MPLS));
+        assertThat(mpls.bos, is((byte) 0));
+        assertTrue(mpls.getPayload() instanceof MPLS);
+        mpls = (MPLS) mpls.getPayload();
+        assertThat(mpls.getLabel(), is(101));
+        assertThat(mpls.getTtl(), is((byte) 0));
+        assertThat(mpls.protocol, is(PROTOCOL_IPV4));
+        assertThat(mpls.bos, is((byte) 1));
+        IPv4 ip = (IPv4) mpls.getPayload();
+        assertThat(ip.getSourceAddress(), is(SRC_IPV4.toInt()));
+        assertThat(ip.getDestinationAddress(), is(DST_IPV4.toInt()));
+        assertTrue(ip.getPayload() instanceof ICMP);
+        ICMP icmp = (ICMP) ip.getPayload();
+        assertThat(icmp.getIcmpType(), is(TYPE_ECHO_REQUEST));
+    }
+
+    @Test
+    public void testIpv6OverMplsDeserialize() throws Exception {
+        // Serialize
+        byte[] packet = ETH_IPV6.serialize();
+        assertThat(MPLS_IPV6.protocol, is(PROTOCOL_MPLS));
+        assertThat(MPLS_IPV6.bos, is((byte) 0));
+        assertThat(MPLS_BOS_IPV6.protocol, is(PROTOCOL_IPV6));
+        assertThat(MPLS_BOS_IPV6.bos, is((byte) 1));
+        // Deserialize
+        Ethernet ethernet;
+        ethernet = Ethernet.deserializer().deserialize(packet, 0, packet.length);
+
+        // Verify
+        assertNotNull(ethernet);
+        assertThat(ethernet.getSourceMAC(), is(ETH_IPV6.getSourceMAC()));
+        assertThat(ethernet.getDestinationMAC(), is(ETH_IPV6.getDestinationMAC()));
+        assertThat(ethernet.getEtherType(), is(Ethernet.MPLS_UNICAST));
+        assertTrue(ethernet.getPayload() instanceof MPLS);
+        MPLS mpls = (MPLS) ethernet.getPayload();
+        assertThat(mpls.getLabel(), is(2));
+        assertThat(mpls.getTtl(), is((byte) 0));
+        assertThat(mpls.protocol, is(PROTOCOL_MPLS));
+        assertThat(mpls.bos, is((byte) 0));
+        assertTrue(mpls.getPayload() instanceof MPLS);
+        mpls = (MPLS) mpls.getPayload();
+        assertThat(mpls.getLabel(), is(201));
+        assertThat(mpls.getTtl(), is((byte) 0));
+        assertThat(mpls.protocol, is(PROTOCOL_IPV6));
+        assertThat(mpls.bos, is((byte) 1));
+        IPv6 ip = (IPv6) mpls.getPayload();
+        assertThat(ip.getSourceAddress(), is(SRC_IPV6.toOctets()));
+        assertThat(ip.getDestinationAddress(), is(DST_IPV6.toOctets()));
+        assertTrue(ip.getPayload() instanceof ICMP6);
+        ICMP6 icmp = (ICMP6) ip.getPayload();
+        assertThat(icmp.getIcmpType(), is(ECHO_REQUEST));
+    }
 }