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/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java
index 1af3fd9..e068c94 100644
--- a/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java
+++ b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java
@@ -15,8 +15,6 @@
*/
package org.onosproject.bgprouter;
-import java.nio.ByteBuffer;
-
import org.onlab.packet.Ethernet;
import org.onlab.packet.ICMP;
import org.onlab.packet.IPv4;
@@ -36,6 +34,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.nio.ByteBuffer;
+
public class IcmpHandler {
private static final Logger log = LoggerFactory.getLogger(IcmpHandler.class);
@@ -100,7 +100,7 @@
icmpReplyIpv4.setTtl((byte) 64);
icmpReplyIpv4.setChecksum((short) 0);
- ICMP icmpReply = (ICMP) icmpRequestIpv4.getPayload().clone();
+ ICMP icmpReply = new ICMP();
icmpReply.setIcmpType(ICMP.TYPE_ECHO_REPLY);
icmpReply.setIcmpCode(ICMP.SUBTYPE_ECHO_REPLY);
icmpReply.setChecksum((short) 0);
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
index b3da8dc..0f8fa59 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
@@ -15,8 +15,6 @@
*/
package org.onosproject.segmentrouting;
-import java.nio.ByteBuffer;
-import java.util.List;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ICMP;
import org.onlab.packet.IPv4;
@@ -33,6 +31,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.nio.ByteBuffer;
+import java.util.List;
+
import static com.google.common.base.Preconditions.checkNotNull;
public class IcmpHandler {
@@ -109,7 +110,7 @@
icmpReplyIpv4.setTtl((byte) 64);
icmpReplyIpv4.setChecksum((short) 0);
- ICMP icmpReply = (ICMP) icmpRequestIpv4.getPayload().clone();
+ ICMP icmpReply = new ICMP();
icmpReply.setIcmpType(ICMP.TYPE_ECHO_REPLY);
icmpReply.setIcmpCode(ICMP.SUBTYPE_ECHO_REPLY);
icmpReply.setChecksum((short) 0);
diff --git a/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java b/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java
index 679a888..90cf6b4 100644
--- a/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java
+++ b/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java
@@ -49,8 +49,14 @@
import java.util.List;
import java.util.Set;
-import static org.easymock.EasyMock.*;
-import static org.junit.Assert.*;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
public class HostMonitorTest {
@@ -151,10 +157,9 @@
assertEquals(portNum, oi.port());
// Check the output packet is correct (well the important bits anyway)
- Ethernet eth = new Ethernet();
final byte[] pktData = new byte[packet.data().remaining()];
packet.data().get(pktData);
- eth.deserialize(pktData, 0, pktData.length);
+ Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length);
assertEquals(Ethernet.VLAN_UNTAGGED, eth.getVlanID());
ARP arp = (ARP) eth.getPayload();
assertArrayEquals(SOURCE_ADDR.toOctets(),
@@ -220,10 +225,9 @@
assertEquals(portNum, oi.port());
// Check the output packet is correct (well the important bits anyway)
- Ethernet eth = new Ethernet();
final byte[] pktData = new byte[packet.data().remaining()];
packet.data().get(pktData);
- eth.deserialize(pktData, 0, pktData.length);
+ Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length);
assertEquals(vlan, eth.getVlanID());
ARP arp = (ARP) eth.getPayload();
assertArrayEquals(SOURCE_ADDR.toOctets(),
diff --git a/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java b/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java
index be7e4b0..f4ca827 100644
--- a/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java
+++ b/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java
@@ -15,7 +15,7 @@
*/
package org.onosproject.openflow.controller;
-
+import org.onlab.packet.DeserializationException;
import org.onlab.packet.Ethernet;
import org.onosproject.core.Permission;
import org.projectfloodlight.openflow.protocol.OFPacketIn;
@@ -26,6 +26,8 @@
import org.projectfloodlight.openflow.protocol.match.MatchField;
import org.projectfloodlight.openflow.types.OFBufferId;
import org.projectfloodlight.openflow.types.OFPort;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.nio.BufferUnderflowException;
import java.util.Collections;
@@ -97,11 +99,12 @@
public Ethernet parsed() {
checkPermission(Permission.PACKET_READ);
- Ethernet eth = new Ethernet();
try {
- eth.deserialize(pktin.getData(), 0, pktin.getData().length);
- return eth;
- } catch (BufferUnderflowException | NullPointerException e) {
+ return Ethernet.deserializer().deserialize(pktin.getData(), 0, pktin.getData().length);
+ } catch (BufferUnderflowException | NullPointerException |
+ DeserializationException e) {
+ Logger log = LoggerFactory.getLogger(getClass());
+ log.warn("packet deserialization problem");
return null;
}
}
diff --git a/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowCorePacketContext.java b/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowCorePacketContext.java
index 5d44c91..6d15310 100644
--- a/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowCorePacketContext.java
+++ b/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowCorePacketContext.java
@@ -15,6 +15,8 @@
*/
package org.onosproject.provider.of.packet.impl;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Ethernet;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.Instruction.Type;
@@ -23,8 +25,9 @@
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.openflow.controller.OpenFlowPacketContext;
-import org.onlab.packet.Ethernet;
import org.projectfloodlight.openflow.types.OFPort;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.List;
@@ -33,6 +36,8 @@
*/
public class OpenFlowCorePacketContext extends DefaultPacketContext {
+ private static final Logger log = LoggerFactory.getLogger(OpenFlowCorePacketContext.class);
+
private final OpenFlowPacketContext ofPktCtx;
/**
@@ -57,12 +62,15 @@
if (outPacket() == null) {
sendPacket(null);
} else {
- Ethernet eth = new Ethernet();
- eth.deserialize(outPacket().data().array(), 0,
- outPacket().data().array().length);
- sendPacket(eth);
+ try {
+ Ethernet eth = Ethernet.deserializer()
+ .deserialize(outPacket().data().array(), 0,
+ outPacket().data().array().length);
+ sendPacket(eth);
+ } catch (DeserializationException e) {
+ log.warn("Unable to deserialize packet");
+ }
}
-
}
}
diff --git a/utils/misc/pom.xml b/utils/misc/pom.xml
index e3635f4..1795121 100644
--- a/utils/misc/pom.xml
+++ b/utils/misc/pom.xml
@@ -42,6 +42,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
</dependency>
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 88e1d4f..dc3c07f 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ARP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ARP.java
@@ -21,6 +21,8 @@
import java.nio.ByteBuffer;
import java.util.Arrays;
+import static org.onlab.packet.PacketUtils.*;
+
/**
*
*
@@ -35,6 +37,8 @@
public static final short OP_RARP_REQUEST = 0x3;
public static final short OP_RARP_REPLY = 0x4;
+ public static final short INITIAL_HEADER_LENGTH = 8;
+
protected short hardwareType;
protected short protocolType;
protected byte hardwareAddressLength;
@@ -247,7 +251,7 @@
@Override
public IPacket deserialize(final byte[] data, final int offset,
- final int length) {
+ final int length) {
final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
this.hardwareType = bb.getShort();
this.protocolType = bb.getShort();
@@ -386,10 +390,50 @@
arp.setTargetHardwareAddress(request.getSourceMACAddress());
arp.setTargetProtocolAddress(((ARP) request.getPayload())
- .getSenderProtocolAddress());
+ .getSenderProtocolAddress());
arp.setSenderProtocolAddress(srcIp.toInt());
eth.setPayload(arp);
return eth;
}
+
+ /**
+ * Deserializer function for ARP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<ARP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, INITIAL_HEADER_LENGTH);
+
+ ARP arp = new ARP();
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ arp.setHardwareType(bb.getShort());
+ arp.setProtocolType(bb.getShort());
+
+ byte hwAddressLength = bb.get();
+ arp.setHardwareAddressLength(hwAddressLength);
+
+ byte protocolAddressLength = bb.get();
+ arp.setProtocolAddressLength(protocolAddressLength);
+ arp.setOpCode(bb.getShort());
+
+ // Check we have enough space for the addresses
+ checkHeaderLength(length, INITIAL_HEADER_LENGTH +
+ 2 * hwAddressLength +
+ 2 * protocolAddressLength);
+
+ arp.senderHardwareAddress = new byte[0xff & hwAddressLength];
+ bb.get(arp.senderHardwareAddress, 0, arp.senderHardwareAddress.length);
+ arp.senderProtocolAddress = new byte[0xff & protocolAddressLength];
+ bb.get(arp.senderProtocolAddress, 0, arp.senderProtocolAddress.length);
+ arp.targetHardwareAddress = new byte[0xff & hwAddressLength];
+ bb.get(arp.targetHardwareAddress, 0, arp.targetHardwareAddress.length);
+ arp.targetProtocolAddress = new byte[0xff & protocolAddressLength];
+ bb.get(arp.targetProtocolAddress, 0, arp.targetProtocolAddress.length);
+
+ return arp;
+ };
+ }
+
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/DHCP.java b/utils/misc/src/main/java/org/onlab/packet/DHCP.java
index 8dba13c..40a7745 100644
--- a/utils/misc/src/main/java/org/onlab/packet/DHCP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/DHCP.java
@@ -24,6 +24,8 @@
import java.util.List;
import java.util.ListIterator;
+import static org.onlab.packet.PacketUtils.*;
+
/**
*
*/
@@ -429,33 +431,9 @@
return data;
}
- protected void writeString(final String string, final ByteBuffer bb,
- final int maxLength) {
- if (string == null) {
- for (int i = 0; i < maxLength; ++i) {
- bb.put((byte) 0x0);
- }
- } else {
- byte[] bytes = null;
- try {
- bytes = string.getBytes("ascii");
- } catch (final UnsupportedEncodingException e) {
- throw new RuntimeException("Failure encoding server name", e);
- }
- int writeLength = bytes.length;
- if (writeLength > maxLength) {
- writeLength = maxLength;
- }
- bb.put(bytes, 0, writeLength);
- for (int i = writeLength; i < maxLength; ++i) {
- bb.put((byte) 0x0);
- }
- }
- }
-
@Override
public IPacket deserialize(final byte[] data, final int offset,
- final int length) {
+ final int length) {
final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
if (bb.remaining() < DHCP.MIN_HEADER_LENGTH) {
return this;
@@ -529,7 +507,31 @@
return this;
}
- protected String readString(final ByteBuffer bb, final int maxLength) {
+ protected void writeString(final String string, final ByteBuffer bb,
+ final int maxLength) {
+ if (string == null) {
+ for (int i = 0; i < maxLength; ++i) {
+ bb.put((byte) 0x0);
+ }
+ } else {
+ byte[] bytes = null;
+ try {
+ bytes = string.getBytes("ascii");
+ } catch (final UnsupportedEncodingException e) {
+ throw new RuntimeException("Failure encoding server name", e);
+ }
+ int writeLength = bytes.length;
+ if (writeLength > maxLength) {
+ writeLength = maxLength;
+ }
+ bb.put(bytes, 0, writeLength);
+ for (int i = writeLength; i < maxLength; ++i) {
+ bb.put((byte) 0x0);
+ }
+ }
+ }
+
+ private static String readString(final ByteBuffer bb, final int maxLength) {
final byte[] bytes = new byte[maxLength];
bb.get(bytes);
String result = null;
@@ -540,4 +542,84 @@
}
return result;
}
+
+ /**
+ * Deserializer function for DHCP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<DHCP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, MIN_HEADER_LENGTH);
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ DHCP dhcp = new DHCP();
+
+ dhcp.opCode = bb.get();
+ dhcp.hardwareType = bb.get();
+ dhcp.hardwareAddressLength = bb.get();
+ dhcp.hops = bb.get();
+ dhcp.transactionId = bb.getInt();
+ dhcp.seconds = bb.getShort();
+ dhcp.flags = bb.getShort();
+ dhcp.clientIPAddress = bb.getInt();
+ dhcp.yourIPAddress = bb.getInt();
+ dhcp.serverIPAddress = bb.getInt();
+ dhcp.gatewayIPAddress = bb.getInt();
+ final int hardwareAddressLength = 0xff & dhcp.hardwareAddressLength;
+ dhcp.clientHardwareAddress = new byte[hardwareAddressLength];
+
+ bb.get(dhcp.clientHardwareAddress);
+ for (int i = hardwareAddressLength; i < 16; ++i) {
+ bb.get();
+ }
+ dhcp.serverName = readString(bb, 64);
+ dhcp.bootFileName = readString(bb, 128);
+ // read the magic cookie
+ // magic cookie
+ bb.get();
+ bb.get();
+ bb.get();
+ bb.get();
+
+ // read options
+ boolean foundEndOptionsMarker = false;
+ while (bb.hasRemaining()) {
+ final DHCPOption option = new DHCPOption();
+ int code = 0xff & bb.get(); // convert signed byte to int in range
+ // [0,255]
+ option.setCode((byte) code);
+ if (code == 0) {
+ // skip these
+ continue;
+ } else if (code != 255) {
+ if (bb.hasRemaining()) {
+ final int l = 0xff & bb.get(); // convert signed byte to
+ // int in range [0,255]
+ option.setLength((byte) l);
+ if (bb.remaining() >= l) {
+ final byte[] optionData = new byte[l];
+ bb.get(optionData);
+ option.setData(optionData);
+ dhcp.options.add(option);
+ } else {
+ throw new DeserializationException(
+ "Buffer underflow while reading DHCP option");
+ }
+ }
+ } else if (code == 255) {
+ // remaining bytes are supposed to be 0, but ignore them just in
+ // case
+ foundEndOptionsMarker = true;
+ break;
+ }
+ }
+
+ if (!foundEndOptionsMarker) {
+ throw new DeserializationException("DHCP End options marker was missing");
+ }
+
+ return dhcp;
+ };
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/Data.java b/utils/misc/src/main/java/org/onlab/packet/Data.java
index f3a1092..79abcba 100644
--- a/utils/misc/src/main/java/org/onlab/packet/Data.java
+++ b/utils/misc/src/main/java/org/onlab/packet/Data.java
@@ -20,6 +20,8 @@
import java.util.Arrays;
+import static org.onlab.packet.PacketUtils.*;
+
/**
*
*/
@@ -30,6 +32,7 @@
*
*/
public Data() {
+ data = new byte[0];
}
/**
@@ -63,7 +66,7 @@
@Override
public IPacket deserialize(final byte[] data, final int offset,
- final int length) {
+ final int length) {
this.data = Arrays.copyOfRange(data, offset, data.length);
return this;
}
@@ -103,4 +106,27 @@
}
return true;
}
+
+ /**
+ * Deserializer function for generic payload data.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Data> deserializer() {
+ return (data, offset, length) -> {
+ // Allow zero-length data for now
+ if (length == 0) {
+ return new Data();
+ }
+
+ checkInput(data, offset, length, 1);
+
+ Data dataObject = new Data();
+
+ dataObject.data = Arrays.copyOfRange(data, offset, data.length);
+
+ return dataObject;
+ };
+ }
+
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/DeserializationException.java b/utils/misc/src/main/java/org/onlab/packet/DeserializationException.java
new file mode 100644
index 0000000..03a8aa3
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/DeserializationException.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * Signals that an error occurred during deserialization of a packet.
+ */
+public class DeserializationException extends Exception {
+
+ /**
+ * Creates a new deserialization exception with the given message.
+ *
+ * @param message exception message
+ */
+ public DeserializationException(String message) {
+ super(message);
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/Deserializer.java b/utils/misc/src/main/java/org/onlab/packet/Deserializer.java
new file mode 100644
index 0000000..e0ef381
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/Deserializer.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+/**
+ * Function to deserialize a packet from a byte-based input stream.
+ */
+@FunctionalInterface
+public interface Deserializer<U extends IPacket> {
+
+ /**
+ * Deserialize a packet object from a byte array.
+ *
+ * @param data input array to take packet bytes from
+ * @param offset index where this packet header begins in the byte array
+ * @param length length of the packet header
+ * @return a deserialized packet object
+ * @throws DeserializationException if the packet cannot be deserialized
+ * from the input
+ */
+ U deserialize(byte[] data, int offset, int length) throws DeserializationException;
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/EthType.java b/utils/misc/src/main/java/org/onlab/packet/EthType.java
index 4f52b7d..7573ae8 100644
--- a/utils/misc/src/main/java/org/onlab/packet/EthType.java
+++ b/utils/misc/src/main/java/org/onlab/packet/EthType.java
@@ -93,25 +93,27 @@
public static enum EtherType {
- ARP(0x806, "arp", ARP.class),
- RARP(0x8035, "rarp", null),
- IPV4(0x800, "ipv4", IPv4.class),
- IPV6(0x86dd, "ipv6", IPv6.class),
- LLDP(0x88cc, "lldp", LLDP.class),
- VLAN(0x8100, "vlan", null),
- BDDP(0x8942, "bddp", LLDP.class),
- MPLS_UNICAST(0x8847, "mpls_unicast", null),
- MPLS_MULTICAST(0x8848, "mpls_unicast", null);
+ ARP(0x806, "arp", ARP.class, org.onlab.packet.ARP.deserializer()),
+ RARP(0x8035, "rarp", null, org.onlab.packet.ARP.deserializer()),
+ IPV4(0x800, "ipv4", IPv4.class, org.onlab.packet.IPv4.deserializer()),
+ IPV6(0x86dd, "ipv6", IPv6.class, org.onlab.packet.IPv6.deserializer()),
+ LLDP(0x88cc, "lldp", LLDP.class, org.onlab.packet.LLDP.deserializer()),
+ VLAN(0x8100, "vlan", null, null),
+ BDDP(0x8942, "bddp", LLDP.class, org.onlab.packet.LLDP.deserializer()),
+ MPLS_UNICAST(0x8847, "mpls_unicast", null, org.onlab.packet.MPLS.deserializer()),
+ MPLS_MULTICAST(0x8848, "mpls_unicast", null, org.onlab.packet.MPLS.deserializer());
private final Class clazz;
private EthType ethType;
private String type;
+ private Deserializer<?> deserializer;
- EtherType(int ethType, String type, Class clazz) {
+ EtherType(int ethType, String type, Class clazz, Deserializer deserializer) {
this.ethType = new EthType(ethType);
this.type = type;
this.clazz = clazz;
+ this.deserializer = deserializer;
}
public EthType ethType() {
@@ -127,6 +129,8 @@
return clazz;
}
-
+ public Deserializer<?> deserializer() {
+ return deserializer;
+ }
}
}
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 779d96c..700bdfc 100644
--- a/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
+++ b/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
@@ -18,13 +18,14 @@
package org.onlab.packet;
-import static com.google.common.base.Preconditions.checkNotNull;
-
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.packet.PacketUtils.checkHeaderLength;
+import static org.onlab.packet.PacketUtils.checkInput;
/**
*
@@ -42,15 +43,21 @@
public static final short MPLS_UNICAST = EthType.MPLS_UNICAST;
public static final short MPLS_MULTICAST = EthType.MPLS_MULTICAST;
+
public static final short VLAN_UNTAGGED = (short) 0xffff;
+
+ public static final short ETHERNET_HEADER_LENGTH = 14; // bytes
+ public static final short VLAN_HEADER_LENGTH = 4; // bytes
+
public static final short DATALAYER_ADDRESS_LENGTH = 6; // bytes
- public static final Map<Short, Class<? extends IPacket>> ETHER_TYPE_CLASS_MAP =
- new HashMap<>();
+
+ private static final Map<Short, Deserializer<? extends IPacket>> ETHERTYPE_DESERIALIZER_MAP =
+ new HashMap<>();
static {
for (EthType.EtherType ethType : EthType.EtherType.values()) {
if (ethType.clazz() != null) {
- ETHER_TYPE_CLASS_MAP.put(ethType.ethType().toShort(), ethType.clazz());
+ ETHERTYPE_DESERIALIZER_MAP.put(ethType.ethType().toShort(), ethType.deserializer());
}
}
}
@@ -300,7 +307,7 @@
@Override
public IPacket deserialize(final byte[] data, final int offset,
- final int length) {
+ final int length) {
if (length <= 0) {
return null;
}
@@ -331,21 +338,19 @@
this.etherType = ethType;
IPacket payload;
- if (Ethernet.ETHER_TYPE_CLASS_MAP.containsKey(this.etherType)) {
- final Class<? extends IPacket> clazz = Ethernet.ETHER_TYPE_CLASS_MAP
- .get(this.etherType);
- try {
- payload = clazz.newInstance();
- } catch (final Exception e) {
- throw new RuntimeException(
- "Error parsing payload for Ethernet packet", e);
- }
+ Deserializer<? extends IPacket> deserializer;
+ if (Ethernet.ETHERTYPE_DESERIALIZER_MAP.containsKey(ethType)) {
+ deserializer = Ethernet.ETHERTYPE_DESERIALIZER_MAP.get(ethType);
} else {
- payload = new Data();
+ deserializer = Data.deserializer();
}
- this.payload = payload.deserialize(data, bb.position(),
- bb.limit() - bb.position());
- this.payload.setParent(this);
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
return this;
}
@@ -567,4 +572,53 @@
return builder.toString();
}
+ /**
+ * Deserializer function for Ethernet packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Ethernet> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, ETHERNET_HEADER_LENGTH);
+
+ byte[] addressBuffer = new byte[DATALAYER_ADDRESS_LENGTH];
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ Ethernet eth = new Ethernet();
+ // Read destination MAC address into buffer
+ bb.get(addressBuffer);
+ eth.setDestinationMACAddress(addressBuffer);
+
+ // Read source MAC address into buffer
+ bb.get(addressBuffer);
+ eth.setSourceMACAddress(addressBuffer);
+
+ short ethType = bb.getShort();
+ if (ethType == TYPE_VLAN) {
+ checkHeaderLength(length, ETHERNET_HEADER_LENGTH + VLAN_HEADER_LENGTH);
+ final short tci = bb.getShort();
+ eth.setPriorityCode((byte) (tci >> 13 & 0x07));
+ eth.setVlanID((short) (tci & 0x0fff));
+ ethType = bb.getShort();
+ } else {
+ eth.setVlanID(Ethernet.VLAN_UNTAGGED);
+ }
+ eth.setEtherType(ethType);
+
+ IPacket payload;
+ Deserializer<? extends IPacket> deserializer;
+ if (Ethernet.ETHERTYPE_DESERIALIZER_MAP.containsKey(ethType)) {
+ deserializer = Ethernet.ETHERTYPE_DESERIALIZER_MAP.get(ethType);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ payload.setParent(eth);
+ eth.setPayload(payload);
+
+ return eth;
+ };
+ }
+
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ICMP.java b/utils/misc/src/main/java/org/onlab/packet/ICMP.java
index 46814cd..d07a9ba 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ICMP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ICMP.java
@@ -20,6 +20,8 @@
import java.nio.ByteBuffer;
+import static org.onlab.packet.PacketUtils.*;
+
/**
* Implements ICMP packet format.
*
@@ -33,6 +35,8 @@
public static final byte TYPE_ECHO_REPLY = 0x00;
public static final byte SUBTYPE_ECHO_REPLY = 0x00;
+ public static final short ICMP_HEADER_LENGTH = 4;
+
/**
* @return the icmpType
*/
@@ -134,6 +138,21 @@
return data;
}
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.icmpType = bb.get();
+ this.icmpCode = bb.get();
+ this.checksum = bb.getShort();
+
+ this.payload = new Data();
+ this.payload = this.payload.deserialize(data, bb.position(), bb.limit()
+ - bb.position());
+ this.payload.setParent(this);
+ return this;
+ }
+
/*
* (non-Javadoc)
*
@@ -178,18 +197,27 @@
return true;
}
- @Override
- public IPacket deserialize(final byte[] data, final int offset,
- final int length) {
- final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
- this.icmpType = bb.get();
- this.icmpCode = bb.get();
- this.checksum = bb.getShort();
+ /**
+ * Deserializer function for ICMP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<ICMP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, ICMP_HEADER_LENGTH);
- this.payload = new Data();
- this.payload = this.payload.deserialize(data, bb.position(), bb.limit()
- - bb.position());
- this.payload.setParent(this);
- return this;
+ ICMP icmp = new ICMP();
+
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ icmp.icmpType = bb.get();
+ icmp.icmpCode = bb.get();
+ icmp.checksum = bb.getShort();
+
+ icmp.payload = Data.deserializer()
+ .deserialize(data, bb.position(), bb.limit()
+ - bb.position());
+ icmp.payload.setParent(icmp);
+ return icmp;
+ };
}
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ICMP6.java b/utils/misc/src/main/java/org/onlab/packet/ICMP6.java
index 7c9b4b9..c898130 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ICMP6.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ICMP6.java
@@ -24,10 +24,13 @@
import org.onlab.packet.ndp.Redirect;
import org.onlab.packet.ndp.RouterAdvertisement;
import org.onlab.packet.ndp.RouterSolicitation;
+
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
+import static org.onlab.packet.PacketUtils.checkInput;
+
/**
* Implements ICMPv6 packet format. (RFC 4443)
*/
@@ -96,15 +99,15 @@
/** Unrecognized IPv6 option encountered. */
public static final byte IPV6_OPT_ERR = (byte) 0x01;
- public static final Map<Byte, Class<? extends IPacket>> PROTOCOL_CLASS_MAP =
+ public static final Map<Byte, Deserializer<? extends IPacket>> TYPE_DESERIALIZER_MAP =
new HashMap<>();
static {
- ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.ROUTER_SOLICITATION, RouterSolicitation.class);
- ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.ROUTER_ADVERTISEMENT, RouterAdvertisement.class);
- ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.NEIGHBOR_SOLICITATION, NeighborSolicitation.class);
- ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.NEIGHBOR_ADVERTISEMENT, NeighborAdvertisement.class);
- ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.REDIRECT, Redirect.class);
+ ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.ROUTER_SOLICITATION, RouterSolicitation.deserializer());
+ ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.ROUTER_ADVERTISEMENT, RouterAdvertisement.deserializer());
+ ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.NEIGHBOR_SOLICITATION, NeighborSolicitation.deserializer());
+ ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.NEIGHBOR_ADVERTISEMENT, NeighborAdvertisement.deserializer());
+ ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.REDIRECT, Redirect.deserializer());
}
protected byte icmpType;
@@ -261,6 +264,31 @@
return data;
}
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.icmpType = bb.get();
+ this.icmpCode = bb.get();
+ this.checksum = bb.getShort();
+
+ Deserializer<? extends IPacket> deserializer;
+ if (ICMP6.TYPE_DESERIALIZER_MAP.containsKey(icmpType)) {
+ deserializer = TYPE_DESERIALIZER_MAP.get(icmpType);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
+
+ return this;
+ }
+
/*
* (non-Javadoc)
*
@@ -305,31 +333,34 @@
return true;
}
- @Override
- public IPacket deserialize(final byte[] data, final int offset,
- final int length) {
- final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
- this.icmpType = bb.get();
- this.icmpCode = bb.get();
- this.checksum = bb.getShort();
+ /**
+ * Deserializer function for ICMPv6 packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<ICMP6> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
- IPacket payload;
- if (ICMP6.PROTOCOL_CLASS_MAP.containsKey(this.icmpType)) {
- final Class<? extends IPacket> clazz = ICMP6.PROTOCOL_CLASS_MAP
- .get(this.icmpType);
- try {
- payload = clazz.newInstance();
- } catch (final Exception e) {
- throw new RuntimeException(
- "Error parsing payload for ICMP6 packet", e);
+ ICMP6 icmp6 = new ICMP6();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ icmp6.icmpType = bb.get();
+ icmp6.icmpCode = bb.get();
+ icmp6.checksum = bb.getShort();
+
+ Deserializer<? extends IPacket> deserializer;
+ if (ICMP6.TYPE_DESERIALIZER_MAP.containsKey(icmp6.icmpType)) {
+ deserializer = TYPE_DESERIALIZER_MAP.get(icmp6.icmpType);
+ } else {
+ deserializer = Data.deserializer();
}
- } else {
- payload = new Data();
- }
- this.payload = payload.deserialize(data, bb.position(),
- bb.limit() - bb.position());
- this.payload.setParent(this);
+ icmp6.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ icmp6.payload.setParent(icmp6);
- return this;
+ return icmp6;
+ };
}
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/IPacket.java b/utils/misc/src/main/java/org/onlab/packet/IPacket.java
index ac6ae60..38684eb 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IPacket.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IPacket.java
@@ -64,6 +64,11 @@
/**
* Deserializes this packet layer and all possible payloads.
*
+ * NOTE: This method has been deprecated and will be removed in a future
+ * release. It is now recommended to use the Deserializer function provided
+ * by the deserialize() method on each packet to deserialize them. The
+ * Deserializer functions are robust to malformed input.
+ *
* @param data bytes to deserialize
* @param offset
* offset to start deserializing from
@@ -71,6 +76,7 @@
* length of the data to deserialize
* @return the deserialized data
*/
+ @Deprecated
IPacket deserialize(byte[] data, int offset, int length);
/**
diff --git a/utils/misc/src/main/java/org/onlab/packet/IPv4.java b/utils/misc/src/main/java/org/onlab/packet/IPv4.java
index d1563eb..0d75245 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IPv4.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IPv4.java
@@ -25,6 +25,8 @@
import java.util.HashMap;
import java.util.Map;
+import static org.onlab.packet.PacketUtils.*;
+
/**
*
*/
@@ -32,19 +34,21 @@
public static final byte PROTOCOL_ICMP = 0x1;
public static final byte PROTOCOL_TCP = 0x6;
public static final byte PROTOCOL_UDP = 0x11;
- public static final Map<Byte, Class<? extends IPacket>> PROTOCOL_CLASS_MAP =
+ public static final Map<Byte, Deserializer<? extends IPacket>> PROTOCOL_DESERIALIZER_MAP =
new HashMap<>();
static {
- IPv4.PROTOCOL_CLASS_MAP.put(IPv4.PROTOCOL_ICMP, ICMP.class);
- IPv4.PROTOCOL_CLASS_MAP.put(IPv4.PROTOCOL_TCP, TCP.class);
- IPv4.PROTOCOL_CLASS_MAP.put(IPv4.PROTOCOL_UDP, UDP.class);
+ IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_ICMP, ICMP.deserializer());
+ IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_TCP, TCP.deserializer());
+ IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_UDP, UDP.deserializer());
}
private static final byte DSCP_MASK = 0x3f;
private static final byte DSCP_OFFSET = 2;
private static final byte ECN_MASK = 0x3;
+ private static final short HEADER_LENGTH = 20;
+
protected byte version;
protected byte headerLength;
protected byte diffServ;
@@ -414,7 +418,7 @@
@Override
public IPacket deserialize(final byte[] data, final int offset,
- final int length) {
+ final int length) {
final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
short sscratch;
@@ -439,29 +443,26 @@
bb.get(this.options);
}
- IPacket payload;
- if (IPv4.PROTOCOL_CLASS_MAP.containsKey(this.protocol)) {
- final Class<? extends IPacket> clazz = IPv4.PROTOCOL_CLASS_MAP
- .get(this.protocol);
- try {
- payload = clazz.newInstance();
- } catch (final Exception e) {
- throw new RuntimeException(
- "Error parsing payload for IPv4 packet", e);
- }
- } else {
- payload = new Data();
- }
- this.payload = payload.deserialize(data, bb.position(),
- bb.limit() - bb.position());
- this.payload.setParent(this);
-
if (this.totalLength != length) {
this.isTruncated = true;
} else {
this.isTruncated = false;
}
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv4.PROTOCOL_DESERIALIZER_MAP.containsKey(this.protocol)) {
+ deserializer = IPv4.PROTOCOL_DESERIALIZER_MAP.get(this.protocol);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
+
return this;
}
@@ -669,4 +670,60 @@
}
return true;
}
+
+ /**
+ * Deserializer function for IPv4 packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<IPv4> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ IPv4 ipv4 = new IPv4();
+
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ byte versionByte = bb.get();
+ ipv4.headerLength = (byte) (versionByte & 0xf);
+ ipv4.setVersion((byte) (versionByte >> 4 & 0xf));
+ ipv4.setDiffServ(bb.get());
+ ipv4.totalLength = bb.getShort();
+ ipv4.identification = bb.getShort();
+ short flagsFragment = bb.getShort();
+ ipv4.flags = (byte) (flagsFragment >> 13 & 0x7);
+ ipv4.fragmentOffset = (short) (flagsFragment & 0x1fff);
+ ipv4.ttl = bb.get();
+ ipv4.protocol = bb.get();
+ ipv4.checksum = bb.getShort();
+ ipv4.sourceAddress = bb.getInt();
+ ipv4.destinationAddress = bb.getInt();
+
+ if (ipv4.headerLength > 5) {
+ checkHeaderLength(length, ipv4.headerLength * 4);
+
+ int optionsLength = (ipv4.headerLength - 5) * 4;
+ ipv4.options = new byte[optionsLength];
+ bb.get(ipv4.options);
+ }
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv4.PROTOCOL_DESERIALIZER_MAP.containsKey(ipv4.protocol)) {
+ deserializer = IPv4.PROTOCOL_DESERIALIZER_MAP.get(ipv4.protocol);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ ipv4.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ ipv4.payload.setParent(ipv4);
+
+ if (ipv4.totalLength != length) {
+ ipv4.isTruncated = true;
+ } else {
+ ipv4.isTruncated = false;
+ }
+
+ return ipv4;
+ };
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/IPv6.java b/utils/misc/src/main/java/org/onlab/packet/IPv6.java
index 3bb35c5..2e59632 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IPv6.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IPv6.java
@@ -22,14 +22,17 @@
import org.onlab.packet.ipv6.DestinationOptions;
import org.onlab.packet.ipv6.EncapSecurityPayload;
import org.onlab.packet.ipv6.Fragment;
-import org.onlab.packet.ipv6.IExtensionHeader;
import org.onlab.packet.ipv6.HopByHopOptions;
+import org.onlab.packet.ipv6.IExtensionHeader;
import org.onlab.packet.ipv6.Routing;
+
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import static org.onlab.packet.PacketUtils.checkInput;
+
/**
* Implements IPv6 packet format. (RFC 2460)
*/
@@ -47,19 +50,19 @@
public static final byte PROTOCOL_DSTOPT = 0x3C;
- public static final Map<Byte, Class<? extends IPacket>> PROTOCOL_CLASS_MAP =
+ public static final Map<Byte, Deserializer<? extends IPacket>> PROTOCOL_DESERIALIZER_MAP =
new HashMap<>();
static {
- IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_ICMP6, ICMP6.class);
- IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_TCP, TCP.class);
- IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_UDP, UDP.class);
- IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_HOPOPT, HopByHopOptions.class);
- IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_ROUTING, Routing.class);
- IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_FRAG, Fragment.class);
- IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_ESP, EncapSecurityPayload.class);
- IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_AH, Authentication.class);
- IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_DSTOPT, DestinationOptions.class);
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_ICMP6, ICMP6.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_TCP, TCP.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_UDP, UDP.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_HOPOPT, HopByHopOptions.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_ROUTING, Routing.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_FRAG, Fragment.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_ESP, EncapSecurityPayload.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_AH, Authentication.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_DSTOPT, DestinationOptions.deserializer());
}
protected byte version;
@@ -256,22 +259,19 @@
bb.get(this.sourceAddress, 0, Ip6Address.BYTE_LENGTH);
bb.get(this.destinationAddress, 0, Ip6Address.BYTE_LENGTH);
- IPacket payload;
- if (IPv6.PROTOCOL_CLASS_MAP.containsKey(this.nextHeader)) {
- final Class<? extends IPacket> clazz = IPv6.PROTOCOL_CLASS_MAP
- .get(this.nextHeader);
- try {
- payload = clazz.newInstance();
- } catch (final Exception e) {
- throw new RuntimeException(
- "Error parsing payload for IPv6 packet", e);
- }
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(this.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(this.nextHeader);
} else {
- payload = new Data();
+ deserializer = Data.deserializer();
}
- this.payload = payload.deserialize(data, bb.position(),
- bb.limit() - bb.position());
- this.payload.setParent(this);
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
return this;
}
@@ -343,4 +343,42 @@
}
return true;
}
+
+ /**
+ * Deserializer function for IPv6 packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<IPv6> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, FIXED_HEADER_LENGTH);
+
+ IPv6 ipv6 = new IPv6();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ int iscratch = bb.getInt();
+
+ ipv6.version = (byte) (iscratch >> 28 & 0xf);
+ ipv6.trafficClass = (byte) (iscratch >> 20 & 0xff);
+ ipv6.flowLabel = iscratch & 0xfffff;
+ ipv6.payloadLength = bb.getShort();
+ ipv6.nextHeader = bb.get();
+ ipv6.hopLimit = bb.get();
+ bb.get(ipv6.sourceAddress, 0, Ip6Address.BYTE_LENGTH);
+ bb.get(ipv6.destinationAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(ipv6.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(ipv6.nextHeader);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ ipv6.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ ipv6.payload.setParent(ipv6);
+
+ return ipv6;
+ };
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/LLC.java b/utils/misc/src/main/java/org/onlab/packet/LLC.java
index 035f396..78b4f3f 100644
--- a/utils/misc/src/main/java/org/onlab/packet/LLC.java
+++ b/utils/misc/src/main/java/org/onlab/packet/LLC.java
@@ -20,6 +20,8 @@
import java.nio.ByteBuffer;
+import static org.onlab.packet.PacketUtils.*;
+
/**
* This class represents an Link Local Control header that is used in Ethernet
* 802.3.
@@ -27,6 +29,9 @@
*
*/
public class LLC extends BasePacket {
+
+ public static final byte LLC_HEADER_LENGTH = 3;
+
private byte dsap = 0;
private byte ssap = 0;
private byte ctrl = 0;
@@ -67,11 +72,31 @@
@Override
public IPacket deserialize(final byte[] data, final int offset,
- final int length) {
+ final int length) {
final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
this.dsap = bb.get();
this.ssap = bb.get();
this.ctrl = bb.get();
return this;
}
+
+ /**
+ * Deserializer function for LLC packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<LLC> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, LLC_HEADER_LENGTH);
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ LLC llc = new LLC();
+
+ llc.dsap = bb.get();
+ llc.ssap = bb.get();
+ llc.ctrl = bb.get();
+
+ return llc;
+ };
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/LLDP.java b/utils/misc/src/main/java/org/onlab/packet/LLDP.java
index 339bc3a..ae9d717 100644
--- a/utils/misc/src/main/java/org/onlab/packet/LLDP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/LLDP.java
@@ -20,13 +20,26 @@
package org.onlab.packet;
import java.nio.ByteBuffer;
-import java.util.ArrayList;
+import java.util.LinkedList;
import java.util.List;
+import static org.onlab.packet.PacketUtils.*;
+
/**
*
*/
public class LLDP extends BasePacket {
+ public static final byte CHASSIS_TLV_TYPE = 1;
+ public static final short CHASSIS_TLV_SIZE = 7;
+ public static final byte CHASSIS_TLV_SUBTYPE = 4;
+
+ public static final byte PORT_TLV_TYPE = 2;
+ public static final short PORT_TLV_SIZE = 5;
+ public static final byte PORT_TLV_SUBTYPE = 2;
+
+ public static final byte TTL_TLV_TYPE = 3;
+ public static final short TTL_TLV_SIZE = 2;
+
protected LLDPTLV chassisId;
protected LLDPTLV portId;
protected LLDPTLV ttl;
@@ -34,7 +47,7 @@
protected short ethType;
public LLDP() {
- this.optionalTLVList = new ArrayList<LLDPTLV>();
+ this.optionalTLVList = new LinkedList<>();
this.ethType = Ethernet.TYPE_LLDP;
}
@@ -134,11 +147,15 @@
@Override
public IPacket deserialize(final byte[] data, final int offset,
- final int length) {
+ final int length) {
final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
LLDPTLV tlv;
do {
- tlv = new LLDPOrganizationalTLV().deserialize(bb);
+ try {
+ tlv = new LLDPOrganizationalTLV().deserialize(bb);
+ } catch (DeserializationException e) {
+ break;
+ }
// if there was a failure to deserialize stop processing TLVs
if (tlv == null) {
@@ -227,4 +244,57 @@
}
return true;
}
+
+ /**
+ * Deserializer function for LLDP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<LLDP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, 0);
+
+ LLDP lldp = new LLDP();
+
+ int currentIndex = 0;
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ LLDPTLV tlv;
+ do {
+ // Each new TLV must be a minimum of 2 bytes
+ // (containing the type and length fields).
+ currentIndex += 2;
+ checkHeaderLength(length, currentIndex);
+
+ tlv = new LLDPOrganizationalTLV().deserialize(bb);
+
+ // if there was a failure to deserialize stop processing TLVs
+ if (tlv == null) {
+ break;
+ }
+ switch (tlv.getType()) {
+ case 0x0:
+ // can throw this one away, it's just an end delimiter
+ break;
+ case 0x1:
+ lldp.chassisId = tlv;
+ break;
+ case 0x2:
+ lldp.portId = tlv;
+ break;
+ case 0x3:
+ lldp.ttl = tlv;
+ break;
+ default:
+ lldp.optionalTLVList.add(tlv);
+ break;
+ }
+
+ currentIndex += tlv.getLength();
+ } while (tlv.getType() != 0);
+
+ return lldp;
+ };
+ }
+
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java b/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java
index 497e0ad..bedf439 100644
--- a/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java
+++ b/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java
@@ -154,10 +154,15 @@
}
@Override
- public LLDPTLV deserialize(final ByteBuffer bb) {
- LLDPTLV tlv = super.deserialize(bb);
- if (tlv.getType() != LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
- return tlv;
+ public LLDPTLV deserialize(final ByteBuffer bb) throws DeserializationException {
+ super.deserialize(bb);
+ if (this.getType() != LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+ return this;
+ }
+
+ if (this.getLength() <= OUI_LENGTH + SUBTYPE_LENGTH) {
+ throw new DeserializationException(
+ "TLV length is less than required for organizational TLV");
}
final ByteBuffer optionalField = ByteBuffer.wrap(this.value);
diff --git a/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java b/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java
index b5fe833..77efe1b 100644
--- a/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java
+++ b/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java
@@ -95,18 +95,23 @@
return data;
}
- public LLDPTLV deserialize(final ByteBuffer bb) {
- short sscratch;
- sscratch = bb.getShort();
- this.type = (byte) (sscratch >> 9 & 0x7f);
- this.length = (short) (sscratch & 0x1ff);
+ public LLDPTLV deserialize(final ByteBuffer bb) throws DeserializationException {
+ if (bb.remaining() < 2) {
+ throw new DeserializationException(
+ "Not enough bytes to deserialize TLV type and length");
+ }
+ short typeLength;
+ typeLength = bb.getShort();
+ this.type = (byte) (typeLength >> 9 & 0x7f);
+ this.length = (short) (typeLength & 0x1ff);
if (this.length > 0) {
this.value = new byte[this.length];
// if there is an underrun just toss the TLV
if (bb.remaining() < this.length) {
- return null;
+ throw new DeserializationException(
+ "Remaining bytes are less then the length of the TLV");
}
bb.get(this.value);
}
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 4ba6a0c..47dbeed 100644
--- a/utils/misc/src/main/java/org/onlab/packet/MPLS.java
+++ b/utils/misc/src/main/java/org/onlab/packet/MPLS.java
@@ -4,16 +4,19 @@
import java.util.HashMap;
import java.util.Map;
+import static org.onlab.packet.PacketUtils.checkInput;
+
public class MPLS extends BasePacket {
- public static final int ADDRESS_LENGTH = 4;
+ public static final int HEADER_LENGTH = 4;
+
public static final byte PROTOCOL_IPV4 = 0x1;
public static final byte PROTOCOL_MPLS = 0x6;
- public static final Map<Byte, Class<? extends IPacket>> PROTOCOL_CLASS_MAP;
+ static Map<Byte, Deserializer<? extends IPacket>> protocolDeserializerMap
+ = new HashMap<>();
static {
- PROTOCOL_CLASS_MAP = new HashMap<Byte, Class<? extends IPacket>>();
- PROTOCOL_CLASS_MAP.put(PROTOCOL_IPV4, IPv4.class);
- PROTOCOL_CLASS_MAP.put(PROTOCOL_MPLS, MPLS.class);
+ protocolDeserializerMap.put(PROTOCOL_IPV4, IPv4.deserializer());
+ protocolDeserializerMap.put(PROTOCOL_MPLS, MPLS.deserializer());
}
protected int label; //20bits
@@ -59,19 +62,18 @@
this.bos = (byte) (mplsheader & 0x000000ff);
this.protocol = (this.bos == 1) ? PROTOCOL_IPV4 : PROTOCOL_MPLS;
- IPacket payload;
- if (IPv4.PROTOCOL_CLASS_MAP.containsKey(this.protocol)) {
- Class<? extends IPacket> clazz = IPv4.PROTOCOL_CLASS_MAP.get(this.protocol);
- try {
- payload = clazz.newInstance();
- } catch (Exception e) {
- throw new RuntimeException("Error parsing payload for MPLS packet", e);
- }
+ Deserializer<? extends IPacket> deserializer;
+ if (protocolDeserializerMap.containsKey(this.protocol)) {
+ deserializer = protocolDeserializerMap.get(this.protocol);
} else {
- payload = new Data();
+ deserializer = Data.deserializer();
}
- this.payload = payload.deserialize(data, bb.position(), bb.limit() - bb.position());
- this.payload.setParent(this);
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(), bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
return this;
}
@@ -112,4 +114,34 @@
this.ttl = ttl;
}
+ /**
+ * Deserializer function for MPLS packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<MPLS> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ MPLS mpls = new MPLS();
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ int mplsheader = bb.getInt();
+ 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;
+
+ Deserializer<? extends IPacket> deserializer;
+ if (protocolDeserializerMap.containsKey(mpls.protocol)) {
+ deserializer = protocolDeserializerMap.get(mpls.protocol);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ mpls.payload = deserializer.deserialize(data, bb.position(), bb.limit() - bb.position());
+ mpls.payload.setParent(mpls);
+
+ return mpls;
+ };
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/PacketUtils.java b/utils/misc/src/main/java/org/onlab/packet/PacketUtils.java
new file mode 100644
index 0000000..c3bede2
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/PacketUtils.java
@@ -0,0 +1,84 @@
+/*
+ * 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 static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Utilities for working with packet headers.
+ */
+public final class PacketUtils {
+
+ private PacketUtils() {
+ }
+
+ /**
+ * Check the length of the input buffer is appropriate given the offset and
+ * length parameters.
+ *
+ * @param byteLength length of the input buffer array
+ * @param offset offset given to begin reading bytes from
+ * @param length length given to read up until
+ * @throws DeserializationException if the input parameters don't match up (i.e
+ * we can't read that many bytes from the buffer at the given offest)
+ */
+ public static void checkBufferLength(int byteLength, int offset, int length)
+ throws DeserializationException {
+ boolean ok = (offset >= 0 && offset < byteLength);
+ ok = ok & (length >= 0 && offset + length <= byteLength);
+
+ if (!ok) {
+ throw new DeserializationException("Unable to read " + length + " bytes from a "
+ + byteLength + " byte array starting at offset " + offset);
+ }
+ }
+
+ /**
+ * Check that there are enough bytes in the buffer to read some number of
+ * bytes that we need to read a full header.
+ *
+ * @param givenLength given size of the buffer
+ * @param requiredLength number of bytes we need to read some header fully
+ * @throws DeserializationException if there aren't enough bytes
+ */
+ public static void checkHeaderLength(int givenLength, int requiredLength)
+ throws DeserializationException {
+ if (requiredLength > givenLength) {
+ throw new DeserializationException(requiredLength
+ + " bytes are needed to continue deserialization, however only "
+ + givenLength + " remain in buffer");
+ }
+ }
+
+ /**
+ * Check the input parameters are sane and there's enough bytes to read
+ * the required length.
+ *
+ * @param data input byte buffer
+ * @param offset offset of the start of the header
+ * @param length length given to deserialize the header
+ * @param requiredLength length needed to deserialize header
+ * @throws DeserializationException if we're unable to deserialize the
+ * packet based on the input parameters
+ */
+ public static void checkInput(byte[] data, int offset, int length, int requiredLength)
+ throws DeserializationException {
+ checkNotNull(data);
+ checkBufferLength(data.length, offset, length);
+ checkHeaderLength(length, requiredLength);
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/TCP.java b/utils/misc/src/main/java/org/onlab/packet/TCP.java
index 3b92c83..b13b53c 100644
--- a/utils/misc/src/main/java/org/onlab/packet/TCP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/TCP.java
@@ -20,11 +20,16 @@
import java.nio.ByteBuffer;
+import static org.onlab.packet.PacketUtils.*;
+
/**
* Implements TCP packet format.
*/
public class TCP extends BasePacket {
+
+ private static final short TCP_HEADER_LENGTH = 20;
+
protected short sourcePort;
protected short destinationPort;
protected int sequence;
@@ -339,6 +344,40 @@
return data;
}
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.sourcePort = bb.getShort();
+ this.destinationPort = bb.getShort();
+ this.sequence = bb.getInt();
+ this.acknowledge = bb.getInt();
+ this.flags = bb.getShort();
+ this.dataOffset = (byte) (this.flags >> 12 & 0xf);
+ this.flags = (short) (this.flags & 0x1ff);
+ this.windowSize = bb.getShort();
+ this.checksum = bb.getShort();
+ this.urgentPointer = bb.getShort();
+ if (this.dataOffset > 5) {
+ int optLength = (this.dataOffset << 2) - 20;
+ if (bb.limit() < bb.position() + optLength) {
+ optLength = bb.limit() - bb.position();
+ }
+ try {
+ this.options = new byte[optLength];
+ bb.get(this.options, 0, optLength);
+ } catch (final IndexOutOfBoundsException e) {
+ this.options = null;
+ }
+ }
+
+ this.payload = new Data();
+ this.payload = this.payload.deserialize(data, bb.position(), bb.limit()
+ - bb.position());
+ this.payload.setParent(this);
+ return this;
+ }
+
/*
* (non-Javadoc)
*
@@ -384,37 +423,39 @@
&& (this.dataOffset == 5 || this.options.equals(other.options));
}
- @Override
- public IPacket deserialize(final byte[] data, final int offset,
- final int length) {
- final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
- this.sourcePort = bb.getShort();
- this.destinationPort = bb.getShort();
- this.sequence = bb.getInt();
- this.acknowledge = bb.getInt();
- this.flags = bb.getShort();
- this.dataOffset = (byte) (this.flags >> 12 & 0xf);
- this.flags = (short) (this.flags & 0x1ff);
- this.windowSize = bb.getShort();
- this.checksum = bb.getShort();
- this.urgentPointer = bb.getShort();
- if (this.dataOffset > 5) {
- int optLength = (this.dataOffset << 2) - 20;
- if (bb.limit() < bb.position() + optLength) {
- optLength = bb.limit() - bb.position();
- }
- try {
- this.options = new byte[optLength];
- bb.get(this.options, 0, optLength);
- } catch (final IndexOutOfBoundsException e) {
- this.options = null;
- }
- }
+ /**
+ * Deserializer function for TCP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<TCP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, TCP_HEADER_LENGTH);
- this.payload = new Data();
- this.payload = this.payload.deserialize(data, bb.position(), bb.limit()
- - bb.position());
- this.payload.setParent(this);
- return this;
+ TCP tcp = new TCP();
+
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ tcp.sourcePort = bb.getShort();
+ tcp.destinationPort = bb.getShort();
+ tcp.sequence = bb.getInt();
+ tcp.acknowledge = bb.getInt();
+ tcp.flags = bb.getShort();
+ tcp.dataOffset = (byte) (tcp.flags >> 12 & 0xf);
+ tcp.flags = (short) (tcp.flags & 0x1ff);
+ tcp.windowSize = bb.getShort();
+ tcp.checksum = bb.getShort();
+ tcp.urgentPointer = bb.getShort();
+ if (tcp.dataOffset > 5) {
+ int optLength = (tcp.dataOffset << 2) - 20;
+ checkHeaderLength(length, TCP_HEADER_LENGTH + tcp.dataOffset);
+ tcp.options = new byte[optLength];
+ bb.get(tcp.options, 0, optLength);
+ }
+
+ tcp.payload = Data.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+ tcp.payload.setParent(tcp);
+ return tcp;
+ };
}
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/UDP.java b/utils/misc/src/main/java/org/onlab/packet/UDP.java
index c743f09..32432e6 100644
--- a/utils/misc/src/main/java/org/onlab/packet/UDP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/UDP.java
@@ -22,23 +22,27 @@
import java.util.HashMap;
import java.util.Map;
+import static org.onlab.packet.PacketUtils.*;
+
/**
*
*/
public class UDP extends BasePacket {
- public static final Map<Short, Class<? extends IPacket>> DECODE_MAP =
+ public static final Map<Short, Deserializer<? extends IPacket>> PORT_DESERIALIZER_MAP =
new HashMap<>();
public static final short DHCP_SERVER_PORT = (short) 67;
public static final short DHCP_CLIENT_PORT = (short) 68;
+ private static final short UDP_HEADER_LENGTH = 8;
+
static {
/*
* Disable DHCP until the deserialize code is hardened to deal with
* garbage input
*/
- UDP.DECODE_MAP.put(UDP.DHCP_SERVER_PORT, DHCP.class);
- UDP.DECODE_MAP.put(UDP.DHCP_CLIENT_PORT, DHCP.class);
+ UDP.PORT_DESERIALIZER_MAP.put(UDP.DHCP_SERVER_PORT, DHCP.deserializer());
+ UDP.PORT_DESERIALIZER_MAP.put(UDP.DHCP_CLIENT_PORT, DHCP.deserializer());
}
@@ -192,6 +196,34 @@
return data;
}
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.sourcePort = bb.getShort();
+ this.destinationPort = bb.getShort();
+ this.length = bb.getShort();
+ this.checksum = bb.getShort();
+
+ Deserializer<? extends IPacket> deserializer;
+ if (UDP.PORT_DESERIALIZER_MAP.containsKey(this.destinationPort)) {
+ deserializer = UDP.PORT_DESERIALIZER_MAP.get(this.destinationPort);
+ } else if (UDP.PORT_DESERIALIZER_MAP.containsKey(this.sourcePort)) {
+ deserializer = UDP.PORT_DESERIALIZER_MAP.get(this.sourcePort);
+ } else {
+ deserializer = Data.deserializer();
+ }
+
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
+ return this;
+ }
+
/*
* (non-Javadoc)
*
@@ -240,35 +272,36 @@
return true;
}
- @Override
- public IPacket deserialize(final byte[] data, final int offset,
- final int length) {
- final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
- this.sourcePort = bb.getShort();
- this.destinationPort = bb.getShort();
- this.length = bb.getShort();
- this.checksum = bb.getShort();
+ /**
+ * Deserializer function for UDP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<UDP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, UDP_HEADER_LENGTH);
- if (UDP.DECODE_MAP.containsKey(this.destinationPort)) {
- try {
- this.payload = UDP.DECODE_MAP.get(this.destinationPort)
- .getConstructor().newInstance();
- } catch (final Exception e) {
- throw new RuntimeException("Failure instantiating class", e);
+ UDP udp = new UDP();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ udp.sourcePort = bb.getShort();
+ udp.destinationPort = bb.getShort();
+ udp.length = bb.getShort();
+ udp.checksum = bb.getShort();
+
+ Deserializer<? extends IPacket> deserializer;
+ if (UDP.PORT_DESERIALIZER_MAP.containsKey(udp.destinationPort)) {
+ deserializer = UDP.PORT_DESERIALIZER_MAP.get(udp.destinationPort);
+ } else if (UDP.PORT_DESERIALIZER_MAP.containsKey(udp.sourcePort)) {
+ deserializer = UDP.PORT_DESERIALIZER_MAP.get(udp.sourcePort);
+ } else {
+ deserializer = Data.deserializer();
}
- } else if (UDP.DECODE_MAP.containsKey(this.sourcePort)) {
- try {
- this.payload = UDP.DECODE_MAP.get(this.sourcePort)
- .getConstructor().newInstance();
- } catch (final Exception e) {
- throw new RuntimeException("Failure instantiating class", e);
- }
- } else {
- this.payload = new Data();
- }
- this.payload = this.payload.deserialize(data, bb.position(), bb.limit()
- - bb.position());
- this.payload.setParent(this);
- return this;
+
+ udp.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ udp.payload.setParent(udp);
+ return udp;
+ };
}
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ipv6/Authentication.java b/utils/misc/src/main/java/org/onlab/packet/ipv6/Authentication.java
index d4c741f..ec04a81 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ipv6/Authentication.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ipv6/Authentication.java
@@ -18,11 +18,16 @@
import org.onlab.packet.BasePacket;
import org.onlab.packet.Data;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
import org.onlab.packet.IPacket;
import org.onlab.packet.IPv6;
+
import java.nio.ByteBuffer;
import java.util.Arrays;
+import static org.onlab.packet.PacketUtils.checkInput;
+
/**
* Implements IPv6 authentication extension header format. (RFC 4302)
*/
@@ -186,22 +191,20 @@
this.integrityCheck = new byte[icvLength];
bb.get(this.integrityCheck, 0, icvLength);
- IPacket payload;
- if (IPv6.PROTOCOL_CLASS_MAP.containsKey(this.nextHeader)) {
- final Class<? extends IPacket> clazz = IPv6.PROTOCOL_CLASS_MAP
- .get(this.nextHeader);
- try {
- payload = clazz.newInstance();
- } catch (final Exception e) {
- throw new RuntimeException(
- "Error parsing payload for Authentication packet", e);
- }
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(this.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(this.nextHeader);
} else {
- payload = new Data();
+ deserializer = Data.deserializer();
}
- this.payload = payload.deserialize(data, bb.position(),
- bb.limit() - bb.position());
- this.payload.setParent(this);
+
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
return this;
}
@@ -259,4 +262,39 @@
}
return true;
}
+
+ /**
+ * Deserializer function for authentication headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Authentication> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, FIXED_HEADER_LENGTH);
+
+ Authentication authentication = new Authentication();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ authentication.nextHeader = bb.get();
+ authentication.payloadLength = bb.get();
+ bb.getShort();
+ authentication.securityParamIndex = bb.getInt();
+ authentication.sequence = bb.getInt();
+ int icvLength = (authentication.payloadLength + MINUS) * LENGTH_UNIT - FIXED_HEADER_LENGTH;
+ authentication.integrityCheck = new byte[icvLength];
+ bb.get(authentication.integrityCheck, 0, icvLength);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(authentication.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(authentication.nextHeader);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ authentication.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ authentication.payload.setParent(authentication);
+
+ return authentication;
+ };
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ipv6/BaseOptions.java b/utils/misc/src/main/java/org/onlab/packet/ipv6/BaseOptions.java
index 6fd81cf..f57b756 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ipv6/BaseOptions.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ipv6/BaseOptions.java
@@ -18,12 +18,17 @@
import org.onlab.packet.BasePacket;
import org.onlab.packet.Data;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
import org.onlab.packet.IPacket;
import org.onlab.packet.IPv6;
import java.nio.ByteBuffer;
import java.util.Arrays;
+import static org.onlab.packet.PacketUtils.checkHeaderLength;
+import static org.onlab.packet.PacketUtils.checkInput;
+
/**
* Base class for hop-by-hop options and destination options.
*/
@@ -151,22 +156,19 @@
this.options = new byte[optionLength];
bb.get(this.options, 0, optionLength);
- IPacket payload;
- if (IPv6.PROTOCOL_CLASS_MAP.containsKey(this.nextHeader)) {
- final Class<? extends IPacket> clazz = IPv6.PROTOCOL_CLASS_MAP
- .get(this.nextHeader);
- try {
- payload = clazz.newInstance();
- } catch (final Exception e) {
- throw new RuntimeException(
- "Error parsing payload for BaseOptions packet", e);
- }
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(this.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(this.nextHeader);
} else {
- payload = new Data();
+ deserializer = Data.deserializer();
}
- this.payload = payload.deserialize(data, bb.position(),
- bb.limit() - bb.position());
- this.payload.setParent(this);
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
return this;
}
@@ -219,4 +221,40 @@
}
return true;
}
+
+ /**
+ * Deserializer function for IPv6 base options.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<BaseOptions> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, FIXED_HEADER_LENGTH);
+
+ BaseOptions baseOptions = new BaseOptions();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ baseOptions.nextHeader = bb.get();
+ baseOptions.headerExtLength = bb.get();
+ int optionLength =
+ FIXED_OPTIONS_LENGTH + LENGTH_UNIT * baseOptions.headerExtLength;
+
+ checkHeaderLength(bb.remaining(), optionLength);
+
+ baseOptions.options = new byte[optionLength];
+ bb.get(baseOptions.options, 0, optionLength);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(baseOptions.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(baseOptions.nextHeader);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ baseOptions.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ baseOptions.payload.setParent(baseOptions);
+
+ return baseOptions;
+ };
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ipv6/EncapSecurityPayload.java b/utils/misc/src/main/java/org/onlab/packet/ipv6/EncapSecurityPayload.java
index ffe552f..e46a126 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ipv6/EncapSecurityPayload.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ipv6/EncapSecurityPayload.java
@@ -18,10 +18,14 @@
import org.onlab.packet.BasePacket;
import org.onlab.packet.Data;
+import org.onlab.packet.Deserializer;
import org.onlab.packet.IPacket;
import org.onlab.packet.IPv6;
+
import java.nio.ByteBuffer;
+import static org.onlab.packet.PacketUtils.checkInput;
+
/**
* Implements IPv6 Encapsulating Security Payload (ESP) extension header format.
* (RFC 4303)
@@ -113,7 +117,7 @@
this.payload = new Data();
this.payload.deserialize(data, bb.position(),
- bb.limit() - bb.position());
+ bb.limit() - bb.position());
this.payload.setParent(this);
return this;
@@ -158,4 +162,27 @@
}
return true;
}
+
+ /**
+ * Deserializer function for encapsulated security payload headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<EncapSecurityPayload> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ EncapSecurityPayload encapSecurityPayload = new EncapSecurityPayload();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ encapSecurityPayload.securityParamIndex = bb.getInt();
+ encapSecurityPayload.sequence = bb.getInt();
+
+ encapSecurityPayload.payload = Data.deserializer().deserialize(
+ data, bb.position(), bb.limit() - bb.position());
+ encapSecurityPayload.payload.setParent(encapSecurityPayload);
+
+ return encapSecurityPayload;
+ };
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ipv6/Fragment.java b/utils/misc/src/main/java/org/onlab/packet/ipv6/Fragment.java
index 5a0d4b4..68015d3 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ipv6/Fragment.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ipv6/Fragment.java
@@ -18,11 +18,15 @@
import org.onlab.packet.BasePacket;
import org.onlab.packet.Data;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
import org.onlab.packet.IPacket;
import org.onlab.packet.IPv6;
import java.nio.ByteBuffer;
+import static org.onlab.packet.PacketUtils.checkInput;
+
/**
* Implements IPv6 fragment extension header format. (RFC 2460)
*/
@@ -149,22 +153,19 @@
this.moreFragment = (byte) (sscratch & 0x1);
this.identification = bb.getInt();
- IPacket payload;
- if (IPv6.PROTOCOL_CLASS_MAP.containsKey(this.nextHeader)) {
- final Class<? extends IPacket> clazz = IPv6.PROTOCOL_CLASS_MAP
- .get(this.nextHeader);
- try {
- payload = clazz.newInstance();
- } catch (final Exception e) {
- throw new RuntimeException(
- "Error parsing payload for Fragment packet", e);
- }
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(this.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(this.nextHeader);
} else {
- payload = new Data();
+ deserializer = Data.deserializer();
}
- this.payload = payload.deserialize(data, bb.position(),
- bb.limit() - bb.position());
- this.payload.setParent(this);
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
return this;
}
@@ -216,4 +217,37 @@
}
return true;
}
+
+ /**
+ * Deserializer function for fragment headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Fragment> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ Fragment fragment = new Fragment();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ fragment.nextHeader = bb.get();
+ bb.get();
+ short sscratch = bb.getShort();
+ fragment.fragmentOffset = (short) (sscratch >> 3 & 0x1fff);
+ fragment.moreFragment = (byte) (sscratch & 0x1);
+ fragment.identification = bb.getInt();
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(fragment.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(fragment.nextHeader);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ fragment.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ fragment.payload.setParent(fragment);
+
+ return fragment;
+ };
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ipv6/Routing.java b/utils/misc/src/main/java/org/onlab/packet/ipv6/Routing.java
index 808d2ec..d7d204a 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ipv6/Routing.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ipv6/Routing.java
@@ -18,12 +18,17 @@
import org.onlab.packet.BasePacket;
import org.onlab.packet.Data;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
import org.onlab.packet.IPacket;
import org.onlab.packet.IPv6;
import java.nio.ByteBuffer;
import java.util.Arrays;
+import static org.onlab.packet.PacketUtils.checkHeaderLength;
+import static org.onlab.packet.PacketUtils.checkInput;
+
/**
* Implements IPv6 routing extension header format. (RFC 2460)
*/
@@ -175,22 +180,19 @@
this.routingData = new byte[dataLength];
bb.get(this.routingData, 0, dataLength);
- IPacket payload;
- if (IPv6.PROTOCOL_CLASS_MAP.containsKey(this.nextHeader)) {
- final Class<? extends IPacket> clazz = IPv6.PROTOCOL_CLASS_MAP
- .get(this.nextHeader);
- try {
- payload = clazz.newInstance();
- } catch (final Exception e) {
- throw new RuntimeException(
- "Error parsing payload for Routing packet", e);
- }
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(this.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(this.nextHeader);
} else {
- payload = new Data();
+ deserializer = new Data().deserializer();
}
- this.payload = payload.deserialize(data, bb.position(),
- bb.limit() - bb.position());
- this.payload.setParent(this);
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
return this;
}
@@ -248,4 +250,42 @@
}
return true;
}
-}
\ No newline at end of file
+
+ /**
+ * Deserializer function for routing headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Routing> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, FIXED_HEADER_LENGTH);
+
+ Routing routing = new Routing();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ routing.nextHeader = bb.get();
+ routing.headerExtLength = bb.get();
+ routing.routingType = bb.get();
+ routing.segmentsLeft = bb.get();
+ int dataLength =
+ FIXED_ROUTING_DATA_LENGTH + LENGTH_UNIT * routing.headerExtLength;
+
+ checkHeaderLength(bb.remaining(), dataLength);
+
+ routing.routingData = new byte[dataLength];
+ bb.get(routing.routingData, 0, dataLength);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(routing.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(routing.nextHeader);
+ } else {
+ deserializer = new Data().deserializer();
+ }
+ routing.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ routing.payload.setParent(routing);
+
+ return routing;
+ };
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java b/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java
index ccb87e3..08c749a 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java
@@ -16,6 +16,7 @@
package org.onlab.packet.ndp;
import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
import org.onlab.packet.IPacket;
import org.onlab.packet.Ip6Address;
@@ -23,6 +24,8 @@
import java.util.Arrays;
import java.util.List;
+import static org.onlab.packet.PacketUtils.checkInput;
+
/**
* Implements ICMPv6 Neighbor Advertisement packet format (RFC 4861).
*/
@@ -238,4 +241,36 @@
}
return true;
}
+
+ /**
+ * Deserializer function for neighbor advertisement packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<NeighborAdvertisement> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ NeighborAdvertisement neighborAdvertisement = new NeighborAdvertisement();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ int iscratch;
+
+ iscratch = bb.getInt();
+ neighborAdvertisement.routerFlag = (byte) (iscratch >> 31 & 0x1);
+ neighborAdvertisement.solicitedFlag = (byte) (iscratch >> 30 & 0x1);
+ neighborAdvertisement.overrideFlag = (byte) (iscratch >> 29 & 0x1);
+ bb.get(neighborAdvertisement.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+
+ for (NeighborDiscoveryOptions.Option option : options.options()) {
+ neighborAdvertisement.addOption(option.type(), option.data());
+ }
+
+ return neighborAdvertisement;
+ };
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborDiscoveryOptions.java b/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborDiscoveryOptions.java
index b8561b1..00a2606 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborDiscoveryOptions.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborDiscoveryOptions.java
@@ -15,13 +15,17 @@
*/
package org.onlab.packet.ndp;
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import org.onlab.packet.BasePacket;
-import org.onlab.packet.IPacket;
+import static org.onlab.packet.PacketUtils.checkInput;
/**
* Neighbor Discovery Protocol packet options.
@@ -33,6 +37,11 @@
public static final byte TYPE_REDIRECTED_HEADER = 4;
public static final byte TYPE_MTU = 5;
+ public static final byte INITIAL_HEADER_REQUIRED = 2;
+
+ private static final String BUFFER_UNDERFLOW_ERROR =
+ "Not enough bytes in buffer to read option";
+
private final List<Option> options = new ArrayList<>();
/**
@@ -187,7 +196,7 @@
}
byte lengthField = bb.get();
int dataLength = lengthField * 8; // The data length field is in
- // unit of 8 octets
+ // unit of 8 octets
// Exclude the type and length fields
if (dataLength < 2) {
@@ -229,4 +238,44 @@
}
return false;
}
+
+ public static Deserializer<NeighborDiscoveryOptions> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, INITIAL_HEADER_REQUIRED);
+
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ NeighborDiscoveryOptions ndo = new NeighborDiscoveryOptions();
+
+ ndo.options.clear();
+
+ //
+ // Deserialize all options
+ //
+ while (bb.hasRemaining()) {
+ byte type = bb.get();
+ if (!bb.hasRemaining()) {
+ throw new DeserializationException(BUFFER_UNDERFLOW_ERROR);
+ }
+ byte lengthField = bb.get();
+ int dataLength = lengthField * 8; // The data length field is in
+ // unit of 8 octets
+
+ // Exclude the type and length fields
+ if (dataLength < 2) {
+ break;
+ }
+ dataLength -= 2;
+
+ if (bb.remaining() < dataLength) {
+ throw new DeserializationException(BUFFER_UNDERFLOW_ERROR);
+ }
+ byte[] optionData = new byte[dataLength];
+ bb.get(optionData, 0, optionData.length);
+ ndo.addOption(type, optionData);
+ }
+
+ return ndo;
+ };
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborSolicitation.java b/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborSolicitation.java
index 25df3b4..33a6eb2b6 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborSolicitation.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborSolicitation.java
@@ -16,6 +16,7 @@
package org.onlab.packet.ndp;
import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
import org.onlab.packet.IPacket;
import org.onlab.packet.Ip6Address;
@@ -23,6 +24,8 @@
import java.util.Arrays;
import java.util.List;
+import static org.onlab.packet.PacketUtils.checkInput;
+
/**
* Implements ICMPv6 Neighbor Solicitation packet format. (RFC 4861)
*/
@@ -157,4 +160,31 @@
}
return true;
}
+
+ /**
+ * Deserializer function for neighbor solicitation packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<NeighborSolicitation> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ NeighborSolicitation neighborSolicitation = new NeighborSolicitation();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ bb.getInt();
+ bb.get(neighborSolicitation.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+
+ for (NeighborDiscoveryOptions.Option option : options.options()) {
+ neighborSolicitation.addOption(option.type(), option.data());
+ }
+
+ return neighborSolicitation;
+ };
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ndp/Redirect.java b/utils/misc/src/main/java/org/onlab/packet/ndp/Redirect.java
index 0515869..c3f46dd 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ndp/Redirect.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ndp/Redirect.java
@@ -16,6 +16,7 @@
package org.onlab.packet.ndp;
import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
import org.onlab.packet.IPacket;
import org.onlab.packet.Ip6Address;
@@ -23,6 +24,8 @@
import java.util.Arrays;
import java.util.List;
+import static org.onlab.packet.PacketUtils.checkInput;
+
/**
* Implements ICMPv6 Redirect packet format. (RFC 4861)
*/
@@ -188,4 +191,33 @@
}
return true;
}
+
+ /**
+ * Deserializer function for redirect packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Redirect> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ Redirect redirect = new Redirect();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ bb.getInt();
+
+ bb.get(redirect.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ bb.get(redirect.destinationAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+
+ for (NeighborDiscoveryOptions.Option option : options.options()) {
+ redirect.addOption(option.type(), option.data());
+ }
+
+ return redirect;
+ };
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ndp/RouterAdvertisement.java b/utils/misc/src/main/java/org/onlab/packet/ndp/RouterAdvertisement.java
index 1c2f928..e4ef2c6 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ndp/RouterAdvertisement.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ndp/RouterAdvertisement.java
@@ -16,11 +16,14 @@
package org.onlab.packet.ndp;
import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
import org.onlab.packet.IPacket;
import java.nio.ByteBuffer;
import java.util.List;
+import static org.onlab.packet.PacketUtils.checkInput;
+
/**
* Implements ICMPv6 Router Advertisement packet format. (RFC 4861)
*/
@@ -284,4 +287,37 @@
}
return true;
}
+
+ /**
+ * Deserializer function for router advertisement packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<RouterAdvertisement> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ RouterAdvertisement routerAdvertisement = new RouterAdvertisement();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ int bscratch;
+
+ routerAdvertisement.currentHopLimit = bb.get();
+ bscratch = bb.get();
+ routerAdvertisement.mFlag = (byte) ((bscratch >> 7) & 0x1);
+ routerAdvertisement.oFlag = (byte) ((bscratch >> 6) & 0x1);
+ routerAdvertisement.routerLifetime = bb.getShort();
+ routerAdvertisement.reachableTime = bb.getInt();
+ routerAdvertisement.retransmitTimer = bb.getInt();
+
+ NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+
+ for (NeighborDiscoveryOptions.Option option : options.options()) {
+ routerAdvertisement.addOption(option.type(), option.data());
+ }
+
+ return routerAdvertisement;
+ };
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ndp/RouterSolicitation.java b/utils/misc/src/main/java/org/onlab/packet/ndp/RouterSolicitation.java
index 3fddc5e..07729df 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ndp/RouterSolicitation.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ndp/RouterSolicitation.java
@@ -16,19 +16,21 @@
package org.onlab.packet.ndp;
import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
import org.onlab.packet.IPacket;
import java.nio.ByteBuffer;
import java.util.List;
+import static org.onlab.packet.PacketUtils.checkInput;
+
/**
* Implements ICMPv6 Router Solicitation packet format. (RFC 4861)
*/
public class RouterSolicitation extends BasePacket {
public static final byte HEADER_LENGTH = 4; // bytes
- private final NeighborDiscoveryOptions options =
- new NeighborDiscoveryOptions();
+ private final NeighborDiscoveryOptions options = new NeighborDiscoveryOptions();
/**
* Gets the Neighbor Discovery Protocol packet options.
@@ -122,4 +124,30 @@
}
return true;
}
+
+ /**
+ * Deserializer function for router solicitation packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<RouterSolicitation> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ RouterSolicitation routerSolicitation = new RouterSolicitation();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ bb.getInt();
+
+ NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+
+ for (NeighborDiscoveryOptions.Option option : options.options()) {
+ routerSolicitation.addOption(option.type(), option.data());
+ }
+
+ return routerSolicitation;
+ };
+ }
}
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));