Improve the resiliency of the packet deserialization code.

Packet deserializers now check for malformed input while reading the byte
stream. Deserializers are re-implemented as functions that take a byte array
and return a packet object. The old IPacket.deserialize(...) methods have been
deprecated with the goal of eventually moving to immutable packet objects.
Unit tests have been implemented for all Deserializer functions.

ONOS-1589

Change-Id: I9073d5e6e7991e15d43830cfd810989256b71c56
diff --git a/utils/misc/src/test/java/org/onlab/packet/ArpTest.java b/utils/misc/src/test/java/org/onlab/packet/ArpTest.java
new file mode 100644
index 0000000..6224624
--- /dev/null
+++ b/utils/misc/src/test/java/org/onlab/packet/ArpTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Unit tests for the ARP class.
+ */
+public class ArpTest {
+
+    private Deserializer<ARP> deserializer = ARP.deserializer();
+
+    private final byte hwAddressLength = 6;
+    private final byte protoAddressLength = 4;
+
+    private MacAddress srcMac = MacAddress.valueOf(1);
+    private MacAddress targetMac = MacAddress.valueOf(2);
+    private Ip4Address srcIp = Ip4Address.valueOf(1);
+    private Ip4Address targetIp = Ip4Address.valueOf(2);
+
+    private byte[] byteHeader;
+
+    @Before
+    public void setUp() {
+        ByteBuffer bb = ByteBuffer.allocate(ARP.INITIAL_HEADER_LENGTH +
+                        2 * hwAddressLength + 2 * protoAddressLength);
+        bb.putShort(ARP.HW_TYPE_ETHERNET);
+        bb.putShort(ARP.PROTO_TYPE_IP);
+        bb.put(hwAddressLength);
+        bb.put(protoAddressLength);
+        bb.putShort(ARP.OP_REPLY);
+
+        bb.put(srcMac.toBytes());
+        bb.put(srcIp.toOctets());
+        bb.put(targetMac.toBytes());
+        bb.put(targetIp.toOctets());
+
+        byteHeader = bb.array();
+    }
+
+    @Test
+    public void testDeserializeBadInput() throws Exception {
+        PacketTestUtils.testDeserializeBadInput(deserializer);
+    }
+
+    @Test
+    public void testDeserializeTruncated() throws Exception {
+        PacketTestUtils.testDeserializeTruncated(deserializer, byteHeader);
+    }
+
+    @Test
+    public void testDeserialize() throws Exception {
+        ARP arp = deserializer.deserialize(byteHeader, 0, byteHeader.length);
+
+        assertEquals(ARP.HW_TYPE_ETHERNET, arp.getHardwareType());
+        assertEquals(ARP.PROTO_TYPE_IP, arp.getProtocolType());
+        assertEquals(hwAddressLength, arp.getHardwareAddressLength());
+        assertEquals(protoAddressLength, arp.getProtocolAddressLength());
+        assertEquals(ARP.OP_REPLY, arp.getOpCode());
+
+        assertTrue(Arrays.equals(srcMac.toBytes(), arp.getSenderHardwareAddress()));
+        assertTrue(Arrays.equals(srcIp.toOctets(), arp.getSenderProtocolAddress()));
+        assertTrue(Arrays.equals(targetMac.toBytes(), arp.getTargetHardwareAddress()));
+        assertTrue(Arrays.equals(targetIp.toOctets(), arp.getTargetProtocolAddress()));
+    }
+}
diff --git a/utils/misc/src/test/java/org/onlab/packet/DhcpTest.java b/utils/misc/src/test/java/org/onlab/packet/DhcpTest.java
new file mode 100644
index 0000000..ff48331
--- /dev/null
+++ b/utils/misc/src/test/java/org/onlab/packet/DhcpTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet;
+
+import com.google.common.base.Charsets;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Unit tests for DHCP class.
+ */
+public class DhcpTest {
+
+    private Deserializer<DHCP> deserializer = DHCP.deserializer();
+
+    private byte opCode = 1;
+    private byte hardwareType = 1;
+    private byte hardwareAddressLength = Ethernet.DATALAYER_ADDRESS_LENGTH;
+    private byte hops = 0;
+    private int transactionId = 0x2ed4eb50;
+    private short seconds = 0;
+    private short flags = 0;
+    private int clientIpAddress = 1;
+    private int yourIpAddress = 2;
+    private int serverIpAddress = 3;
+    private int gatewayIpAddress = 4;
+    private byte[] clientHardwareAddress = MacAddress.valueOf(500).toBytes();
+    private String serverName = "test-server";
+    private String bootFileName = "test-file";
+
+    private String hostName = "test-host";
+    private DHCPOption hostNameOption = new DHCPOption();
+
+    private byte[] byteHeader;
+
+    @Before
+    public void setUp() {
+        hostNameOption.setCode((byte) 55);
+        hostNameOption.setLength((byte) hostName.length());
+        hostNameOption.setData(hostName.getBytes(Charsets.US_ASCII));
+
+        // Packet length is the fixed DHCP header plus option length plus an
+        // extra byte to indicate 'end of options'.
+        ByteBuffer bb = ByteBuffer.allocate(DHCP.MIN_HEADER_LENGTH +
+                                                    2 + hostNameOption.getLength()  + 1);
+
+        bb.put(opCode);
+        bb.put(hardwareType);
+        bb.put(hardwareAddressLength);
+        bb.put(hops);
+        bb.putInt(transactionId);
+        bb.putShort(seconds);
+        bb.putShort(flags);
+        bb.putInt(clientIpAddress);
+        bb.putInt(yourIpAddress);
+        bb.putInt(serverIpAddress);
+        bb.putInt(gatewayIpAddress);
+        bb.put(clientHardwareAddress);
+
+        // need 16 bytes of zeros to pad out the client hardware address field
+        bb.put(new byte[16 - hardwareAddressLength]);
+
+        // Put server name and pad out to 64 bytes
+        bb.put(serverName.getBytes(Charsets.US_ASCII));
+        bb.put(new byte[64 - serverName.length()]);
+
+        // Put boot file name and pad out to 128 bytes
+        bb.put(bootFileName.getBytes(Charsets.US_ASCII));
+        bb.put(new byte[128 - bootFileName.length()]);
+
+        // Magic cookie
+        bb.put("DHCP".getBytes(Charsets.US_ASCII));
+
+        bb.put(hostNameOption.getCode());
+        bb.put(hostNameOption.getLength());
+        bb.put(hostNameOption.getData());
+
+        // End of options marker
+        bb.put((byte) (0xff & 255));
+
+        byteHeader = bb.array();
+    }
+
+    @Test
+    public void testDeserializeBadInput() throws Exception {
+        PacketTestUtils.testDeserializeBadInput(deserializer);
+    }
+
+    @Test
+    public void testDeserializeTruncated() throws Exception {
+        PacketTestUtils.testDeserializeTruncated(deserializer, byteHeader);
+    }
+
+    @Test
+    public void testDeserialize() throws Exception {
+        DHCP dhcp = deserializer.deserialize(byteHeader, 0, byteHeader.length);
+
+        assertEquals(opCode, dhcp.opCode);
+        assertEquals(hardwareType, dhcp.hardwareType);
+        assertEquals(hardwareAddressLength, dhcp.hardwareAddressLength);
+        assertEquals(hops, dhcp.hops);
+        assertEquals(transactionId, dhcp.transactionId);
+        assertEquals(seconds, dhcp.seconds);
+        assertEquals(flags, dhcp.flags);
+        assertEquals(clientIpAddress, dhcp.clientIPAddress);
+        assertEquals(yourIpAddress, dhcp.yourIPAddress);
+        assertEquals(serverIpAddress, dhcp.serverIPAddress);
+        assertEquals(gatewayIpAddress, dhcp.gatewayIPAddress);
+        assertTrue(Arrays.equals(clientHardwareAddress, dhcp.clientHardwareAddress));
+
+        assertEquals(serverName, dhcp.serverName);
+        assertEquals(bootFileName, dhcp.bootFileName);
+        assertEquals(1, dhcp.options.size());
+        assertEquals(hostNameOption, dhcp.options.get(0));
+    }
+
+}
diff --git a/utils/misc/src/test/java/org/onlab/packet/EthernetTest.java b/utils/misc/src/test/java/org/onlab/packet/EthernetTest.java
new file mode 100644
index 0000000..15a01fc
--- /dev/null
+++ b/utils/misc/src/test/java/org/onlab/packet/EthernetTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Unit tests for the Ethernet class.
+ */
+public class EthernetTest {
+
+    private MacAddress dstMac;
+    private MacAddress srcMac;
+    private short ethertype = 6;
+    private short vlan = 5;
+
+    private Deserializer<Ethernet> deserializer;
+
+    private byte[] byteHeader;
+    private byte[] vlanByteHeader;
+
+    @Before
+    public void setUp() {
+        deserializer = Ethernet.deserializer();
+
+        byte[] dstMacBytes = {
+                (byte) 0x88, (byte) 0x88, (byte) 0x88, (byte) 0x88, (byte) 0x88,
+                (byte) 0x88 };
+        dstMac = MacAddress.valueOf(dstMacBytes);
+        byte[] srcMacBytes = {
+                (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
+                (byte) 0xaa };
+        srcMac = MacAddress.valueOf(srcMacBytes);
+
+        // Create Ethernet byte array with no VLAN header
+        ByteBuffer bb = ByteBuffer.allocate(Ethernet.ETHERNET_HEADER_LENGTH);
+        bb.put(dstMacBytes);
+        bb.put(srcMacBytes);
+        bb.putShort(ethertype);
+
+        byteHeader = bb.array();
+
+        // Create Ethernet byte array with a VLAN header
+        bb = ByteBuffer.allocate(Ethernet.ETHERNET_HEADER_LENGTH + Ethernet.VLAN_HEADER_LENGTH);
+        bb.put(dstMacBytes);
+        bb.put(srcMacBytes);
+        bb.putShort(Ethernet.TYPE_VLAN);
+        bb.putShort(vlan);
+        bb.putShort(ethertype);
+
+        vlanByteHeader = bb.array();
+    }
+
+    @Test
+    public void testDeserializeBadInput() throws Exception {
+        PacketTestUtils.testDeserializeBadInput(deserializer);
+    }
+
+    @Test
+    public void testDeserializeTruncated() throws DeserializationException {
+        PacketTestUtils.testDeserializeTruncated(deserializer, vlanByteHeader);
+    }
+
+    @Test
+    public void testDeserializeNoVlan() throws Exception {
+        Ethernet eth = deserializer.deserialize(byteHeader, 0, byteHeader.length);
+
+        assertEquals(dstMac, eth.getDestinationMAC());
+        assertEquals(srcMac, eth.getSourceMAC());
+        assertEquals(Ethernet.VLAN_UNTAGGED, eth.getVlanID());
+        assertEquals(ethertype, eth.getEtherType());
+    }
+
+    @Test
+    public void testDeserializeWithVlan() throws Exception {
+        Ethernet eth = deserializer.deserialize(vlanByteHeader, 0, vlanByteHeader.length);
+
+        assertEquals(dstMac, eth.getDestinationMAC());
+        assertEquals(srcMac, eth.getSourceMAC());
+        assertEquals(vlan, eth.getVlanID());
+        assertEquals(ethertype, eth.getEtherType());
+    }
+
+}
diff --git a/utils/misc/src/test/java/org/onlab/packet/ICMP6Test.java b/utils/misc/src/test/java/org/onlab/packet/ICMP6Test.java
index 1a6308e..39ddc24 100644
--- a/utils/misc/src/test/java/org/onlab/packet/ICMP6Test.java
+++ b/utils/misc/src/test/java/org/onlab/packet/ICMP6Test.java
@@ -67,13 +67,22 @@
         assertArrayEquals(bytePacket, icmp6.serialize());
     }
 
