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/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;
+ };
}
}