Improve the resiliency of the packet deserialization code.
Packet deserializers now check for malformed input while reading the byte
stream. Deserializers are re-implemented as functions that take a byte array
and return a packet object. The old IPacket.deserialize(...) methods have been
deprecated with the goal of eventually moving to immutable packet objects.
Unit tests have been implemented for all Deserializer functions.
ONOS-1589
Change-Id: I9073d5e6e7991e15d43830cfd810989256b71c56
diff --git a/utils/misc/src/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;
+ };
+ }
}