+    @Test
+    public void testDeserializeBadInput() throws Exception {
+        PacketTestUtils.testDeserializeBadInput(ICMP6.deserializer());
+    }
+
+    @Test
+    public void testDeserializeTruncated() throws Exception {
+        PacketTestUtils.testDeserializeTruncated(ICMP6.deserializer(), bytePacket);
+    }
+
     /**
      * Tests deserialize and getters.
      */
     @Test
-    public void testDeserialize() {
-        ICMP6 icmp6 = new ICMP6();
-        icmp6.deserialize(bytePacket, 0, bytePacket.length);
+    public void testDeserialize() throws Exception {
+        ICMP6 icmp6 = ICMP6.deserializer().deserialize(bytePacket, 0, bytePacket.length);
 
         assertThat(icmp6.getIcmpType(), is(ICMP6.ECHO_REQUEST));
         assertThat(icmp6.getIcmpCode(), is((byte) 0x00));
diff --git a/utils/misc/src/test/java/org/onlab/packet/ICMPTest.java b/utils/misc/src/test/java/org/onlab/packet/ICMPTest.java
new file mode 100644
index 0000000..8514aa5
--- /dev/null
+++ b/utils/misc/src/test/java/org/onlab/packet/ICMPTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Unit tests for the ICMP class.
+ */
+public class ICMPTest {
+
+    private Deserializer<ICMP> deserializer;
+
+    private byte icmpType = ICMP.TYPE_ECHO_REQUEST;
+    private byte icmpCode = 4;
+    private short checksum = 870;
+
+    private byte[] headerBytes;
+
+    @Before
+    public void setUp() throws Exception {
+        deserializer = ICMP.deserializer();
+
+        ByteBuffer bb = ByteBuffer.allocate(ICMP.ICMP_HEADER_LENGTH);
+
+        bb.put(icmpType);
+        bb.put(icmpCode);
+        bb.putShort(checksum);
+
+        headerBytes = bb.array();
+    }
+
+    @Test
+    public void testDeserializeBadInput() throws Exception {
+        PacketTestUtils.testDeserializeBadInput(deserializer);
+    }
+
+    @Test
+    public void testDeserializeTruncated() throws Exception {
+        PacketTestUtils.testDeserializeTruncated(deserializer, headerBytes);
+    }
+
+    @Test
+    public void testDeserialize() throws Exception {
+        ICMP icmp = deserializer.deserialize(headerBytes, 0, headerBytes.length);
+
+        assertEquals(icmpType, icmp.getIcmpType());
+        assertEquals(icmpCode, icmp.getIcmpCode());
+        assertEquals(checksum, icmp.getChecksum());
+    }
+}
diff --git a/utils/misc/src/test/java/org/onlab/packet/IPv4Test.java b/utils/misc/src/test/java/org/onlab/packet/IPv4Test.java
new file mode 100644
index 0000000..1bacf2a
--- /dev/null
+++ b/utils/misc/src/test/java/org/onlab/packet/IPv4Test.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Unit tests for IPv4 class.
+ */
+public class IPv4Test {
+
+    private Deserializer<IPv4> deserializer;
+
+    private byte version = 4;
+    private byte headerLength = 6;
+    private byte diffServ = 2;
+    private short totalLength = 20;
+    private short identification = 1;
+    private byte flags = 1;
+    private short fragmentOffset = 1;
+    private byte ttl = 60;
+    private byte protocol = 4;
+    private short checksum = 4;
+    private int sourceAddress = 1;
+    private int destinationAddress = 2;
+    private byte[] options = new byte[] {0x1, 0x2, 0x3, 0x4};
+
+    private byte[] headerBytes;
+
+    @Before
+    public void setUp() throws Exception {
+        deserializer = IPv4.deserializer();
+
+        ByteBuffer bb = ByteBuffer.allocate(headerLength * 4);
+
+        bb.put((byte) ((version & 0xf) << 4 | headerLength & 0xf));
+        bb.put(diffServ);
+        bb.putShort(totalLength);
+        bb.putShort(identification);
+        bb.putShort((short) ((flags & 0x7) << 13 | fragmentOffset & 0x1fff));
+        bb.put(ttl);
+        bb.put(protocol);
+        bb.putShort(checksum);
+        bb.putInt(sourceAddress);
+        bb.putInt(destinationAddress);
+        bb.put(options);
+
+        headerBytes = bb.array();
+    }
+
+    @Test
+    public void testDeserializeBadInput() throws Exception {
+        PacketTestUtils.testDeserializeBadInput(deserializer);
+    }
+
+    @Test
+    public void testDeserializeTruncated() throws Exception {
+        PacketTestUtils.testDeserializeTruncated(deserializer, headerBytes);
+    }
+
+    @Test
+    public void testDeserialize() throws Exception {
+        IPv4 ipv4 = deserializer.deserialize(headerBytes, 0, headerBytes.length);
+
+        assertEquals(version, ipv4.getVersion());
+        assertEquals(headerLength, ipv4.getHeaderLength());
+        assertEquals(diffServ, ipv4.getDiffServ());
+        assertEquals(totalLength, ipv4.getTotalLength());
+        assertEquals(identification, ipv4.getIdentification());
+        assertEquals(flags, ipv4.getFlags());
+        assertEquals(fragmentOffset, ipv4.getFragmentOffset());
+        assertEquals(ttl, ipv4.getTtl());
+        assertEquals(protocol, ipv4.getProtocol());
+        assertEquals(checksum, ipv4.getChecksum());
+        assertEquals(sourceAddress, ipv4.getSourceAddress());
+        assertEquals(destinationAddress, ipv4.getDestinationAddress());
+        assertTrue(ipv4.isTruncated());
+    }
+}
diff --git a/utils/misc/src/test/java/org/onlab/packet/IPv6Test.java b/utils/misc/src/test/java/org/onlab/packet/IPv6Test.java
index 2e12d5b..720a4d2 100644
--- a/utils/misc/src/test/java/org/onlab/packet/IPv6Test.java
+++ b/utils/misc/src/test/java/org/onlab/packet/IPv6Test.java
@@ -18,14 +18,17 @@
 
 package org.onlab.packet;
 
+import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import java.nio.ByteBuffer;
+
 import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 
 /**
  * Tests for class {@link IPv6}.
@@ -43,6 +46,8 @@
     private static UDP udp;
     private static byte[] bytePacket;
 
+    private Deserializer<IPv6> deserializer;
+
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
         data = new Data();
@@ -65,6 +70,11 @@
         System.arraycopy(bytePayload, 0, bytePacket, byteHeader.length, bytePayload.length);
     }
 
+    @Before
+    public void setUp() {
+        deserializer = IPv6.deserializer();
+    }
+
     /**
      * Tests serialize and setters.
      */
@@ -83,13 +93,26 @@
         assertArrayEquals(ipv6.serialize(), bytePacket);
     }
 
+    @Test
+    public void testDeserializeBadInput() throws Exception {
+        PacketTestUtils.testDeserializeBadInput(deserializer);
+    }
+
+    @Test
+    public void testDeserializeTruncated() throws Exception {
+        // Run the truncation test only on the IPv6 header
+        byte[] ipv6Header = new byte[IPv6.FIXED_HEADER_LENGTH];
+        ByteBuffer.wrap(bytePacket).get(ipv6Header);
+
+        PacketTestUtils.testDeserializeTruncated(deserializer, ipv6Header);
+    }
+
     /**
      * Tests deserialize and getters.
      */
     @Test
-    public void testDeserialize() {
-        IPv6 ipv6 = new IPv6();
-        ipv6.deserialize(bytePacket, 0, bytePacket.length);
+    public void testDeserialize() throws DeserializationException {
+        IPv6 ipv6 = deserializer.deserialize(bytePacket, 0, bytePacket.length);
 
         assertThat(ipv6.getVersion(), is((byte) 6));
         assertThat(ipv6.getTrafficClass(), is((byte) 0x93));
diff --git a/utils/misc/src/test/java/org/onlab/packet/LLCTest.java b/utils/misc/src/test/java/org/onlab/packet/LLCTest.java
new file mode 100644
index 0000000..39bb72f
--- /dev/null
+++ b/utils/misc/src/test/java/org/onlab/packet/LLCTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Unit tests for LLC class.
+ */
+public class LLCTest {
+
+    private Deserializer<LLC> deserializer;
+
+    private byte dsap = 10;
+    private byte ssap = 20;
+    private byte ctrl = 30;
+
+    private byte[] bytes;
+
+    @Before
+    public void setUp() throws Exception {
+        deserializer = LLC.deserializer();
+
+        ByteBuffer bb = ByteBuffer.allocate(LLC.LLC_HEADER_LENGTH);
+
+        bb.put(dsap);
+        bb.put(ssap);
+        bb.put(ctrl);
+
+        bytes = bb.array();
+    }
+
+    @Test
+    public void testDeserializeBadInput() throws Exception {
+        PacketTestUtils.testDeserializeBadInput(deserializer);
+    }
+
+    @Test
+    public void testDeserializeTruncated() throws Exception {
+        PacketTestUtils.testDeserializeTruncated(deserializer, bytes);
+    }
+
+    @Test
+    public void testDeserialize() throws Exception {
+        LLC llc = deserializer.deserialize(bytes, 0, bytes.length);
+
+        assertEquals(dsap, llc.getDsap());
+        assertEquals(ssap, llc.getSsap());
+        assertEquals(ctrl, llc.getCtrl());
+    }
+}
diff --git a/utils/misc/src/test/java/org/onlab/packet/LLDPTest.java b/utils/misc/src/test/java/org/onlab/packet/LLDPTest.java
new file mode 100644
index 0000000..95b0b04
--- /dev/null
+++ b/utils/misc/src/test/java/org/onlab/packet/LLDPTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Unit tests for the LLDP class.
+ */
+public class LLDPTest {
+
+    private Deserializer<LLDP> deserializer;
+
+    private byte[] chassisValue = new byte[] {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7};
+    private byte[] portValue = new byte[] {0x1, 0x2, 0x3, 0x4, 0x5};
+    private byte[] ttlValue = new byte[] {0x0, 0x20};
+
+    private short optionalTlvSize = 6;
+    private byte[] optionalTlvValue = new byte[] {0x6, 0x5, 0x4, 0x3, 0x2, 0x1};
+
+    private byte[] bytes;
+
+    @Before
+    public void setUp() throws Exception {
+        deserializer = LLDP.deserializer();
+
+        // Each TLV is 2 bytes for the type+length, plus the size of the value
+        // There are 2 zero-bytes at the end
+        ByteBuffer bb = ByteBuffer.allocate(2 + LLDP.CHASSIS_TLV_SIZE +
+                                            2 + LLDP.PORT_TLV_SIZE +
+                                            2 + LLDP.TTL_TLV_SIZE +
+                                            2 + optionalTlvSize +
+                                            2);
+
+        // Chassis TLV
+        bb.putShort(getTypeLength(LLDP.CHASSIS_TLV_TYPE, LLDP.CHASSIS_TLV_SIZE));
+        bb.put(chassisValue);
+
+        // Port TLV
+        bb.putShort(getTypeLength(LLDP.PORT_TLV_TYPE, LLDP.PORT_TLV_SIZE));
+        bb.put(portValue);
+
+        // TTL TLV
+        bb.putShort(getTypeLength(LLDP.TTL_TLV_TYPE, LLDP.TTL_TLV_SIZE));
+        bb.put(ttlValue);
+
+        // Optional TLV
+        bb.putShort(getTypeLength(LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE, optionalTlvSize));
+        bb.put(optionalTlvValue);
+
+        bb.putShort((short) 0);
+
+        bytes = bb.array();
+
+    }
+
+    private short getTypeLength(byte type, short length) {
+        return (short) ((0x7f & type) << 9 | 0x1ff & length);
+    }
+
+    @Test
+    public void testDeserializeBadInput() throws Exception {
+        PacketTestUtils.testDeserializeBadInput(deserializer);
+    }
+
+    @Test
+    public void testDeserializeTruncated() throws Exception {
+        PacketTestUtils.testDeserializeTruncated(deserializer, bytes);
+    }
+
+    @Test
+    public void testDeserialize() throws Exception {
+        LLDP lldp = deserializer.deserialize(bytes, 0, bytes.length);
+
+        assertEquals(LLDP.CHASSIS_TLV_TYPE, lldp.getChassisId().getType());
+        assertEquals(LLDP.CHASSIS_TLV_SIZE, lldp.getChassisId().getLength());
+        assertTrue(Arrays.equals(chassisValue, lldp.getChassisId().getValue()));
+
+        assertEquals(LLDP.PORT_TLV_TYPE, lldp.getPortId().getType());
+        assertEquals(LLDP.PORT_TLV_SIZE, lldp.getPortId().getLength());
+        assertTrue(Arrays.equals(portValue, lldp.getPortId().getValue()));
+
+        assertEquals(LLDP.TTL_TLV_TYPE, lldp.getTtl().getType());
+        assertEquals(LLDP.TTL_TLV_SIZE, lldp.getTtl().getLength());
+        assertTrue(Arrays.equals(ttlValue, lldp.getTtl().getValue()));
+
+        assertEquals(1, lldp.getOptionalTLVList().size());
+        LLDPTLV optionalTlv = lldp.getOptionalTLVList().get(0);
+
+        assertEquals(LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE, optionalTlv.getType());
+        assertEquals(optionalTlvSize, optionalTlv.getLength());
+        assertTrue(Arrays.equals(optionalTlvValue, optionalTlv.getValue()));
+    }
+}
diff --git a/utils/misc/src/test/java/org/onlab/packet/MplsTest.java b/utils/misc/src/test/java/org/onlab/packet/MplsTest.java
new file mode 100644
index 0000000..2ab8ff9
--- /dev/null
+++ b/utils/misc/src/test/java/org/onlab/packet/MplsTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Unit tests for MPLS class.
+ */
+public class MplsTest {
+
+    private Deserializer<MPLS> deserializer;
+
+    private int label = 1048575;
+    private byte bos = 1;
+    private byte ttl = 20;
+    private byte protocol = MPLS.PROTOCOL_IPV4;
+
+    private byte[] bytes;
+
+    @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<>();
+
+        deserializer = MPLS.deserializer();
+
+        ByteBuffer bb = ByteBuffer.allocate(MPLS.HEADER_LENGTH);
+        bb.putInt(((label & 0x000fffff) << 12) | ((bos & 0x1) << 8 | (ttl & 0xff)));
+
+        bytes = bb.array();
+    }
+
+    @Test
+    public void testDeserializeBadInput() throws Exception {
+        PacketTestUtils.testDeserializeBadInput(deserializer);
+    }
+
+    @Test
+    public void testDeserializeTruncated() throws Exception {
+        PacketTestUtils.testDeserializeTruncated(deserializer, bytes);
+    }
+
+    @Test
+    public void testDeserialize() throws Exception {
+        MPLS mpls = deserializer.deserialize(bytes, 0, bytes.length);
+
+        assertEquals(label, mpls.label);
+        assertEquals(bos, mpls.bos);
+        assertEquals(ttl, mpls.ttl);
+        assertEquals(protocol, mpls.protocol);
+    }
+}
diff --git a/utils/misc/src/test/java/org/onlab/packet/PacketTestUtils.java b/utils/misc/src/test/java/org/onlab/packet/PacketTestUtils.java
new file mode 100644
index 0000000..209b1d2
--- /dev/null
+++ b/utils/misc/src/test/java/org/onlab/packet/PacketTestUtils.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+
+import static junit.framework.TestCase.fail;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Utilities for testing packet methods.
+ */
+public final class PacketTestUtils {
+
+    private PacketTestUtils() {
+    }
+
+    /**
+     * Tests that the Deserializer function is resilient to bad input parameters
+     * such as null input, negative offset and length, etc.
+     *
+     * @param deserializer deserializer function to test
+     */
+    public static void testDeserializeBadInput(Deserializer deserializer) {
+        byte[] bytes = ByteBuffer.allocate(4).array();
+
+        try {
+            deserializer.deserialize(null, 0, 4);
+            fail("NullPointerException was not thrown");
+        } catch (NullPointerException e) {
+            assertTrue(true);
+        } catch (DeserializationException e) {
+            fail("NullPointerException was not thrown");
+        }
+
+        // input byte array length, offset and length don't make sense
+        expectDeserializationException(deserializer, bytes, -1, 0);
+        expectDeserializationException(deserializer, bytes, 0, -1);
+        expectDeserializationException(deserializer, bytes, 0, 5);
+        expectDeserializationException(deserializer, bytes, 2, 3);
+        expectDeserializationException(deserializer, bytes, 5, 0);
+    }
+
+    /**
+     * Tests that the Deserializer function is resilient to truncated input, or
+     * cases where the input byte array does not contain enough bytes to
+     * deserialize the packet.
+     *
+     * @param deserializer deserializer function to test
+     * @param header byte array of a full-size packet
+     */
+    public static void testDeserializeTruncated(Deserializer deserializer,
+                                                byte[] header) {
+        byte[] truncated;
+
+        for (int i = 0; i < header.length; i++) {
+            truncated = new byte[i];
+
+            ByteBuffer.wrap(header).get(truncated);
+
+            expectDeserializationException(deserializer, truncated, 0, truncated.length);
+        }
+    }
+
+    /**
+     * Run the given deserializer function against the given inputs and verify
+     * that a DeserializationException is thrown. The the test will fail if a
+     * DeserializationException is not thrown by the deserializer function.
+     *
+     * @param deserializer deserializer function to test
+     * @param bytes input byte array
+     * @param offset input offset
+     * @param length input length
+     */
+    public static void expectDeserializationException(Deserializer deserializer,
+                                                      byte[] bytes, int offset, int length) {
+        try {
+            deserializer.deserialize(bytes, offset, length);
+            fail("DeserializationException was not thrown");
+        } catch (DeserializationException e) {
+            assertTrue(true);
+        }
+    }
+}
diff --git a/utils/misc/src/test/java/org/onlab/packet/TCPTest.java b/utils/misc/src/test/java/org/onlab/packet/TCPTest.java
index 8bc3212..18f532e 100644
--- a/utils/misc/src/test/java/org/onlab/packet/TCPTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/TCPTest.java
@@ -67,8 +67,12 @@
             (byte) 0x00, (byte) 0x01  // urgent
     };
 
+    private static Deserializer<TCP> deserializer;
+
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
+        deserializer = TCP.deserializer();
+
         ipv4.setSourceAddress(IPv4.toIPv4Address(IPV4_SOURCE_ADDRESS));
         ipv4.setDestinationAddress(IPv4.toIPv4Address(IPV4_DESTINATION_ADDRESS));
         ipv4.setProtocol(IPv4.PROTOCOL_TCP);
@@ -100,13 +104,22 @@
         assertArrayEquals(bytePacketTCP6, tcp.serialize());
     }
 
