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 1af3fd9a44..e068c940f8 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.onosproject.routing.config.RoutingConfigurationService;
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 @@ public class IcmpHandler {
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 b3da8dcb23..0f8fa59d74 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.onosproject.net.packet.OutboundPacket;
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 @@ public class IcmpHandler {
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 679a888eeb..90cf6b4e28 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.Collections;
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 @@ public class HostMonitorTest {
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 @@ public class HostMonitorTest {
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 be7e4b0872..f4ca827ccd 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.action.OFActionOutput;
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 final class DefaultOpenFlowPacketContext implements OpenFlowPacketContext
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 5d44c91fb2..6d1531036b 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.DefaultPacketContext;
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 @@ import java.util.List;
*/
public class OpenFlowCorePacketContext extends DefaultPacketContext {
+ private static final Logger log = LoggerFactory.getLogger(OpenFlowCorePacketContext.class);
+
private final OpenFlowPacketContext ofPktCtx;
/**
@@ -57,12 +62,15 @@ public class OpenFlowCorePacketContext extends DefaultPacketContext {
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 e3635f493a..1795121504 100644
--- a/utils/misc/pom.xml
+++ b/utils/misc/pom.xml
@@ -41,6 +41,11 @@
onlab-junit
test
+
+ org.easymock
+ easymock
+ test
+
io.netty
netty
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 88e1d4ff49..dc3c07f159 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 @@ package org.onlab.packet;
import java.nio.ByteBuffer;
import java.util.Arrays;
+import static org.onlab.packet.PacketUtils.*;
+
/**
*
*
@@ -35,6 +37,8 @@ public class ARP extends BasePacket {
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 @@ public class ARP extends BasePacket {
@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 @@ public class ARP extends BasePacket {
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 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 8dba13c593..40a7745d3c 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.ArrayList;
import java.util.List;
import java.util.ListIterator;
+import static org.onlab.packet.PacketUtils.*;
+
/**
*
*/
@@ -429,33 +431,9 @@ public class DHCP extends BasePacket {
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 @@ public class DHCP extends BasePacket {
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 @@ public class DHCP extends BasePacket {
}
return result;
}
+
+ /**
+ * Deserializer function for DHCP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer 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 f3a10929a4..79abcba1fc 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 @@ package org.onlab.packet;
import java.util.Arrays;
+import static org.onlab.packet.PacketUtils.*;
+
/**
*
*/
@@ -30,6 +32,7 @@ public class Data extends BasePacket {
*
*/
public Data() {
+ data = new byte[0];
}
/**
@@ -63,7 +66,7 @@ public class Data extends BasePacket {
@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 @@ public class Data extends BasePacket {
}
return true;
}
+
+ /**
+ * Deserializer function for generic payload data.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer 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 0000000000..03a8aa35d6
--- /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 0000000000..e0ef381b1a
--- /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 {
+
+ /**
+ * 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 4f52b7d54f..7573ae896d 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 class EthType {
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 @@ public class EthType {
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 779d96cfc4..700bdfc416 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 class Ethernet extends BasePacket {
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> ETHER_TYPE_CLASS_MAP =
- new HashMap<>();
+
+ private static final Map> 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 @@ public class Ethernet extends BasePacket {
@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 @@ public class Ethernet extends BasePacket {
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();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
}
- this.payload = payload.deserialize(data, bb.position(),
- bb.limit() - bb.position());
- this.payload.setParent(this);
return this;
}
@@ -567,4 +572,53 @@ public class Ethernet extends BasePacket {
return builder.toString();
}
+ /**
+ * Deserializer function for Ethernet packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer 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 46814cd136..d07a9ba483 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 @@ package org.onlab.packet;
import java.nio.ByteBuffer;
+import static org.onlab.packet.PacketUtils.*;
+
/**
* Implements ICMP packet format.
*
@@ -33,6 +35,8 @@ public class ICMP extends BasePacket {
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 @@ public class ICMP extends BasePacket {
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 @@ public class ICMP extends BasePacket {
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 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 7c9b4b989c..c8981302ce 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.NeighborSolicitation;
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 @@ public class ICMP6 extends BasePacket {
/** Unrecognized IPv6 option encountered. */
public static final byte IPV6_OPT_ERR = (byte) 0x01;
- public static final Map> PROTOCOL_CLASS_MAP =
+ public static final Map> 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 @@ public class ICMP6 extends BasePacket {
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 @@ public class ICMP6 extends BasePacket {
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 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 ac6ae6098f..38684ebc46 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 @@ public interface IPacket {
/**
* 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 @@ public interface IPacket {
* 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 d1563ebbff..0d75245730 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.Collection;
import java.util.HashMap;
import java.util.Map;
+import static org.onlab.packet.PacketUtils.*;
+
/**
*
*/
@@ -32,19 +34,21 @@ public class IPv4 extends BasePacket {
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> PROTOCOL_CLASS_MAP =
+ public static final Map> 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 @@ s */
@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 @@ s */
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 @@ s */
}
return true;
}
+
+ /**
+ * Deserializer function for IPv4 packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer 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 3bb35c539e..2e59632adb 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.Authentication;
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 class IPv6 extends BasePacket implements IExtensionHeader {
public static final byte PROTOCOL_DSTOPT = 0x3C;
- public static final Map> PROTOCOL_CLASS_MAP =
+ public static final Map> 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 @@ public class IPv6 extends BasePacket implements IExtensionHeader {
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();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
}
- this.payload = payload.deserialize(data, bb.position(),
- bb.limit() - bb.position());
- this.payload.setParent(this);
return this;
}
@@ -343,4 +343,42 @@ public class IPv6 extends BasePacket implements IExtensionHeader {
}
return true;
}
+
+ /**
+ * Deserializer function for IPv6 packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer 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 035f396d37..78b4f3fa8f 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 @@ package org.onlab.packet;
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 @@ import java.nio.ByteBuffer;
*
*/
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 @@ public class LLC extends BasePacket {
@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 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 339bc3a7a5..ae9d717393 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 @@ public class LLDP extends BasePacket {
protected short ethType;
public LLDP() {
- this.optionalTLVList = new ArrayList();
+ this.optionalTLVList = new LinkedList<>();
this.ethType = Ethernet.TYPE_LLDP;
}
@@ -134,11 +147,15 @@ public class LLDP extends BasePacket {
@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 @@ public class LLDP extends BasePacket {
}
return true;
}
+
+ /**
+ * Deserializer function for LLDP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer 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 497e0ad716..bedf439f54 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 @@ public class LLDPOrganizationalTLV extends LLDPTLV {
}
@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 b5fe83313b..77efe1b719 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 @@ public class LLDPTLV {
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 4ba6a0c756..47dbeed2f7 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.nio.ByteBuffer;
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> PROTOCOL_CLASS_MAP;
+ static Map> protocolDeserializerMap
+ = new HashMap<>();
static {
- PROTOCOL_CLASS_MAP = new HashMap>();
- 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 @@ public class MPLS extends BasePacket {
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();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(), bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
}
- this.payload = payload.deserialize(data, bb.position(), bb.limit() - bb.position());
- this.payload.setParent(this);
return this;
}
@@ -112,4 +114,34 @@ public class MPLS extends BasePacket {
this.ttl = ttl;
}
+ /**
+ * Deserializer function for MPLS packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer 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 0000000000..c3bede2fd8
--- /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 3b92c83fc9..b13b53c488 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 @@ package org.onlab.packet;
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 @@ public class TCP extends BasePacket {
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 @@ public class TCP extends BasePacket {
&& (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 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 c743f09c51..32432e6edb 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.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
+import static org.onlab.packet.PacketUtils.*;
+
/**
*
*/
public class UDP extends BasePacket {
- public static final Map> DECODE_MAP =
+ public static final Map> 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 @@ public class UDP extends BasePacket {
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 @@ public class UDP extends BasePacket {
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 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 d4c741fa18..ec04a812c5 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 @@ package org.onlab.packet.ipv6;
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 @@ public class Authentication extends BasePacket implements IExtensionHeader {
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();
+ }
+
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
}
- this.payload = payload.deserialize(data, bb.position(),
- bb.limit() - bb.position());
- this.payload.setParent(this);
return this;
}
@@ -259,4 +262,39 @@ public class Authentication extends BasePacket implements IExtensionHeader {
}
return true;
}
+
+ /**
+ * Deserializer function for authentication headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer 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 6fd81cf7d0..f57b756ee8 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 @@ package org.onlab.packet.ipv6;
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 @@ public class BaseOptions extends BasePacket implements IExtensionHeader {
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();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
}
- this.payload = payload.deserialize(data, bb.position(),
- bb.limit() - bb.position());
- this.payload.setParent(this);
return this;
}
@@ -219,4 +221,40 @@ public class BaseOptions extends BasePacket implements IExtensionHeader {
}
return true;
}
+
+ /**
+ * Deserializer function for IPv6 base options.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer 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 ffe552fec3..e46a126101 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 @@ package org.onlab.packet.ipv6;
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 @@ public class EncapSecurityPayload extends BasePacket {
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 @@ public class EncapSecurityPayload extends BasePacket {
}
return true;
}
+
+ /**
+ * Deserializer function for encapsulated security payload headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer 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 5a0d4b4db0..68015d31a2 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 @@ package org.onlab.packet.ipv6;
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 @@ public class Fragment extends BasePacket implements IExtensionHeader {
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();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
}
- this.payload = payload.deserialize(data, bb.position(),
- bb.limit() - bb.position());
- this.payload.setParent(this);
return this;
}
@@ -216,4 +217,37 @@ public class Fragment extends BasePacket implements IExtensionHeader {
}
return true;
}
+
+ /**
+ * Deserializer function for fragment headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer 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 808d2ecc63..d7d204a91b 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 @@ package org.onlab.packet.ipv6;
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 @@ public class Routing extends BasePacket implements IExtensionHeader {
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();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
}
- this.payload = payload.deserialize(data, bb.position(),
- bb.limit() - bb.position());
- this.payload.setParent(this);
return this;
}
@@ -248,4 +250,42 @@ public class Routing extends BasePacket implements IExtensionHeader {
}
return true;
}
-}
\ No newline at end of file
+
+ /**
+ * Deserializer function for routing headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer 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 ccb87e3fb3..08c749a1f9 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.nio.ByteBuffer;
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 @@ public class NeighborAdvertisement extends BasePacket {
}
return true;
}
+
+ /**
+ * Deserializer function for neighbor advertisement packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer 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 b8561b1c3e..00a2606888 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 class NeighborDiscoveryOptions extends BasePacket {
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