From 50516efa1c2dcb3d2ac3a9c57b373d18231f6c12 Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Wed, 22 May 2013 18:01:41 -0700 Subject: [PATCH] ICMP fix and Packet class should store and provide access to the raw payload in case no packet class is available to parse it. - Packet to store a copy of the raw payload if no packet class is present to parse it - Added missing code for ICMP - Refactored PAcket, ICMP and IPv4 code - Fixed corrupted flag setting when comparing checksum after deserializing - Added Junit for IPv4.computeChecksum() and for serilaize/deserialize ICMP message - Addressed some eclipse and static analysis warnings Change-Id:If2ae95cbc3462ffcda8662e27efd9de5614872ef Signed-off-by: Alessandro Boch --- .../controller/sal/packet/Ethernet.java | 16 +- .../controller/sal/packet/ICMP.java | 118 ++++++++-- .../controller/sal/packet/IPv4.java | 121 +++++----- .../controller/sal/packet/LLDPTLV.java | 17 +- .../controller/sal/packet/Packet.java | 214 +++++++++++------- .../controller/sal/packet/RawPacket.java | 28 ++- .../controller/sal/packet/TCP.java | 17 +- .../controller/sal/packet/UDP.java | 16 +- .../controller/sal/utils/HexEncode.java | 72 +++--- .../controller/sal/packet/ICMPTest.java | 53 ++++- .../controller/sal/packet/IPv4Test.java | 127 ++++++++++- 11 files changed, 541 insertions(+), 258 deletions(-) diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/Ethernet.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/Ethernet.java index 0f1a7f54f3..c96e901561 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/Ethernet.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/Ethernet.java @@ -13,16 +13,12 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.opendaylight.controller.sal.utils.EtherTypes; /** * Class that represents the Ethernet frame objects - * - * */ public class Ethernet extends Packet { private static final String DMAC = "DestinationMACAddress"; @@ -46,7 +42,7 @@ public class Ethernet extends Packet { put(ETHT, new ImmutablePair(96, 16)); } }; - private Map fieldValues; + private final Map fieldValues; /** * Default constructor that creates and sets the HashMap @@ -130,14 +126,4 @@ public class Ethernet extends Packet { return this; } - @Override - public int hashCode() { - return HashCodeBuilder.reflectionHashCode(this); - } - - @Override - public boolean equals(Object obj) { - return EqualsBuilder.reflectionEquals(this, obj); - } - } diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/ICMP.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/ICMP.java index d1f81c5775..0429c0dd27 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/ICMP.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/ICMP.java @@ -13,34 +13,29 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; +import org.opendaylight.controller.sal.utils.NetUtils; /** * Class that represents the ICMP packet objects - * - * */ public class ICMP extends Packet { - private static final String TYPECODE = "TypeCode"; + private static final String TYPE = "Type"; private static final String CODE = "Code"; - private static final String HEADERCHECKSUM = "HeaderChecksum"; + private static final String CHECKSUM = "Checksum"; private static final String IDENTIFIER = "Identifier"; private static final String SEQNUMBER = "SequenceNumber"; private static Map> fieldCoordinates = new LinkedHashMap>() { private static final long serialVersionUID = 1L; - { - put(TYPECODE, new ImmutablePair(0, 8)); + put(TYPE, new ImmutablePair(0, 8)); put(CODE, new ImmutablePair(8, 8)); - put(HEADERCHECKSUM, new ImmutablePair(16, 16)); + put(CHECKSUM, new ImmutablePair(16, 16)); put(IDENTIFIER, new ImmutablePair(32, 16)); put(SEQNUMBER, new ImmutablePair(48, 16)); - } }; @@ -64,7 +59,7 @@ public class ICMP extends Packet { hdrFieldsMap = fieldValues; } - private Map fieldValues; + private final Map fieldValues; @Override public void setHeaderField(String headerField, byte[] readValue) { @@ -72,13 +67,28 @@ public class ICMP extends Packet { } /** - * Sets the TypeCode of ICMP for the current ICMP object instance - * @param short - typeCode - * @return ICMP + * Sets the type for the current ICMP message + * + * @param type + * The ICMP message type + * @return This ICMP object + */ + public ICMP setType(byte type) { + byte[] icmpType = BitBufferHelper.toByteArray(type); + fieldValues.put(TYPE, icmpType); + return this; + } + + /** + * Sets the ICMP code (type subtype) for the current ICMP object instance + * + * @param code + * The ICMP message type subtype + * @return This ICMP object */ - public ICMP setTypeCode(short typeCode) { - byte[] icmpTypeCode = BitBufferHelper.toByteArray(typeCode); - fieldValues.put(TYPECODE, icmpTypeCode); + public ICMP setCode(byte code) { + byte[] icmpCode = BitBufferHelper.toByteArray(code); + fieldValues.put(CODE, icmpCode); return this; } @@ -89,7 +99,7 @@ public class ICMP extends Packet { */ public ICMP setChecksum(short checksum) { byte[] icmpChecksum = BitBufferHelper.toByteArray(checksum); - fieldValues.put(HEADERCHECKSUM, icmpChecksum); + fieldValues.put(CHECKSUM, icmpChecksum); return this; } @@ -115,13 +125,77 @@ public class ICMP extends Packet { return this; } + /** + * Gets the header size in bits + * @return The ICMP header size in bits + */ @Override - public int hashCode() { - return HashCodeBuilder.reflectionHashCode(this); + public int getHeaderSize() { + return 64; + } + + /** + * Computes the ICMP checksum on the serialized ICMP message + * + * @param serialized + * The data stream + * @param start + * The byte index on the data stream from which the ICMP packet + * starts + * @return The checksum + */ + short computeChecksum(byte[] data, int start) { + int sum = 0, carry = 0, finalSum = 0; + int end = start + this.getHeaderSize() / NetUtils.NumBitsInAByte + + rawPayload.length; + int checksumStartByte = start + getfieldOffset(CHECKSUM) + / NetUtils.NumBitsInAByte; + + for (int i = start; i <= (end - 1); i = i + 2) { + // Skip, if the current bytes are checkSum bytes + if (i == checksumStartByte) { + continue; + } + StringBuffer sbuffer = new StringBuffer(); + sbuffer.append(String.format("%02X", data[i])); + if (i < (data.length - 1)) { + sbuffer.append(String.format("%02X", data[i + 1])); + } + sum += Integer.valueOf(sbuffer.toString(), 16); + } + carry = (sum >> 16) & 0xFF; + finalSum = (sum & 0xFFFF) + carry; + return (short) ~((short) finalSum & 0xFFFF); + } + + @Override + protected void postSerializeCustomOperation(byte[] serializedBytes) + throws PacketException { + byte[] checkSum = BitBufferHelper + .toByteArray(computeChecksum(serializedBytes, 0)); + try { + BitBufferHelper.setBytes(serializedBytes, checkSum, + getfieldOffset(CHECKSUM), getfieldnumBits(CHECKSUM)); + } catch (BufferException e) { + throw new PacketException(e.getMessage()); + } } @Override - public boolean equals(Object obj) { - return EqualsBuilder.reflectionEquals(this, obj); + protected void postDeserializeCustomOperation(byte[] data, int endBitOffset) { + short computedChecksum = computeChecksum(data, endBitOffset / NetUtils.NumBitsInAByte); + short actualChecksum = BitBufferHelper.getShort(fieldValues.get(CHECKSUM)); + + if (computedChecksum != actualChecksum) { + corrupted = true; + } + } + + /** + * Gets the checksum value stored + * @return the checksum + */ + public short getChecksum() { + return (BitBufferHelper.getShort(fieldValues.get(CHECKSUM))); } } diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/IPv4.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/IPv4.java index d547e2c905..c7b97e9d48 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/IPv4.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/IPv4.java @@ -18,8 +18,6 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Random; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.opendaylight.controller.sal.utils.IPProtocols; @@ -29,13 +27,11 @@ import org.slf4j.LoggerFactory; /** * Class that represents the IPv4 packet objects - * - * */ public class IPv4 extends Packet { protected static final Logger logger = LoggerFactory - .getLogger(IPv4.class); + .getLogger(IPv4.class); private static final String VERSION = "Version"; private static final String HEADERLENGTH = "HeaderLength"; private static final String DIFFSERV = "DiffServ"; @@ -78,7 +74,8 @@ public class IPv4 extends Packet { } }; - private Map fieldValues; + private final Map fieldValues; + /** * Default constructor that sets the version to 4, headerLength to 5, @@ -90,14 +87,15 @@ public class IPv4 extends Packet { fieldValues = new HashMap(); hdrFieldCoordMap = fieldCoordinates; hdrFieldsMap = fieldValues; + corrupted = false; setVersion((byte) 4); setHeaderLength((byte) 5); setDiffServ((byte) 0); + setECN((byte) 0); setIdentification(generateId()); setFlags((byte) 2); setFragmentOffset((short) 0); - setECN((byte) 0); } /** @@ -112,14 +110,15 @@ public class IPv4 extends Packet { fieldValues = new HashMap(); hdrFieldCoordMap = fieldCoordinates; hdrFieldsMap = fieldValues; + corrupted = false; setVersion((byte) 4); setHeaderLength((byte) 5); setDiffServ((byte) 0); + setECN((byte) 0); setIdentification(generateId()); setFlags((byte) 2); setFragmentOffset((short) 0); - setECN((byte) 0); } /** @@ -139,21 +138,22 @@ public class IPv4 extends Packet { } /** - * Gets the header length in bits, from the header length stored and options if any - * @return HeaderLength to serialize code + * Gets the header size in bits + * @return The number of bits constituting the header */ @Override public int getHeaderSize() { int headerLen = this.getHeaderLen(); - if (headerLen == 0) + if (headerLen == 0) { headerLen = 20; + } byte[] options = hdrFieldsMap.get(OPTIONS); - if (options != null) + if (options != null) { headerLen += options.length; + } return headerLen * NetUtils.NumBitsInAByte; - } /** @@ -442,27 +442,33 @@ public class IPv4 extends Packet { } /** - * Computes the header checksum - * @param byte[] hdrBytes - serialized bytes - * @param int endBitOffset - end bit Offset - * @return short - the computed checksum + * Computes the IPv4 header checksum on the passed stream of bytes + * representing the packet + * + * @param data + * The byte stream + * @param offset + * The byte offset from where the IPv4 packet starts + * @return The computed checksum */ - private short computeChecksum(byte[] hdrBytes, int endByteOffset) { - int startByteOffset = endByteOffset - getHeaderLen(); + short computeChecksum(byte[] data, int start) { + int end = start + getHeaderLen(); short checkSum = (short) 0; int sum = 0, carry = 0, finalSum = 0; int parsedHex = 0; - int checksumStartByte = startByteOffset + getfieldOffset(CHECKSUM) - / NetUtils.NumBitsInAByte; + int checksumStart = start + + (getfieldOffset(CHECKSUM) / NetUtils.NumBitsInAByte); - for (int i = startByteOffset; i <= (endByteOffset - 1); i = i + 2) { - //Skip, if the current bytes are checkSum bytes - if (i == checksumStartByte) + for (int i = start; i <= (end - 1); i = i + 2) { + // Skip, if the current bytes are checkSum bytes + if (i == checksumStart) { continue; + } StringBuffer sbuffer = new StringBuffer(); - sbuffer.append(String.format("%02X", hdrBytes[i])); - if (i < (hdrBytes.length - 1)) - sbuffer.append(String.format("%02X", hdrBytes[i + 1])); + sbuffer.append(String.format("%02X", data[i])); + if (i < (data.length - 1)) { + sbuffer.append(String.format("%02X", data[i + 1])); + } parsedHex = Integer.valueOf(sbuffer.toString(), 16); sum += parsedHex; @@ -470,17 +476,8 @@ public class IPv4 extends Packet { carry = (sum >> 16) & 0xFF; finalSum = (sum & 0xFFFF) + carry; checkSum = (short) ~((short) finalSum & 0xFFFF); - return checkSum; - } - - @Override - public int hashCode() { - return HashCodeBuilder.reflectionHashCode(this); - } - @Override - public boolean equals(Object obj) { - return EqualsBuilder.reflectionEquals(this, obj); + return checkSum; } @Override @@ -496,26 +493,34 @@ public class IPv4 extends Packet { byte[] options = getOptions(); return ((options == null) ? 0 : (options.length - getHeaderLen())); } - return (((Pair) hdrFieldCoordMap.get(fieldName)) - .getRight()); + return hdrFieldCoordMap.get(fieldName).getRight(); } @Override /** * Method to perform post serialization - like computation of checksum of serialized header - * @param serializedBytes + * @param data * @return void * @Exception throws PacketException */ - protected void postSerializeCustomOperation(byte[] serializedBytes) + protected void postSerializeCustomOperation(byte[] data) throws PacketException { - int startOffset = this.getfieldOffset(CHECKSUM); - int numBits = this.getfieldnumBits(CHECKSUM); - byte[] checkSum = BitBufferHelper.toByteArray(computeChecksum( - serializedBytes, serializedBytes.length)); + + // Recompute the total length field here + byte[] totalLength = BitBufferHelper.toByteArray((short) data.length); try { - BitBufferHelper.setBytes(serializedBytes, checkSum, startOffset, - numBits); + BitBufferHelper.setBytes(data, totalLength, getfieldOffset(TOTLENGTH), + getfieldnumBits(TOTLENGTH)); + } catch (BufferException e) { + throw new PacketException(e.getMessage()); + } + + // Now compute the Header Checksum + byte[] checkSum = BitBufferHelper.toByteArray(computeChecksum(data, 0)); + + try { + BitBufferHelper.setBytes(data, checkSum, getfieldOffset(CHECKSUM), + getfieldnumBits(CHECKSUM)); } catch (BufferException e) { throw new PacketException(e.getMessage()); } @@ -527,31 +532,35 @@ public class IPv4 extends Packet { * bytes in Total Length * @param payload - Packet */ + /** + * Set the total length field in the IPv4 Object + * Note: this field will get overwritten during serialization phase. + */ public void setPayload(Packet payload) { this.payload = payload; /* - * Deriving the Total Lenght here - * TODO: See if we can derive the total length during - * another phase (during serialization/deserialization) - * */ + * Deriving the Total Length here + */ int payloadLength = 0; try { payloadLength = payload.serialize().length; } catch (PacketException e) { logger.error("", e); } + this.setTotalLength((short) (this.getHeaderLen() + payloadLength)); } - @Override + /** * Method to perform post deserialization - like compare computed checksum with * the one obtained from IP header */ - protected void postDeserializeCustomOperation(byte[] data, int endBitOffset) { - int endByteOffset = endBitOffset / NetUtils.NumBitsInAByte; - int computedChecksum = computeChecksum(data, endByteOffset); - int actualChecksum = BitBufferHelper.getInt(fieldValues.get(CHECKSUM)); + @Override + protected void postDeserializeCustomOperation(byte[] data, int startBitOffset) { + int start = startBitOffset / NetUtils.NumBitsInAByte; + short computedChecksum = computeChecksum(data, start); + short actualChecksum = BitBufferHelper.getShort(fieldValues.get(CHECKSUM)); if (computedChecksum != actualChecksum) { corrupted = true; } diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/LLDPTLV.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/LLDPTLV.java index 3847e59a13..9254e824c5 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/LLDPTLV.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/LLDPTLV.java @@ -9,6 +9,7 @@ package org.opendaylight.controller.sal.packet; import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; import java.util.HashMap; import java.util.Arrays; @@ -31,7 +32,7 @@ public class LLDPTLV extends Packet { private static final String VALUE = "Value"; private static final int LLDPTLVFields = 3; public static final byte[] OFOUI = new byte[] { (byte) 0x00, (byte) 0x26, - (byte) 0xe1 }; // OpenFlow OUI + (byte) 0xe1 }; // OpenFlow OUI public static final byte[] customTlvSubType = new byte[] { 0 }; public static final int customTlvOffset = OFOUI.length + customTlvSubType.length; @@ -41,7 +42,7 @@ public class LLDPTLV extends Packet { public enum TLVType { Unknown((byte) 0), ChassisID((byte) 1), PortID((byte) 2), TTL((byte) 3), PortDesc( (byte) 4), SystemName((byte) 5), SystemDesc((byte) 6), Custom( - (byte) 127); + (byte) 127); private byte value; @@ -154,9 +155,9 @@ public class LLDPTLV extends Packet { @Override public int getfieldnumBits(String fieldName) { if (fieldName.equals(VALUE)) { - return (NetUtils.NumBitsInAByte * (int) BitBufferHelper.getShort( + return (NetUtils.NumBitsInAByte * BitBufferHelper.getShort( fieldValues.get(LENGTH), fieldCoordinates.get(LENGTH) - .getRight().intValue())); + .getRight().intValue())); } return fieldCoordinates.get(fieldName).getRight(); } @@ -169,7 +170,7 @@ public class LLDPTLV extends Packet { public int getTLVSize() { return (LLDPTLV.fieldCoordinates.get(TYPE).getRight() + // static LLDPTLV.fieldCoordinates.get(LENGTH).getRight() + // static - getfieldnumBits(VALUE)); // variable + getfieldnumBits(VALUE)); // variable } /** @@ -209,7 +210,7 @@ public class LLDPTLV extends Packet { * @return the PortID TLV value in byte array */ static public byte[] createPortIDTLVValue(String portId) { - byte[] pid = portId.getBytes(); + byte[] pid = portId.getBytes(Charset.defaultCharset()); byte[] pidValue = new byte[pid.length + portIDSubType.length]; System.arraycopy(portIDSubType, 0, pidValue, 0, portIDSubType.length); @@ -226,7 +227,7 @@ public class LLDPTLV extends Packet { * @return the custom TLV value in byte array */ static public byte[] createCustomTLVValue(String customString) { - byte[] customArray = customString.getBytes(); + byte[] customArray = customString.getBytes(Charset.defaultCharset()); byte[] customValue = new byte[customTlvOffset + customArray.length]; System.arraycopy(OFOUI, 0, customValue, 0, OFOUI.length); @@ -267,7 +268,7 @@ public class LLDPTLV extends Packet { byte[] pidBytes = new byte[tlvLen - portIDSubType.length]; System.arraycopy(tlvValue, portIDSubType.length, pidBytes, 0, pidBytes.length); - return (new String(pidBytes)); + return (new String(pidBytes, Charset.defaultCharset())); } /** diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/Packet.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/Packet.java index 446ec3e230..b19c0f862b 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/Packet.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/Packet.java @@ -8,8 +8,7 @@ package org.opendaylight.controller.sal.packet; -import java.net.InetAddress; -import java.net.UnknownHostException; +import java.util.Arrays; import java.util.Map; import java.util.Map.Entry; @@ -23,8 +22,6 @@ import org.slf4j.LoggerFactory; * Abstract class which represents the generic network packet object It provides * the basic methods which are common for all the packets, like serialize and * deserialize - * - * */ public abstract class Packet { @@ -38,6 +35,8 @@ public abstract class Packet { protected Packet parent; // The packet encapsulated by this packet protected Packet payload; + // The unparsed raw payload carried by this packet + protected byte[] rawPayload; // Bit coordinates of packet header fields protected Map> hdrFieldCoordMap; // Header fields values: Map @@ -52,7 +51,7 @@ public abstract class Packet { public Packet(boolean writeAccess) { this.writeAccess = writeAccess; - this.corrupted = false; + corrupted = false; } public Packet getParent() { @@ -86,43 +85,41 @@ public abstract class Packet { * @return Packet * @throws PacketException */ - public Packet deserialize(byte[] data, int bitOffset, int size) throws PacketException { - String hdrField; - Integer startOffset = 0, numBits = 0; - byte[] hdrFieldBytes; + // Deserialize the header fields one by one + int startOffset = 0, numBits = 0; for (Entry> pairs : hdrFieldCoordMap .entrySet()) { - hdrField = pairs.getKey(); + String hdrField = pairs.getKey(); startOffset = bitOffset + this.getfieldOffset(hdrField); numBits = this.getfieldnumBits(hdrField); + byte[] hdrFieldBytes = null; try { hdrFieldBytes = BitBufferHelper.getBits(data, startOffset, numBits); } catch (BufferException e) { throw new PacketException(e.getMessage()); } + /* * Store the raw read value, checks the payload type and set the * payloadClass accordingly */ + this.setHeaderField(hdrField, hdrFieldBytes); + if (logger.isTraceEnabled()) { - logger.trace("{}: {}: {} (offset {} bitsize {})", - new Object[] { this.getClass().getSimpleName(), hdrField, - HexEncode.bytesToHexString(hdrFieldBytes), - startOffset, numBits }); + logger.trace("{}: {}: {} (offset {} bitsize {})", + new Object[] { this.getClass().getSimpleName(), hdrField, + HexEncode.bytesToHexString(hdrFieldBytes), + startOffset, numBits }); } - - this.setHeaderField(hdrField, hdrFieldBytes); } - postDeserializeCustomOperation(data, startOffset); - + // Deserialize the payload now int payloadStart = startOffset + numBits; - // int payloadSize = size - payloadStart; int payloadSize = data.length * NetUtils.NumBitsInAByte - payloadStart; if (payloadClass != null) { @@ -135,67 +132,72 @@ public abstract class Packet { payload.deserialize(data, payloadStart, payloadSize); payload.setParent(this); } else { - // For now let's discard unparsable payload + /* + * The payload class was not set, it means no class for parsing + * this payload is present. Let's store the raw payload if any. + */ + int start = payloadStart / NetUtils.NumBitsInAByte; + int stop = start + payloadSize / NetUtils.NumBitsInAByte; + rawPayload = Arrays.copyOfRange(data, start, stop); } + + + // Take care of computation that can be done only after deserialization + postDeserializeCustomOperation(data, payloadStart - getHeaderSize()); + return this; } /** - * This method serializes the header and payload bytes from the respective + * This method serializes the header and payload from the respective * packet class, into a single stream of bytes to be sent on the wire * - * @return byte[] - serialized bytes + * @return The byte array representing the serialized Packet * @throws PacketException */ - public byte[] serialize() throws PacketException { - byte[] payloadBytes = null; - int payloadSize = 0; - int headerSize = this.getHeaderSize(); - int payloadByteOffset = headerSize / NetUtils.NumBitsInAByte; - int size = 0; + // Acquire or compute the serialized payload + byte[] payloadBytes = null; if (payload != null) { payloadBytes = payload.serialize(); - payloadSize = payloadBytes.length * NetUtils.NumBitsInAByte; + } else if (rawPayload != null) { + payloadBytes = rawPayload; } + int payloadSize = (payloadBytes == null) ? 0 : payloadBytes.length; - size = headerSize + payloadSize; - int length = size / NetUtils.NumBitsInAByte; - byte headerBytes[] = new byte[length]; - - if (payload != null) { - System.arraycopy(payloadBytes, 0, headerBytes, payloadByteOffset, - payloadBytes.length); + // Allocate the buffer to contain the full (header + payload) packet + int headerSize = this.getHeaderSize() / NetUtils.NumBitsInAByte; + byte packetBytes[] = new byte[headerSize + payloadSize]; + if (payloadBytes != null) { + System.arraycopy(payloadBytes, 0, packetBytes, headerSize, payloadSize); } - String field; - byte[] fieldBytes; - Integer startOffset, numBits; - + // Serialize this packet header, field by field for (Map.Entry> pairs : hdrFieldCoordMap .entrySet()) { - field = pairs.getKey(); - fieldBytes = hdrFieldsMap.get(field); + String field = pairs.getKey(); + byte[] fieldBytes = hdrFieldsMap.get(field); // Let's skip optional fields when not set if (fieldBytes != null) { - startOffset = this.getfieldOffset(field); - numBits = this.getfieldnumBits(field); try { - BitBufferHelper.setBytes(headerBytes, fieldBytes, - startOffset, numBits); + BitBufferHelper.setBytes(packetBytes, fieldBytes, + getfieldOffset(field), getfieldnumBits(field)); } catch (BufferException e) { throw new PacketException(e.getMessage()); } } } - postSerializeCustomOperation(headerBytes); + + // Perform post serialize operations (like checksum computation) + postSerializeCustomOperation(packetBytes); if (logger.isTraceEnabled()) { - logger.trace("{}: {}", this.getClass().getSimpleName(), - HexEncode.bytesToHexString(headerBytes)); + logger.trace("{}: {}", this.getClass().getSimpleName(), + HexEncode.bytesToHexString(packetBytes)); } - return headerBytes; + + return packetBytes; } /** @@ -216,15 +218,15 @@ public abstract class Packet { /** * This method re-computes the checksum of the bits received on the wire and * validates it with the checksum in the bits received Since the computation - * of checksum varies based on the protocol, this method is overridden - * Currently only IPv4 does checksum computation and validation TCP and UDP - * need to implement these if required + * of checksum varies based on the protocol, this method is overridden. + * Currently only IPv4 and ICMP do checksum computation and validation. TCP + * and UDP need to implement these if required. * - * @param byte[] data - * @param int endBitOffset + * @param byte[] data The byte stream representing the Ethernet frame + * @param int startBitOffset The bit offset from where the byte array corresponding to this Packet starts in the frame * @throws PacketException */ - protected void postDeserializeCustomOperation(byte[] data, int endBitOffset) + protected void postDeserializeCustomOperation(byte[] data, int startBitOffset) throws PacketException { // no op } @@ -260,8 +262,7 @@ public abstract class Packet { * @return Integer - startOffset of the requested field */ public int getfieldOffset(String fieldName) { - return (((Pair) hdrFieldCoordMap.get(fieldName)) - .getLeft()); + return hdrFieldCoordMap.get(fieldName).getLeft(); } /** @@ -274,38 +275,93 @@ public abstract class Packet { * @return Integer - number of bits of the requested field */ public int getfieldnumBits(String fieldName) { - return (((Pair) hdrFieldCoordMap.get(fieldName)) - .getRight()); + return hdrFieldCoordMap.get(fieldName).getRight(); } @Override public String toString() { - StringBuffer ret = new StringBuffer(); - for (Map.Entry entry : hdrFieldsMap.entrySet()) { - ret.append(entry.getKey() + ": "); - if (entry.getValue().length == 6) { - ret.append(HexEncode.bytesToHexString(entry.getValue()) + " "); - } else if (entry.getValue().length == 4) { - try { - ret.append(InetAddress.getByAddress(entry.getValue()) - .getHostAddress() + " "); - } catch (UnknownHostException e) { - logger.error("", e); - } - } else { - ret.append(((Long) BitBufferHelper.getLong(entry.getValue())) - .toString() + " "); - } + StringBuilder ret = new StringBuilder(); + ret.append(this.getClass().getSimpleName()); + ret.append(": ["); + for (String field : hdrFieldCoordMap.keySet()) { + byte[] value = hdrFieldsMap.get(field); + ret.append(field); + ret.append(": "); + ret.append(HexEncode.bytesToHexString(value)); + ret.append(", "); } + ret.replace(ret.length()-2, ret.length()-1, "]"); return ret.toString(); } /** - * Returns true if the packet is corrupted + * Returns the raw payload carried by this packet in case payload was not + * parsed. Caller can call this function in case the getPaylod() returns null. * - * @return boolean + * @return The raw payload if not parsable as an array of bytes, null otherwise */ - protected boolean isPacketCorrupted() { + public byte[] getRawPayload() { + return rawPayload; + } + + /** + * Set a raw payload in the packet class + * + * @param payload The raw payload as byte array + */ + public void setRawPayload(byte[] payload) { + this.rawPayload = Arrays.copyOf(payload, payload.length); + } + + /** + * Return whether the deserialized packet is to be considered corrupted. + * This is the case when the checksum computed after reconstructing the + * packet received from wire is not equal to the checksum read from the + * stream. For the Packet class which do not have a checksum field, this + * function will always return false. + * + * + * @return true if the deserialized packet's recomputed checksum is not + * equal to the packet carried checksum + */ + public boolean isCorrupted() { return corrupted; } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + + ((this.hdrFieldsMap == null) ? 0 : hdrFieldsMap.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (getClass() != obj.getClass()) { + return false; + } + Packet other = (Packet) obj; + if (hdrFieldsMap == other.hdrFieldsMap) { + return true; + } + if (hdrFieldsMap == null || other.hdrFieldsMap == null) { + return false; + } + if (hdrFieldsMap != null && other.hdrFieldsMap != null) { + for (String field : hdrFieldsMap.keySet()) { + if (!Arrays.equals(hdrFieldsMap.get(field), other.hdrFieldsMap.get(field))) { + return false; + } + } + } else { + return false; + } + return true; + } + } diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/RawPacket.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/RawPacket.java index d518902183..a7956d81b3 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/RawPacket.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/RawPacket.java @@ -31,10 +31,10 @@ import org.opendaylight.controller.sal.core.TimeStamp; */ public class RawPacket { private byte[] packetData; - private LinkEncap encap; - private TimeStamp incomingTime; - private TimeStamp copyTime; - private Map props; + private final LinkEncap encap; + private final TimeStamp incomingTime; + private final TimeStamp copyTime; + private Map props; private NodeConnector incomingNodeConnector; private NodeConnector outgoingNodeConnector; @@ -103,7 +103,7 @@ public class RawPacket { */ public void setProps(Object key, Object value) { if (this.props == null) { - this.props = new HashMap(); + this.props = new HashMap(); } this.props.put(key, value); @@ -157,7 +157,8 @@ public class RawPacket { this.incomingTime = src.getIncomingTime(); this.incomingNodeConnector = src.getIncomingNodeConnector(); this.outgoingNodeConnector = src.getOutgoingNodeConnector(); - this.props = (src.props == null ? null : new HashMap(src.props)); + this.props = (src.props == null ? null : new HashMap( + src.props)); this.copyTime = new TimeStamp(System.currentTimeMillis(), "CopyTime"); } @@ -173,9 +174,9 @@ public class RawPacket { } /** - * Read the timestamp when the packet has entered the system + * Read the time stamp when the packet has entered the system * - * @return The timestamp when the packet has entered the system + * @return The time stamp when the packet has entered the system */ public TimeStamp getIncomingTime() { return this.incomingTime; @@ -199,4 +200,15 @@ public class RawPacket { public byte[] getPacketData() { return this.packetData; } + + /** + * Returns the time at which the current instance of RawPacket was created + * as a copy of the original one. + * + * @return The time stamp at which this RawPacket instance was created. null + * if this is the original instance. + */ + public TimeStamp getCopyTime() { + return this.copyTime; + } } diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/TCP.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/TCP.java index ca4a871683..c7c7f99279 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/TCP.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/TCP.java @@ -12,15 +12,12 @@ package org.opendaylight.controller.sal.packet; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; + import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; /** * Class that represents the TCP segment objects - * - * */ public class TCP extends Packet { @@ -51,7 +48,7 @@ public class TCP extends Packet { } }; - private Map fieldValues; + private final Map fieldValues; /** * Default constructor that sets all the header fields to zero @@ -233,14 +230,4 @@ public class TCP extends Packet { return (BitBufferHelper.getShort(fieldValues.get(DESTPORT))); } - @Override - public int hashCode() { - return HashCodeBuilder.reflectionHashCode(this); - } - - @Override - public boolean equals(Object obj) { - return EqualsBuilder.reflectionEquals(this, obj); - } - } diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/UDP.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/UDP.java index 52827d55c6..fba3f5c917 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/UDP.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/UDP.java @@ -13,15 +13,11 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; /** * Class that represents the UDP datagram objects - * - * */ public class UDP extends Packet { @@ -67,7 +63,7 @@ public class UDP extends Packet { setChecksum((short) 0); } - private Map fieldValues; + private final Map fieldValues; /* public static Map> decodeMap; @@ -163,14 +159,4 @@ public class UDP extends Packet { fieldValues.put(CHECKSUM, checksum); return this; } - - @Override - public int hashCode() { - return HashCodeBuilder.reflectionHashCode(this); - } - - @Override - public boolean equals(Object obj) { - return EqualsBuilder.reflectionEquals(this, obj); - } } diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/HexEncode.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/HexEncode.java index 557b848b73..41b4b7a57d 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/HexEncode.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/HexEncode.java @@ -17,21 +17,31 @@ import java.math.BigInteger; * */ public class HexEncode { - /** - * This method converts byte array into String format without ":" inserted. - */ + /** + * This method converts byte array into String format without ":" inserted. + * + * @param bytes + * The byte array to convert to string + * @return The hexadecimal representation of the byte array. If bytes is + * null, "null" string is returned + */ public static String bytesToHexString(byte[] bytes) { - int i; + + if (bytes == null) { + return "null"; + } + String ret = ""; - String tmp; StringBuffer buf = new StringBuffer(); - for (i = 0; i < bytes.length; i++) { - if (i > 0) + for (int i = 0; i < bytes.length; i++) { + if (i > 0) { ret += ":"; - short u8byte = (short) ((short) bytes[i] & 0xff); - tmp = Integer.toHexString(u8byte); - if (tmp.length() == 1) + } + short u8byte = (short) (bytes[i] & 0xff); + String tmp = Integer.toHexString(u8byte); + if (tmp.length() == 1) { buf.append("0"); + } buf.append(tmp); } ret = buf.toString(); @@ -45,24 +55,31 @@ public class HexEncode { int i = 0; for (; i < (16 - arr.length); i++) { buf.append("0"); - if ((i & 0x01) == 1) + if ((i & 0x01) == 1) { buf.append(":"); + } } for (int j = 0; j < arr.length; j++) { buf.append(arr[j]); - if ((((i + j) & 0x01) == 1) && (j < (arr.length - 1))) + if ((((i + j) & 0x01) == 1) && (j < (arr.length - 1))) { buf.append(":"); + } } return buf.toString(); } + public static byte[] bytesFromHexString(String values) { - String[] octets = values.split(":"); - byte[] ret = new byte[octets.length]; - int i; + String target = ""; + if (values != null) { + target = values; + } + String[] octets = target.split(":"); - for (i = 0; i < octets.length; i++) + byte[] ret = new byte[octets.length]; + for (int i = 0; i < octets.length; i++) { ret[i] = Integer.valueOf(octets[i], 16).byteValue(); + } return ret; } @@ -71,21 +88,24 @@ public class HexEncode { return value; } - /** - * This method converts byte array into HexString format with ":" inserted. - */ + /** + * This method converts byte array into HexString format with ":" inserted. + */ public static String bytesToHexStringFormat(byte[] bytes) { - int i; + if (bytes == null) { + return "null"; + } String ret = ""; - String tmp; StringBuffer buf = new StringBuffer(); - for (i = 0; i < bytes.length; i++) { - if (i > 0) + for (int i = 0; i < bytes.length; i++) { + if (i > 0) { buf.append(":"); - short u8byte = (short) ((short) bytes[i] & 0xff); - tmp = Integer.toHexString(u8byte); - if (tmp.length() == 1) + } + short u8byte = (short) (bytes[i] & 0xff); + String tmp = Integer.toHexString(u8byte); + if (tmp.length() == 1) { buf.append("0"); + } buf.append(tmp); } ret = buf.toString(); diff --git a/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/packet/ICMPTest.java b/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/packet/ICMPTest.java index 445f7211c0..e81fbf02cf 100644 --- a/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/packet/ICMPTest.java +++ b/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/packet/ICMPTest.java @@ -12,18 +12,16 @@ package org.opendaylight.controller.sal.packet; import junit.framework.Assert; import org.junit.Test; -import org.opendaylight.controller.sal.packet.ICMP; public class ICMPTest { @Test public void testSetTypeCode() { ICMP icmp = new ICMP(); - short icmpTypeCode = 2; - icmp.setTypeCode(icmpTypeCode); - byte[] typeCode = icmp.hdrFieldsMap.get("TypeCode"); - Assert.assertTrue(typeCode[0] == 0); - Assert.assertTrue(typeCode[1] == 2); + byte icmpType = 2; + icmp.setType(icmpType); + byte[] typeCode = icmp.hdrFieldsMap.get("Type"); + Assert.assertTrue(typeCode[0] == 2); } @@ -32,7 +30,7 @@ public class ICMPTest { ICMP icmp = new ICMP(); short icmpChecksum = 200; icmp.setChecksum(icmpChecksum); - byte[] checksum = icmp.hdrFieldsMap.get("HeaderChecksum"); + byte[] checksum = icmp.hdrFieldsMap.get("Checksum"); Assert.assertTrue(checksum[0] == 0); Assert.assertTrue(checksum[1] == -56); @@ -59,4 +57,45 @@ public class ICMPTest { Assert.assertTrue(sequenceNumber[1] == -120); } + + @Test + public void testSerialization() throws PacketException { + byte icmpRawPayload[] = { (byte) 0x38, (byte) 0x26, (byte) 0x9e, + (byte) 0x51, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x2e, (byte) 0x6a, (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x10, (byte) 0x11, (byte) 0x12, + (byte) 0x13, (byte) 0x14, (byte) 0x15, (byte) 0x16, + (byte) 0x17, (byte) 0x18, (byte) 0x19, (byte) 0x1a, + (byte) 0x1b, (byte) 0x1c, (byte) 0x1d, (byte) 0x1e, + (byte) 0x1f, (byte) 0x20, (byte) 0x21, (byte) 0x22, + (byte) 0x23, (byte) 0x24, (byte) 0x25, (byte) 0x26, + (byte) 0x27, (byte) 0x28, (byte) 0x29, (byte) 0x2a, + (byte) 0x2b, (byte) 0x2c, (byte) 0x2d, (byte) 0x2e, + (byte) 0x2f, (byte) 0x30, (byte) 0x31, (byte) 0x32, + (byte) 0x33, (byte) 0x34, (byte) 0x35, (byte) 0x36, (byte) 0x37 }; + + short checksum = (short)0xe553; + + // Create ICMP object + ICMP icmp = new ICMP(); + icmp.setType((byte)8); + icmp.setCode((byte)0); + icmp.setIdentifier((short) 0x46f5); + icmp.setSequenceNumber((short) 2); + icmp.setRawPayload(icmpRawPayload); + //icmp.setChecksum(checksum); + + // Serialize + byte[] stream = icmp.serialize(); + Assert.assertTrue(stream.length == 64); + + // Deserialize + ICMP icmpDes = new ICMP(); + icmpDes.deserialize(stream, 0, stream.length); + + Assert.assertFalse(icmpDes.isCorrupted()); + Assert.assertTrue(icmpDes.getChecksum() == checksum); + Assert.assertTrue(icmp.equals(icmpDes)); + } } diff --git a/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/packet/IPv4Test.java b/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/packet/IPv4Test.java index 43118ec0dd..429272f227 100644 --- a/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/packet/IPv4Test.java +++ b/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/packet/IPv4Test.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * @@ -9,12 +8,16 @@ package org.opendaylight.controller.sal.packet; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; + import junit.framework.Assert; import org.junit.Test; -import org.opendaylight.controller.sal.packet.ICMP; -import org.opendaylight.controller.sal.packet.IPv4; -import org.opendaylight.controller.sal.packet.Packet; +import org.opendaylight.controller.sal.utils.EtherTypes; +import org.opendaylight.controller.sal.utils.IPProtocols; +import org.opendaylight.controller.sal.utils.NetUtils; public class IPv4Test { @@ -51,7 +54,6 @@ public class IPv4Test { byte[] iptotLength = { 3, -24 }; ip.hdrFieldsMap.put("TotalLength", iptotLength); short totalLength = ip.getTotalLength(); - //System.out.println(totalLength); Assert.assertTrue(totalLength == 1000); } @@ -91,7 +93,6 @@ public class IPv4Test { Assert.assertTrue(protocol == 1); Class clazz = IPv4.protocolClassMap.get(protocol); - System.out.printf("clazz = %s\n", clazz.getName()); Assert.assertTrue(clazz == ICMP.class); } @@ -157,6 +158,11 @@ public class IPv4Test { byte[] totalLength = ip.hdrFieldsMap.get("TotalLength"); Assert.assertTrue(totalLength[0] == 3); Assert.assertTrue(totalLength[1] == -24); + + ip.setTotalLength((short)84); + totalLength = ip.hdrFieldsMap.get("TotalLength"); + Assert.assertTrue(totalLength[0] == 0); + Assert.assertTrue(totalLength[1] == 84); } @Test @@ -206,7 +212,6 @@ public class IPv4Test { Assert.assertTrue(fragmentOffset[1] == -35); } - @Test public void testSetDestinationAddress() { IPv4 ip = new IPv4(); @@ -219,4 +224,112 @@ public class IPv4Test { Assert.assertTrue(destinationAddress[3] == 110); } + @Test + public void testChecksum() { + byte header[] = { (byte) 0x45, 00, 00, (byte) 0x3c, (byte) 0x1c, + (byte) 0x46, (byte) 0x40, 00, (byte) 0x40, 06, (byte) 0xb1, + (byte) 0xe6, (byte) 0xac, (byte) 0x10, (byte) 0x0a, + (byte) 0x63, (byte) 0xac, (byte) 0x10, (byte) 0x0a, (byte) 0x0c }; + byte header2[] = { (byte) 0x45, 00, 00, (byte) 0x73, 00, 00, + (byte) 0x40, 00, (byte) 0x40, (byte) 0x11, (byte) 0xb8, + (byte) 0x61, (byte) 0xc0, (byte) 0xa8, 00, 01, (byte) 0xc0, + (byte) 0xa8, 00, (byte) 0xc7 }; + byte header3[] = { (byte) 0x45, 00, 00, (byte) 0x47, (byte) 0x73, + (byte) 0x88, (byte) 0x40, 00, (byte) 0x40, 06, (byte) 0xA2, + (byte) 0xC4, (byte) 0x83, (byte) 0x9F, (byte) 0x0E, + (byte) 0x85, (byte) 0x83, (byte) 0x9F, (byte) 0x0E, (byte) 0xA1 }; + byte header4[] = { (byte) 0x45, 00, 00, (byte) 0x54, 00, 00, + (byte) 0x40, 00, (byte) 0x40, 01, (byte) 0xf0, (byte) 0x8e, + (byte) 0xc0, (byte) 0xa8, (byte) 0x64, (byte) 0x65, + (byte) 0xc0, (byte) 0xa8, (byte) 0x64, (byte) 0x64 }; + byte header5[] = { (byte) 0x45, 00, 00, (byte) 0x54, 00, 00, + (byte) 0x40, 00, (byte) 0x40, 01, (byte) 0xef, (byte) 0x8d, + (byte) 0xc0, (byte) 0xa8, (byte) 0x64, (byte) 0x65, + (byte) 0xc0, (byte) 0xa8, (byte) 0x65, (byte) 0x65 }; + byte header6[] = { (byte) 0x45, 00, 00, (byte) 0x54, 00, 00, + (byte) 0x40, 00, (byte) 0x40, 01, (byte) 0x0b, (byte) 0x92, + (byte) 0xc0, (byte) 0xa8, (byte) 0x64, (byte) 0x65, (byte) 0x9, + (byte) 0x9, (byte) 0x1, (byte) 0x1 }; + byte header7[] = { (byte) 0x45, 00, 00, (byte) 0x54, 00, 00, + (byte) 0x40, 00, (byte) 0x40, 01, (byte) 0, (byte) 0, + (byte) 0xc0, (byte) 0xa8, (byte) 0x64, (byte) 0x65, (byte) 0x9, + (byte) 0x9, (byte) 0x2, (byte) 0x2 }; + + IPv4 ip = new IPv4(); + + Assert.assertTrue(NetUtils.getUnsignedShort(ip.computeChecksum(header, + 0)) == 0xB1E6); + Assert.assertTrue(NetUtils.getUnsignedShort(ip.computeChecksum(header2, + 0)) == 0xb861); + Assert.assertTrue(NetUtils.getUnsignedShort(ip.computeChecksum(header3, + 0)) == 0xa2c4); + Assert.assertTrue(NetUtils.getUnsignedShort(ip.computeChecksum(header4, + 0)) == 0xf08e); + Assert.assertTrue(NetUtils.getUnsignedShort(ip.computeChecksum(header5, + 0)) == 0xef8d); + Assert.assertTrue(NetUtils.getUnsignedShort(ip.computeChecksum(header6, + 0)) == 0x0b92); + Assert.assertTrue(NetUtils.getUnsignedShort(ip.computeChecksum(header7, + 0)) == 0x0a91); + } + + @Test + public void testFullIP() throws UnknownHostException, PacketException { + byte[] icmpRawPayload = new byte[] { (byte) 0x38, (byte) 0x26, + (byte) 0x9e, (byte) 0x51, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x2e, (byte) 0x6a, + (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x11, + (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15, + (byte) 0x16, (byte) 0x17, (byte) 0x18, (byte) 0x19, + (byte) 0x1a, (byte) 0x1b, (byte) 0x1c, (byte) 0x1d, + (byte) 0x1e, (byte) 0x1f, (byte) 0x20, (byte) 0x21, + (byte) 0x22, (byte) 0x23, (byte) 0x24, (byte) 0x25, + (byte) 0x26, (byte) 0x27, (byte) 0x28, (byte) 0x29, + (byte) 0x2a, (byte) 0x2b, (byte) 0x2c, (byte) 0x2d, + (byte) 0x2e, (byte) 0x2f, (byte) 0x30, (byte) 0x31, + (byte) 0x32, (byte) 0x33, (byte) 0x34, (byte) 0x35, + (byte) 0x36, (byte) 0x37 }; + ICMP icmp = new ICMP(); + icmp.setType((byte) 8); + icmp.setCode((byte) 0); + icmp.setIdentifier((short) 0x46f5); + icmp.setSequenceNumber((short) 2); + icmp.setRawPayload(icmpRawPayload); + + IPv4 ip = new IPv4(); + ip.setVersion((byte) 4); + ip.setIdentification((short) 5); + ip.setDiffServ((byte) 0); + ip.setECN((byte) 0); + ip.setTotalLength((short) 84); + ip.setFlags((byte) 2); + ip.setFragmentOffset((short) 0); + ip.setTtl((byte) 64); + ip.setProtocol(IPProtocols.ICMP.byteValue()); + ip.setDestinationAddress(InetAddress.getByName("192.168.100.100")); + ip.setSourceAddress(InetAddress.getByName("192.168.100.101")); + ip.setPayload(icmp); + + Ethernet eth = new Ethernet(); + eth.setDestinationMACAddress(new byte[] { (byte) 0x98, (byte) 0xfc, + (byte) 0x11, (byte) 0x93, (byte) 0x5c, (byte) 0xb8 }); + eth.setSourceMACAddress(new byte[] { (byte) 0x00, (byte) 0x24, + (byte) 0xd7, (byte) 0xa9, (byte) 0xa3, (byte) 0x50 }); + eth.setEtherType(EtherTypes.IPv4.shortValue()); + eth.setPayload(ip); + + byte[] stream = eth.serialize(); + + Ethernet decEth = new Ethernet(); + decEth.deserialize(stream, 0, stream.length * NetUtils.NumBitsInAByte); + + IPv4 decIp = (IPv4) decEth.getPayload(); + Assert.assertFalse(decIp.isCorrupted()); + Assert.assertTrue(ip.equals(decIp)); + + ICMP decIcmp = (ICMP) decIp.getPayload(); + Assert.assertFalse(decIcmp.isCorrupted()); + Assert.assertTrue(Arrays.equals(icmpRawPayload, decIcmp.getRawPayload())); + } } -- 2.36.6