+    @Test
+    public void testDeserializeBadInput() throws Exception {
+        PacketTestUtils.testDeserializeBadInput(deserializer);
+    }
+
+    @Test
+    public void testDeserializeTruncated() throws Exception {
+        PacketTestUtils.testDeserializeTruncated(deserializer, bytePacketTCP4);
+    }
+
     /**
      * Tests deserialize and getters.
      */
     @Test
-    public void testDeserialize() {
-        TCP tcp = new TCP();
-        tcp.deserialize(bytePacketTCP4, 0, bytePacketTCP4.length);
+    public void testDeserialize() throws Exception {
+        TCP tcp = deserializer.deserialize(bytePacketTCP4, 0, bytePacketTCP4.length);
 
         assertThat(tcp.getSourcePort(), is((short) 0x50));
         assertThat(tcp.getDestinationPort(), is((short) 0x60));
diff --git a/utils/misc/src/test/java/org/onlab/packet/UDPTest.java b/utils/misc/src/test/java/org/onlab/packet/UDPTest.java
index 974fa75..86363fa 100644
--- a/utils/misc/src/test/java/org/onlab/packet/UDPTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/UDPTest.java
@@ -61,8 +61,12 @@
             (byte) 0x02, (byte) 0x2a, // checksum
     };
 
+    private static Deserializer<UDP> deserializer;
+
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
+        deserializer = UDP.deserializer();
+
         ipv4.setSourceAddress(IPv4.toIPv4Address(IPV4_SOURCE_ADDRESS));
         ipv4.setDestinationAddress(IPv4.toIPv4Address(IPV4_DESTINATION_ADDRESS));
         ipv4.setProtocol(IPv4.PROTOCOL_UDP);
@@ -88,13 +92,22 @@
         assertArrayEquals(bytePacketUDP6, udp.serialize());
     }
 
