[CORD-1664] Fix errors when parsing DHCP packets

Change-Id: Ifa9cd3ba04b31f2b7de60fd63dc655978042dbce
diff --git a/utils/misc/src/test/java/org/onlab/packet/dhcp/Dhcp6RelayTest.java b/utils/misc/src/test/java/org/onlab/packet/dhcp/Dhcp6RelayTest.java
index 245a100..79b9a46 100644
--- a/utils/misc/src/test/java/org/onlab/packet/dhcp/Dhcp6RelayTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/dhcp/Dhcp6RelayTest.java
@@ -21,12 +21,15 @@
 import com.google.common.collect.Lists;
 import com.google.common.io.Resources;
 
+import org.apache.commons.io.Charsets;
 import org.junit.Test;
 import org.onlab.packet.DHCP6;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv6;
 import org.onlab.packet.Ip6Address;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.UDP;
 
-import java.util.Collections;
 import java.util.List;
 
 import static org.junit.Assert.*;
@@ -42,9 +45,9 @@
 
     private static final int HOP_COUNT = 0;
     private static final Ip6Address LINK_ADDRESS = Ip6Address.valueOf("2000::2ff");
-    private static final Ip6Address PEER_ADDRESS = Ip6Address.valueOf("fe80::1");
-    private static final int XID_1 = 13346301;
-    private static final int XID_2 = 9807588;
+    private static final Ip6Address PEER_ADDRESS = Ip6Address.valueOf("fe80::2bb:ff:fe00:1");
+    private static final int XID_1 = 8135067;
+    private static final int XID_2 = 14742082;
     private static final int IA_ID = 1;
     private static final int T1_CLIENT = 3600;
     private static final int T2_CLIENT = 5400;
@@ -54,9 +57,18 @@
     private static final int PREFFERRED_LT_SERVER = 375;
     private static final int VALID_LT_SERVER = 600;
     private static final int PREFFERRED_LT_REQ = 7200;
-    private static final int VALID_LT_REQ = 7500;
+    private static final int VALID_LT_REQ = 10800;
+    private static final int VALID_LT_REQ_2 = 7500;
+    private static final MacAddress DOWNSTREAM_MAC = MacAddress.valueOf("4a:c0:c2:78:92:34");
     private static final MacAddress CLIENT_MAC = MacAddress.valueOf("00:bb:00:00:00:01");
