ICMP fix and Packet class should store and provide access to the raw payload in case... 88/388/3
authorAlessandro Boch <aboch@cisco.com>
Thu, 23 May 2013 01:01:41 +0000 (18:01 -0700)
committerAlessandro Boch <aboch@cisco.com>
Fri, 24 May 2013 02:29:28 +0000 (19:29 -0700)
- 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 <aboch@cisco.com>
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/Ethernet.java
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/ICMP.java
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/IPv4.java
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/LLDPTLV.java
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/Packet.java
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/RawPacket.java
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/TCP.java
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/UDP.java
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/HexEncode.java
opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/packet/ICMPTest.java
opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/packet/IPv4Test.java

index 0f1a7f5..c96e901 100644 (file)
@@ -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<Integer, Integer>(96, 16));
         }
     };
-    private Map<String, byte[]> fieldValues;
+    private final Map<String, byte[]> 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);
-    }
-
 }
index d1f81c5..0429c0d 100644 (file)
@@ -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<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
         private static final long serialVersionUID = 1L;
-
         {
-            put(TYPECODE, new ImmutablePair<Integer, Integer>(0, 8));
+            put(TYPE, new ImmutablePair<Integer, Integer>(0, 8));
             put(CODE, new ImmutablePair<Integer, Integer>(8, 8));
-            put(HEADERCHECKSUM, new ImmutablePair<Integer, Integer>(16, 16));
+            put(CHECKSUM, new ImmutablePair<Integer, Integer>(16, 16));
             put(IDENTIFIER, new ImmutablePair<Integer, Integer>(32, 16));
             put(SEQNUMBER, new ImmutablePair<Integer, Integer>(48, 16));
-
         }
     };
 
@@ -64,7 +59,7 @@ public class ICMP extends Packet {
         hdrFieldsMap = fieldValues;
     }
 
-    private Map<String, byte[]> fieldValues;
+    private final Map<String, byte[]> 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)));
     }
 }
index d547e2c..c7b97e9 100644 (file)
@@ -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<String, byte[]> fieldValues;
+    private final Map<String, byte[]> fieldValues;
+
 
     /**
      * Default constructor that sets the version to 4, headerLength to 5,
@@ -90,14 +87,15 @@ public class IPv4 extends Packet {
         fieldValues = new HashMap<String, byte[]>();
         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<String, byte[]>();
         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<Integer, Integer>) 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;
         }
index 3847e59..9254e82 100644 (file)
@@ -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()));
     }
 
     /**
index 446ec3e..b19c0f8 100644 (file)
@@ -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<String, Pair<Integer, Integer>> hdrFieldCoordMap;
     // Header fields values: Map<FieldName,Value>
@@ -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<String, Pair<Integer, Integer>> 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<String, Pair<Integer, Integer>> 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<Integer, Integer>) 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<Integer, Integer>) hdrFieldCoordMap.get(fieldName))
-                .getRight());
+        return hdrFieldCoordMap.get(fieldName).getRight();
     }
 
     @Override
     public String toString() {
-        StringBuffer ret = new StringBuffer();
-        for (Map.Entry<String, byte[]> 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;
+    }
+
 }
index d518902..a7956d8 100644 (file)
@@ -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<Object, Object> 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<Object, Object>();
         }
 
         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<Object, Object>(
+                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;
+    }
 }
index ca4a871..c7c7f99 100644 (file)
@@ -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<String, byte[]> fieldValues;
+    private final Map<String, byte[]> 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);
-    }
-
 }
index 52827d5..fba3f5c 100644 (file)
@@ -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<String, byte[]> fieldValues;
+    private final Map<String, byte[]> fieldValues;
 
     /* public static Map<Short, Class<? extends Packet>> 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);
-    }
 }
index 557b848..41b4b7a 100644 (file)
@@ -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();
index 445f721..e81fbf0 100644 (file)
@@ -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));
+    }
 }
index 43118ec..429272f 100644 (file)
@@ -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<? extends Packet> 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()));
+    }
 }