+    @Test
+    public void testDeserializeBadInput() throws Exception {
+        PacketTestUtils.testDeserializeBadInput(deserializer);
+    }
+
+    @Test
+    public void testDeserializeTruncated() throws Exception {
+        PacketTestUtils.testDeserializeTruncated(deserializer, bytePacketUDP4);
+    }
+
     /**
      * Tests deserialize and getters.
      */
     @Test
-    public void testDeserialize() {
-        UDP udp = new UDP();
-        udp.deserialize(bytePacketUDP4, 0, bytePacketUDP4.length);
+    public void testDeserialize() throws Exception {
+        UDP udp = deserializer.deserialize(bytePacketUDP4, 0, bytePacketUDP4.length);
 
         assertThat(udp.getSourcePort(), is((short) 0x50));
         assertThat(udp.getDestinationPort(), is((short) 0x60));
diff --git a/utils/misc/src/test/java/org/onlab/packet/ipv6/AuthenticationTest.java b/utils/misc/src/test/java/org/onlab/packet/ipv6/AuthenticationTest.java
index a6edc5d..39ab910 100644
--- a/utils/misc/src/test/java/org/onlab/packet/ipv6/AuthenticationTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/ipv6/AuthenticationTest.java
@@ -16,9 +16,11 @@
 
 package org.onlab.packet.ipv6;
 
+import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.onlab.packet.Data;
+import org.onlab.packet.Deserializer;
 import org.onlab.packet.UDP;
 
 import static org.hamcrest.Matchers.is;
@@ -38,6 +40,8 @@
     };
     private static byte[] bytePacket;
 
+    private Deserializer<Authentication> deserializer;
+
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
         data = new Data();
@@ -57,6 +61,11 @@
         System.arraycopy(bytePayload, 0, bytePacket, byteHeader.length, bytePayload.length);
     }
 