-    private static final int CLIENT_DUID_TIME = 0x210016b4;
+    private static final int CLIENT_DUID_TIME = 555636143;
+    private static final MacAddress IPV6_MCAST = MacAddress.valueOf("33:33:00:01:00:03");
+    private static final Ip6Address DOWNSTREAM_LL = Ip6Address.valueOf("fe80::48c0:c2ff:fe78:9234");
+    private static final Ip6Address DHCP6_BRC = Ip6Address.valueOf("ff05::1:3");
+    private static final Ip6Address SERVER_IP = Ip6Address.valueOf("2000::9903");
+    private static final MacAddress SERVER_MAC = MacAddress.valueOf("00:99:66:00:00:01");
+    private static final Ip6Address SERVER_LL = Ip6Address.valueOf("fe80::299:66ff:fe00:1");
+
 
     /**
      * Test deserialize relay message with solicit message.
@@ -66,16 +78,22 @@
     @Test
     public void deserializeSolicit() throws Exception {
         byte[] data = Resources.toByteArray(Dhcp6RelayTest.class.getResource(SOLICIT));
-        DHCP6 relayMsg = DHCP6.deserializer().deserialize(data, 0, data.length);
+        Ethernet eth = Ethernet.deserializer().deserialize(data, 0, data.length);
+        DHCP6 relayMsg = (DHCP6) eth.getPayload().getPayload().getPayload();
         assertEquals(relayMsg.getMsgType(), DHCP6.MsgType.RELAY_FORW.value());
         assertEquals(relayMsg.getHopCount(), HOP_COUNT);
         assertEquals(relayMsg.getIp6LinkAddress(), LINK_ADDRESS);
         assertEquals(relayMsg.getIp6PeerAddress(), PEER_ADDRESS);
 
-        assertEquals(relayMsg.getOptions().size(), 1);
+        assertEquals(relayMsg.getOptions().size(), 2);
         Dhcp6Option option = relayMsg.getOptions().get(0);
+        assertEquals(option.getCode(), DHCP6.OptionCode.SUBSCRIBER_ID.value());
+        assertEquals(option.getLength(), 10);
+        assertArrayEquals(option.getData(), SERVER_IP.toString().getBytes(Charsets.US_ASCII));
+
+        option = relayMsg.getOptions().get(1);
         assertEquals(option.getCode(), DHCP6.OptionCode.RELAY_MSG.value());
-        assertEquals(option.getLength(), 56);
+        assertEquals(option.getLength(), 84);
         assertTrue(option.getPayload() instanceof DHCP6);
 
         DHCP6 relaiedDhcp6 = (DHCP6) option.getPayload();
@@ -113,13 +131,17 @@
         assertTrue(option instanceof Dhcp6IaNaOption);
         Dhcp6IaNaOption iaNaOption = (Dhcp6IaNaOption) option;
         assertEquals(iaNaOption.getCode(), DHCP6.OptionCode.IA_NA.value());
-        assertEquals(iaNaOption.getLength(), 12);
+        assertEquals(iaNaOption.getLength(), 40);
         assertEquals(iaNaOption.getIaId(), IA_ID);
         assertEquals(iaNaOption.getT1(), T1_CLIENT);
         assertEquals(iaNaOption.getT2(), T2_CLIENT);
-        assertEquals(iaNaOption.getOptions().size(), 0);
+        assertEquals(iaNaOption.getOptions().size(), 1);
+        Dhcp6IaAddressOption subOption = (Dhcp6IaAddressOption) iaNaOption.getOptions().get(0);
+        assertEquals(subOption.getIp6Address(), IA_ADDRESS);
+        assertEquals(subOption.getPreferredLifetime(), PREFFERRED_LT_REQ);
+        assertEquals(subOption.getValidLifetime(), VALID_LT_REQ);
 
-        assertArrayEquals(data, relayMsg.serialize());
+        assertArrayEquals(data, eth.serialize());
     }
 
     /**
@@ -169,17 +191,47 @@
         iaNaOption.setIaId(IA_ID);
         iaNaOption.setT1(T1_CLIENT);
         iaNaOption.setT2(T2_CLIENT);
-        iaNaOption.setOptions(Collections.emptyList());
+        Dhcp6IaAddressOption iaAddressOption = new Dhcp6IaAddressOption();
+        iaAddressOption.setIp6Address(IA_ADDRESS);
+        iaAddressOption.setPreferredLifetime(PREFFERRED_LT_REQ);
+        iaAddressOption.setValidLifetime(VALID_LT_REQ);
+        iaNaOption.setOptions(ImmutableList.of(iaAddressOption));
         options.add(iaNaOption);
         relaiedDhcp6.setOptions(options);
 
-
         Dhcp6RelayOption relayOption = new Dhcp6RelayOption();
         relayOption.setPayload(relaiedDhcp6);
 
-        relayMsg.setOptions(ImmutableList.of(relayOption));
+        Dhcp6Option subscriberId = new Dhcp6Option();
+        subscriberId.setCode(DHCP6.OptionCode.SUBSCRIBER_ID.value());
+        subscriberId.setLength((short) 10);
+        subscriberId.setData(SERVER_IP.toString().getBytes(Charsets.US_ASCII));
+
+        relayMsg.setOptions(ImmutableList.of(subscriberId, relayOption));
+
+        UDP udp = new UDP();
+        udp.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
+        udp.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
+        udp.setPayload(relayMsg);
+        udp.setChecksum((short) 0x9a99);
+
+        IPv6 ipv6 = new IPv6();
+        ipv6.setHopLimit((byte) 32);
+        ipv6.setSourceAddress(DOWNSTREAM_LL.toOctets());
+        ipv6.setDestinationAddress(DHCP6_BRC.toOctets());
+        ipv6.setNextHeader(IPv6.PROTOCOL_UDP);
+        ipv6.setTrafficClass((byte) 0);
+        ipv6.setFlowLabel(0x000cbf64);
+        ipv6.setPayload(udp);
+
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(IPV6_MCAST);
+        eth.setSourceMACAddress(DOWNSTREAM_MAC);
+        eth.setEtherType(Ethernet.TYPE_IPV6);
+        eth.setPayload(ipv6);
+
         assertArrayEquals(Resources.toByteArray(Dhcp6RelayTest.class.getResource(SOLICIT)),
-                          relayMsg.serialize());
+                          eth.serialize());
     }
 
     /**
@@ -190,8 +242,8 @@
     @Test
     public void deserializeAdvertise() throws Exception {
         byte[] data = Resources.toByteArray(getClass().getResource(ADVERTISE));
-        DHCP6 relayMsg = DHCP6.deserializer().deserialize(data, 0, data.length);
-
+        Ethernet eth = Ethernet.deserializer().deserialize(data, 0, data.length);
+        DHCP6 relayMsg = (DHCP6) eth.getPayload().getPayload().getPayload();
         assertEquals(relayMsg.getMsgType(), DHCP6.MsgType.RELAY_REPL.value());
         assertEquals(relayMsg.getHopCount(), HOP_COUNT);
         assertEquals(relayMsg.getIp6LinkAddress(), LINK_ADDRESS);
@@ -243,10 +295,13 @@
         option = relaiedDhcp6.getOptions().get(2);
         assertEquals(option.getCode(), DHCP6.OptionCode.SERVERID.value());
         assertEquals(option.getLength(), 14);
-        assertArrayEquals(option.getData(),
-                          new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
-
-        assertArrayEquals(data, relayMsg.serialize());
+        Dhcp6Duid serverDuid =
+                Dhcp6Duid.deserializer().deserialize(option.getData(), 0, option.getData().length);
+        assertEquals(serverDuid.getDuidType(), Dhcp6Duid.DuidType.DUID_LLT);
+        assertEquals(serverDuid.getDuidTime(), 0x211e5340);
+        assertEquals(serverDuid.getHardwareType(), 1);
+        assertArrayEquals(serverDuid.getLinkLayerAddress(), SERVER_MAC.toBytes());
+        assertArrayEquals(data, eth.serialize());
     }
 
     /**
@@ -295,7 +350,12 @@
         Dhcp6Option option = new Dhcp6Option();
         option.setCode(DHCP6.OptionCode.SERVERID.value());
         option.setLength((short) 14);
-        option.setData(new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+        Dhcp6Duid serverDuid = new Dhcp6Duid();
+        serverDuid.setDuidType(Dhcp6Duid.DuidType.DUID_LLT);
+        serverDuid.setLinkLayerAddress(SERVER_MAC.toBytes());
+        serverDuid.setHardwareType((short) 1);
+        serverDuid.setDuidTime(0x211e5340);
+        option.setData(serverDuid.serialize());
         options.add(option);
 
         relaiedDhcp6.setOptions(options);
@@ -305,8 +365,28 @@
 
         relayMsg.setOptions(ImmutableList.of(relayOption));
 
+        UDP udp = new UDP();
+        udp.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
+        udp.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
+        udp.setPayload(relayMsg);
+        udp.setChecksum((short) 0x0000019d);
+
+        IPv6 ipv6 = new IPv6();
+        ipv6.setHopLimit((byte) 64);
+        ipv6.setSourceAddress(SERVER_LL.toOctets());
+        ipv6.setDestinationAddress(DOWNSTREAM_LL.toOctets());
+        ipv6.setNextHeader(IPv6.PROTOCOL_UDP);
+        ipv6.setTrafficClass((byte) 0);
+        ipv6.setFlowLabel(0x000c72ef);
+        ipv6.setPayload(udp);
+
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(DOWNSTREAM_MAC);
+        eth.setSourceMACAddress(SERVER_MAC);
+        eth.setEtherType(Ethernet.TYPE_IPV6);
+        eth.setPayload(ipv6);
         assertArrayEquals(Resources.toByteArray(Dhcp6RelayTest.class.getResource(ADVERTISE)),
-                          relayMsg.serialize());
+                          eth.serialize());
     }
 
     /**
@@ -317,15 +397,20 @@
     @Test
     public void deserializeRequest() throws Exception {
         byte[] data = Resources.toByteArray(getClass().getResource(REQUEST));
-        DHCP6 relayMsg = DHCP6.deserializer().deserialize(data, 0, data.length);
-
+        Ethernet eth = Ethernet.deserializer().deserialize(data, 0, data.length);
+        DHCP6 relayMsg = (DHCP6) eth.getPayload().getPayload().getPayload();
         assertEquals(relayMsg.getMsgType(), DHCP6.MsgType.RELAY_FORW.value());
         assertEquals(relayMsg.getHopCount(), HOP_COUNT);
         assertEquals(relayMsg.getIp6LinkAddress(), LINK_ADDRESS);
         assertEquals(relayMsg.getIp6PeerAddress(), PEER_ADDRESS);
 
-        assertEquals(relayMsg.getOptions().size(), 1);
+        assertEquals(relayMsg.getOptions().size(), 2);
         Dhcp6Option option = relayMsg.getOptions().get(0);
+        assertEquals(option.getCode(), DHCP6.OptionCode.SUBSCRIBER_ID.value());
+        assertEquals(option.getLength(), 10);
+        assertArrayEquals(option.getData(), SERVER_IP.toString().getBytes(Charsets.US_ASCII));
+
+        option = relayMsg.getOptions().get(1);
         assertEquals(option.getCode(), DHCP6.OptionCode.RELAY_MSG.value());
         assertEquals(option.getLength(), 102);
         assertTrue(option.getPayload() instanceof DHCP6);
@@ -350,8 +435,12 @@
         option = relaiedDhcp6.getOptions().get(1);
         assertEquals(option.getCode(), DHCP6.OptionCode.SERVERID.value());
         assertEquals(option.getLength(), 14);
-        assertArrayEquals(option.getData(),
-                          new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+        Dhcp6Duid serverDuid =
+                Dhcp6Duid.deserializer().deserialize(option.getData(), 0, option.getData().length);
+        assertEquals(serverDuid.getDuidType(), Dhcp6Duid.DuidType.DUID_LLT);
+        assertEquals(serverDuid.getDuidTime(), 0x211e5340);
+        assertEquals(serverDuid.getHardwareType(), 1);
+        assertArrayEquals(serverDuid.getLinkLayerAddress(), SERVER_MAC.toBytes());
 
         // Option Request
         option = relaiedDhcp6.getOptions().get(2);
@@ -363,8 +452,7 @@
         option = relaiedDhcp6.getOptions().get(3);
         assertEquals(option.getCode(), DHCP6.OptionCode.ELAPSED_TIME.value());
         assertEquals(option.getLength(), 2);
-        assertArrayEquals(option.getData(),
-                          new byte[]{0, 0});
+        assertArrayEquals(option.getData(), new byte[]{0, 0});
 
         // IA NA
         option = relaiedDhcp6.getOptions().get(4);
@@ -383,10 +471,10 @@
                 (Dhcp6IaAddressOption) iaNaOption.getOptions().get(0);
         assertEquals(iaAddressOption.getIp6Address(), IA_ADDRESS);
         assertEquals(iaAddressOption.getPreferredLifetime(), PREFFERRED_LT_REQ);
-        assertEquals(iaAddressOption.getValidLifetime(), VALID_LT_REQ);
+        assertEquals(iaAddressOption.getValidLifetime(), VALID_LT_REQ_2);
         assertNull(iaAddressOption.getOptions());
 
-        assertArrayEquals(data, relayMsg.serialize());
+        assertArrayEquals(data, eth.serialize());
     }
 
     /**
@@ -421,7 +509,12 @@
         Dhcp6Option option = new Dhcp6Option();
         option.setCode(DHCP6.OptionCode.SERVERID.value());
         option.setLength((short) 14);
-        option.setData(new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+        Dhcp6Duid serverDuid = new Dhcp6Duid();
+        serverDuid.setDuidType(Dhcp6Duid.DuidType.DUID_LLT);
+        serverDuid.setLinkLayerAddress(SERVER_MAC.toBytes());
+        serverDuid.setHardwareType((short) 1);
+        serverDuid.setDuidTime(0x211e5340);
+        option.setData(serverDuid.serialize());
         options.add(option);
 
         // Option request
@@ -442,7 +535,7 @@
         Dhcp6IaAddressOption iaAddressOption = new Dhcp6IaAddressOption();
         iaAddressOption.setIp6Address(IA_ADDRESS);
         iaAddressOption.setPreferredLifetime(PREFFERRED_LT_REQ);
-        iaAddressOption.setValidLifetime(VALID_LT_REQ);
+        iaAddressOption.setValidLifetime(VALID_LT_REQ_2);
 
         // IA NA
         Dhcp6IaNaOption iaNaOption = new Dhcp6IaNaOption();
@@ -454,13 +547,39 @@
 
         relaiedDhcp6.setOptions(options);
 
+        Dhcp6Option subscriberId = new Dhcp6Option();
+        subscriberId.setCode(DHCP6.OptionCode.SUBSCRIBER_ID.value());
+        subscriberId.setLength((short) 10);
+        subscriberId.setData(SERVER_IP.toString().getBytes(Charsets.US_ASCII));
+
         Dhcp6RelayOption relayOption = new Dhcp6RelayOption();
         relayOption.setPayload(relaiedDhcp6);
 
-        relayMsg.setOptions(ImmutableList.of(relayOption));
+        relayMsg.setOptions(ImmutableList.of(subscriberId, relayOption));
+
+        UDP udp = new UDP();
+        udp.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
+        udp.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
+        udp.setPayload(relayMsg);
+        udp.setChecksum((short) 0x9aab);
+
+        IPv6 ipv6 = new IPv6();
+        ipv6.setHopLimit((byte) 32);
+        ipv6.setSourceAddress(DOWNSTREAM_LL.toOctets());
+        ipv6.setDestinationAddress(DHCP6_BRC.toOctets());
+        ipv6.setNextHeader(IPv6.PROTOCOL_UDP);
+        ipv6.setTrafficClass((byte) 0);
+        ipv6.setFlowLabel(0x000cbf64);
+        ipv6.setPayload(udp);
+
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(IPV6_MCAST);
+        eth.setSourceMACAddress(DOWNSTREAM_MAC);
+        eth.setEtherType(Ethernet.TYPE_IPV6);
+        eth.setPayload(ipv6);
 
         assertArrayEquals(Resources.toByteArray(Dhcp6RelayTest.class.getResource(REQUEST)),
-                          relayMsg.serialize());
+                          eth.serialize());
     }
 
     /**
@@ -471,8 +590,8 @@
     @Test
     public void deserializeReply() throws Exception {
         byte[] data = Resources.toByteArray(getClass().getResource(REPLY));
-        DHCP6 relayMsg = DHCP6.deserializer().deserialize(data, 0, data.length);
-
+        Ethernet eth = Ethernet.deserializer().deserialize(data, 0, data.length);
+        DHCP6 relayMsg = (DHCP6) eth.getPayload().getPayload().getPayload();
         assertEquals(relayMsg.getMsgType(), DHCP6.MsgType.RELAY_REPL.value());
         assertEquals(relayMsg.getHopCount(), HOP_COUNT);
         assertEquals(relayMsg.getIp6LinkAddress(), LINK_ADDRESS);
@@ -524,10 +643,14 @@
         option = relaiedDhcp6.getOptions().get(2);
         assertEquals(option.getCode(), DHCP6.OptionCode.SERVERID.value());
         assertEquals(option.getLength(), 14);
-        assertArrayEquals(option.getData(),
-                          new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+        Dhcp6Duid serverDuid =
+                Dhcp6Duid.deserializer().deserialize(option.getData(), 0, option.getData().length);
+        assertEquals(serverDuid.getDuidType(), Dhcp6Duid.DuidType.DUID_LLT);
+        assertEquals(serverDuid.getDuidTime(), 0x211e5340);
+        assertEquals(serverDuid.getHardwareType(), 1);
+        assertArrayEquals(serverDuid.getLinkLayerAddress(), SERVER_MAC.toBytes());
 
-        assertArrayEquals(data, relayMsg.serialize());
+        assertArrayEquals(data, eth.serialize());
     }
 
     @Test
@@ -571,7 +694,12 @@
         Dhcp6Option option = new Dhcp6Option();
         option.setCode(DHCP6.OptionCode.SERVERID.value());
         option.setLength((short) 14);
-        option.setData(new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+        Dhcp6Duid serverDuid = new Dhcp6Duid();
+        serverDuid.setDuidType(Dhcp6Duid.DuidType.DUID_LLT);
+        serverDuid.setLinkLayerAddress(SERVER_MAC.toBytes());
+        serverDuid.setHardwareType((short) 1);
+        serverDuid.setDuidTime(0x211e5340);
+        option.setData(serverDuid.serialize());
         options.add(option);
 
         relaiedDhcp6.setOptions(options);
@@ -581,7 +709,28 @@
 
         relayMsg.setOptions(ImmutableList.of(relayOption));
 
+        UDP udp = new UDP();
+        udp.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
+        udp.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
+        udp.setPayload(relayMsg);
+        udp.setChecksum((short) 0x019d);
+
+        IPv6 ipv6 = new IPv6();
+        ipv6.setHopLimit((byte) 64);
+        ipv6.setSourceAddress(SERVER_LL.toOctets());
+        ipv6.setDestinationAddress(DOWNSTREAM_LL.toOctets());
+        ipv6.setNextHeader(IPv6.PROTOCOL_UDP);
+        ipv6.setTrafficClass((byte) 0);
+        ipv6.setFlowLabel(0x000c72ef);
+        ipv6.setPayload(udp);
+
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(DOWNSTREAM_MAC);
+        eth.setSourceMACAddress(SERVER_MAC);
+        eth.setEtherType(Ethernet.TYPE_IPV6);
+        eth.setPayload(ipv6);
+
         assertArrayEquals(Resources.toByteArray(Dhcp6RelayTest.class.getResource(REPLY)),
-                          relayMsg.serialize());
+                          eth.serialize());
     }
 }