+    @Before
+    public void setUp() {
+        deserializer = Authentication.deserializer();
+    }
+
     /**
      * Tests serialize and setters.
      */
@@ -77,9 +86,8 @@
      * Tests deserialize and getters.
      */
     @Test
-    public void testDeserialize() {
-        Authentication auth = new Authentication();
-        auth.deserialize(bytePacket, 0, bytePacket.length);
+    public void testDeserialize() throws Exception {
+        Authentication auth = deserializer.deserialize(bytePacket, 0, bytePacket.length);
 
         assertThat(auth.getNextHeader(), is((byte) 0x11));
         assertThat(auth.getPayloadLength(), is((byte) 0x02));
diff --git a/utils/misc/src/test/java/org/onlab/packet/ipv6/BaseOptionsTest.java b/utils/misc/src/test/java/org/onlab/packet/ipv6/BaseOptionsTest.java
index 885b4af..bb91e9e 100644
--- a/utils/misc/src/test/java/org/onlab/packet/ipv6/BaseOptionsTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/ipv6/BaseOptionsTest.java
@@ -16,9 +16,11 @@
 
 package org.onlab.packet.ipv6;
 
+import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.onlab.packet.Data;
+import org.onlab.packet.Deserializer;
 import org.onlab.packet.IPv6;
 import org.onlab.packet.UDP;
 
@@ -40,6 +42,8 @@
     };
     private static byte[] bytePacket;
 
+    private Deserializer<BaseOptions> deserializer;
+
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
         data = new Data();
@@ -57,6 +61,11 @@
         System.arraycopy(bytePayload, 0, bytePacket, byteHeader.length, bytePayload.length);
     }
 
+    @Before
+    public void setUp() {
+        deserializer = BaseOptions.deserializer();
+    }
+
     /**
      * Tests serialize and setters.
      */
@@ -75,9 +84,8 @@
      * Tests deserialize and getters.
      */
     @Test
-    public void testDeserialize() {
-        BaseOptions baseopt = new BaseOptions();
-        baseopt.deserialize(bytePacket, 0, bytePacket.length);
+    public void testDeserialize() throws Exception {
+        BaseOptions baseopt = deserializer.deserialize(bytePacket, 0, bytePacket.length);
 
         assertThat(baseopt.getNextHeader(), is((byte) 0x11));
         assertThat(baseopt.getHeaderExtLength(), is((byte) 0x00));
diff --git a/utils/misc/src/test/java/org/onlab/packet/ipv6/EncapSecurityPayloadTest.java b/utils/misc/src/test/java/org/onlab/packet/ipv6/EncapSecurityPayloadTest.java
index 294dffb..e0e9919 100644
--- a/utils/misc/src/test/java/org/onlab/packet/ipv6/EncapSecurityPayloadTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/ipv6/EncapSecurityPayloadTest.java
@@ -16,9 +16,13 @@
 
 package org.onlab.packet.ipv6;
 
+import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.onlab.packet.Data;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+
 import java.util.Arrays;
 
 import static org.hamcrest.Matchers.is;
@@ -35,6 +39,8 @@
     private static byte[] dataByte = new byte[32];
     private static byte[] bytePacket;
 
+    private Deserializer<EncapSecurityPayload> deserializer;
+
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
         Arrays.fill(dataByte, (byte) 0xff);
@@ -50,6 +56,11 @@
         System.arraycopy(bytePayload, 0, bytePacket, byteHeader.length, bytePayload.length);
     }
 
+    @Before
+    public void setUp() {
+        deserializer = EncapSecurityPayload.deserializer();
+    }
+
     /**
      * Tests serialize and setters.
      */
@@ -67,9 +78,8 @@
      * Tests deserialize and getters.
      */
     @Test
-    public void testDeserialize() {
-        EncapSecurityPayload esp = new EncapSecurityPayload();
-        esp.deserialize(bytePacket, 0, bytePacket.length);
+    public void testDeserialize() throws DeserializationException {
+        EncapSecurityPayload esp = deserializer.deserialize(bytePacket, 0, bytePacket.length);
 
         assertThat(esp.getSecurityParamIndex(), is(0x13572468));
         assertThat(esp.getSequence(), is(0xffff00));
diff --git a/utils/misc/src/test/java/org/onlab/packet/ipv6/FragmentTest.java b/utils/misc/src/test/java/org/onlab/packet/ipv6/FragmentTest.java
index a7c2492..f2d5e48 100644
--- a/utils/misc/src/test/java/org/onlab/packet/ipv6/FragmentTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/ipv6/FragmentTest.java
@@ -16,9 +16,12 @@
 
 package org.onlab.packet.ipv6;
 
+import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.onlab.packet.Data;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
 import org.onlab.packet.UDP;
 
 import static org.hamcrest.Matchers.is;
@@ -35,6 +38,8 @@
     private static UDP udp;
     private static byte[] bytePacket;
 
+    private Deserializer<Fragment> deserializer;
+
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
         data = new Data();
@@ -52,6 +57,11 @@
         System.arraycopy(bytePayload, 0, bytePacket, byteHeader.length, bytePayload.length);
     }
 
+    @Before
+    public void setUp() {
+        deserializer = Fragment.deserializer();
+    }
+
     /**
      * Tests serialize and setters.
      */
@@ -71,9 +81,8 @@
      * Tests deserialize and getters.
      */
     @Test
-    public void testDeserialize() {
-        Fragment frag = new Fragment();
-        frag.deserialize(bytePacket, 0, bytePacket.length);
+    public void testDeserialize() throws DeserializationException {
+        Fragment frag = deserializer.deserialize(bytePacket, 0, bytePacket.length);
 
         assertThat(frag.getNextHeader(), is((byte) 0x11));
         assertThat(frag.getFragmentOffset(), is((short) 0x1f));
diff --git a/utils/misc/src/test/java/org/onlab/packet/ipv6/RoutingTest.java b/utils/misc/src/test/java/org/onlab/packet/ipv6/RoutingTest.java
index 2dc71c8..a03bacc 100644
--- a/utils/misc/src/test/java/org/onlab/packet/ipv6/RoutingTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/ipv6/RoutingTest.java
@@ -16,9 +16,12 @@
 
 package org.onlab.packet.ipv6;
 
+import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.onlab.packet.Data;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
 import org.onlab.packet.UDP;
 
 import static org.hamcrest.Matchers.is;
@@ -42,6 +45,8 @@
     };
     private static byte[] bytePacket;
 
+    private Deserializer<Routing> deserializer;
+
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
         data = new Data();
@@ -63,6 +68,11 @@
         System.arraycopy(bytePayload, 0, bytePacket, byteHeader.length, bytePayload.length);
     }
 
+    @Before
+    public void setUp() {
+        deserializer = Routing.deserializer();
+    }
+
     /**
      * Tests serialize and setters.
      */
@@ -83,9 +93,8 @@
      * Tests deserialize and getters.
      */
     @Test
-    public void testDeserialize() {
-        Routing routing = new Routing();
-        routing.deserialize(bytePacket, 0, bytePacket.length);
+    public void testDeserialize() throws DeserializationException {
+        Routing routing = deserializer.deserialize(bytePacket, 0, bytePacket.length);
 
         assertThat(routing.getNextHeader(), is((byte) 0x11));
         assertThat(routing.getHeaderExtLength(), is((byte) 0x02));
diff --git a/utils/misc/src/test/java/org/onlab/packet/ndp/NeighborAdvertisementTest.java b/utils/misc/src/test/java/org/onlab/packet/ndp/NeighborAdvertisementTest.java
index 0c95c6f..303db07 100644
--- a/utils/misc/src/test/java/org/onlab/packet/ndp/NeighborAdvertisementTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/ndp/NeighborAdvertisementTest.java
@@ -17,6 +17,8 @@
 
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
 import org.onlab.packet.MacAddress;
 
 import static org.hamcrest.Matchers.is;
@@ -40,6 +42,9 @@
 
     private static byte[] bytePacket;
 
+    private Deserializer<NeighborAdvertisement> deserializer
+            = NeighborAdvertisement.deserializer();
+
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
         byte[] byteHeader = {
@@ -75,9 +80,8 @@
      * Tests deserialize and getters.
      */
     @Test
-    public void testDeserialize() {
-        NeighborAdvertisement na = new NeighborAdvertisement();
-        na.deserialize(bytePacket, 0, bytePacket.length);
+    public void testDeserialize() throws DeserializationException {
+        NeighborAdvertisement na = deserializer.deserialize(bytePacket, 0, bytePacket.length);
 
         assertThat(na.getRouterFlag(), is((byte) 1));
         assertThat(na.getSolicitedFlag(), is((byte) 1));
diff --git a/utils/misc/src/test/java/org/onlab/packet/ndp/NeighborSolicitationTest.java b/utils/misc/src/test/java/org/onlab/packet/ndp/NeighborSolicitationTest.java
index 09e0117..2571b7a 100644
--- a/utils/misc/src/test/java/org/onlab/packet/ndp/NeighborSolicitationTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/ndp/NeighborSolicitationTest.java
@@ -17,6 +17,8 @@
 
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
 import org.onlab.packet.MacAddress;
 
 import static org.hamcrest.Matchers.is;
@@ -46,6 +48,9 @@
 
     private static byte[] bytePacket;
 
+    private Deserializer<NeighborSolicitation> deserializer
+            = NeighborSolicitation.deserializer();
+
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
         byte[] byteHeader = {
@@ -78,9 +83,8 @@
      * Tests deserialize and getters.
      */
     @Test
-    public void testDeserialize() {
-        NeighborSolicitation ns = new NeighborSolicitation();
-        ns.deserialize(bytePacket, 0, bytePacket.length);
+    public void testDeserialize() throws DeserializationException {
+        NeighborSolicitation ns = deserializer.deserialize(bytePacket, 0, bytePacket.length);
 
         assertArrayEquals(ns.getTargetAddress(), TARGET_ADDRESS);
 
diff --git a/utils/misc/src/test/java/org/onlab/packet/ndp/RedirectTest.java b/utils/misc/src/test/java/org/onlab/packet/ndp/RedirectTest.java
index 865f03a..7d0d56c 100644
--- a/utils/misc/src/test/java/org/onlab/packet/ndp/RedirectTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/ndp/RedirectTest.java
@@ -17,6 +17,8 @@
 
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
 import org.onlab.packet.MacAddress;
 
 import static org.hamcrest.Matchers.is;
@@ -52,6 +54,8 @@
 
     private static byte[] bytePacket;
 
+    private Deserializer<Redirect> deserializer = Redirect.deserializer();
+
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
         byte[] byteHeader = {
@@ -89,9 +93,8 @@
      * Tests deserialize and getters.
      */
     @Test
-    public void testDeserialize() {
-        Redirect rd = new Redirect();
-        rd.deserialize(bytePacket, 0, bytePacket.length);
+    public void testDeserialize() throws DeserializationException {
+        Redirect rd = deserializer.deserialize(bytePacket, 0, bytePacket.length);
 
         assertArrayEquals(rd.getTargetAddress(), TARGET_ADDRESS);
         assertArrayEquals(rd.getDestinationAddress(), DESTINATION_ADDRESS);
diff --git a/utils/misc/src/test/java/org/onlab/packet/ndp/RouterAdvertisementTest.java b/utils/misc/src/test/java/org/onlab/packet/ndp/RouterAdvertisementTest.java
index b69d142..d65dd51 100644
--- a/utils/misc/src/test/java/org/onlab/packet/ndp/RouterAdvertisementTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/ndp/RouterAdvertisementTest.java
@@ -17,6 +17,8 @@
 
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
 import org.onlab.packet.MacAddress;
 
 import static org.hamcrest.Matchers.is;
@@ -34,6 +36,9 @@
 
     private static byte[] bytePacket;
 
+    private Deserializer<RouterAdvertisement> deserializer
+            = RouterAdvertisement.deserializer();
+
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
         byte[] byteHeader = {
@@ -69,9 +74,8 @@
      * Tests deserialize and getters.
      */
     @Test
-    public void testDeserialize() {
-        RouterAdvertisement ra = new RouterAdvertisement();
-        ra.deserialize(bytePacket, 0, bytePacket.length);
+    public void testDeserialize() throws DeserializationException {
+        RouterAdvertisement ra = deserializer.deserialize(bytePacket, 0, bytePacket.length);
 
         assertThat(ra.getCurrentHopLimit(), is((byte) 3));
         assertThat(ra.getMFlag(), is((byte) 1));
diff --git a/utils/misc/src/test/java/org/onlab/packet/ndp/RouterSolicitationTest.java b/utils/misc/src/test/java/org/onlab/packet/ndp/RouterSolicitationTest.java
index 9c087e3..173baaa 100644
--- a/utils/misc/src/test/java/org/onlab/packet/ndp/RouterSolicitationTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/ndp/RouterSolicitationTest.java
@@ -17,7 +17,9 @@
 
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.onlab.packet.Deserializer;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.PacketTestUtils;
 
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertArrayEquals;
@@ -36,6 +38,9 @@
 
     private static byte[] bytePacket;
 
+    private Deserializer<RouterSolicitation> deserializer
+            = RouterSolicitation.deserializer();
+
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
         byte[] byteHeader = {
@@ -59,13 +64,22 @@
         assertArrayEquals(rs.serialize(), bytePacket);
     }
 
+    @Test
+    public void testDeserializeBadInput() throws Exception {
+        PacketTestUtils.testDeserializeBadInput(RouterSolicitation.deserializer());
+    }
+
+    @Test
+    public void testDeserializeTruncated() throws Exception {
+        PacketTestUtils.testDeserializeTruncated(RouterSolicitation.deserializer(), bytePacket);
+    }
+
     /**
      * Tests deserialize and getters.
      */
     @Test
-    public void testDeserialize() {
-        RouterSolicitation rs = new RouterSolicitation();
-        rs.deserialize(bytePacket, 0, bytePacket.length);
+    public void testDeserialize() throws Exception {
+        RouterSolicitation rs = deserializer.deserialize(bytePacket, 0, bytePacket.length);
 
         // Check the option(s)
         assertThat(rs.getOptions().size(), is(1));