Merge "Re-added config.version to config-module-archetype."
[controller.git] / opendaylight / sal / api / src / main / java / org / opendaylight / controller / sal / packet / IPv4.java
index d547e2c905ecae0d5397a053d9c784a3f64a2aea..3363f423d695f1b2f090e6c5274715f47d765e3c 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ * Copyright (c) 2013-2014 Cisco Systems, Inc. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -18,10 +18,10 @@ 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.match.Match;
+import org.opendaylight.controller.sal.match.MatchType;
 import org.opendaylight.controller.sal.utils.IPProtocols;
 import org.opendaylight.controller.sal.utils.NetUtils;
 import org.slf4j.Logger;
@@ -29,13 +29,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";
@@ -51,6 +49,10 @@ public class IPv4 extends Packet {
     private static final String DIP = "DestinationIPAddress";
     private static final String OPTIONS = "Options";
 
+    private static final int UNIT_SIZE_SHIFT = 2;
+    private static final int UNIT_SIZE = (1 << UNIT_SIZE_SHIFT);
+    private static final int MIN_HEADER_SIZE = 20;
+
     public static final Map<Byte, Class<? extends Packet>> protocolClassMap;
     static {
         protocolClassMap = new HashMap<Byte, Class<? extends Packet>>();
@@ -78,7 +80,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 +93,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 +116,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 +144,17 @@ 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)
-            headerLen = 20;
-
-        byte[] options = hdrFieldsMap.get(OPTIONS);
-        if (options != null)
-            headerLen += options.length;
+        if (headerLen == 0) {
+            headerLen = MIN_HEADER_SIZE;
+        }
 
         return headerLen * NetUtils.NumBitsInAByte;
-
     }
 
     /**
@@ -260,6 +261,10 @@ public class IPv4 extends Packet {
     public void setHeaderField(String headerField, byte[] readValue) {
         if (headerField.equals(PROTOCOL)) {
             payloadClass = protocolClassMap.get(readValue[0]);
+        } else if (headerField.equals(OPTIONS) &&
+                   (readValue == null || readValue.length == 0)) {
+            hdrFieldsMap.remove(headerField);
+            return;
         }
         hdrFieldsMap.put(headerField, readValue);
     }
@@ -276,7 +281,7 @@ public class IPv4 extends Packet {
     }
 
     /**
-     * Stores the length of IP header in words (2 bytes)
+     * Stores the length of IP header in words (4 bytes)
      * @param headerLength the headerLength to set
      * @return IPv4
      */
@@ -378,9 +383,9 @@ public class IPv4 extends Packet {
      * @param checksum the checksum to set
      */
     /*public IPv4 setChecksum() {
-       short ipChecksum = computeChecksum();
+        short ipChecksum = computeChecksum();
         byte[] checksum = BitBufferHelper.toByteArray(ipChecksum);
-       fieldValues.put(CHECKSUM, checksum);
+        fieldValues.put(CHECKSUM, checksum);
         return this;
     }*/
 
@@ -434,88 +439,100 @@ public class IPv4 extends Packet {
      * @return IPv4
      */
     public IPv4 setOptions(byte[] options) {
-        fieldValues.put(OPTIONS, options);
-        byte newIHL = (byte) (5 + options.length);
+        byte newIHL = (byte)(MIN_HEADER_SIZE >>> UNIT_SIZE_SHIFT);
+        if (options == null || options.length == 0) {
+            fieldValues.remove(OPTIONS);
+        } else {
+            int len = options.length;
+            int rlen = (len + (UNIT_SIZE - 1)) & ~(UNIT_SIZE - 1);
+            if (rlen > len) {
+                // Padding is required.
+                byte[] newopt = new byte[rlen];
+                System.arraycopy(options, 0, newopt, 0, len);
+                options = newopt;
+                len = rlen;
+            }
+            fieldValues.put(OPTIONS, options);
+            newIHL += (len >>> UNIT_SIZE_SHIFT);
+        }
+
         setHeaderLength(newIHL);
 
         return this;
     }
 
     /**
-     * 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 wordData;
+        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]));
-
-            parsedHex = Integer.valueOf(sbuffer.toString(), 16);
-            sum += parsedHex;
+            }
+            wordData = ((data[i] << 8) & 0xFF00) + (data[i + 1] & 0xFF);
+            sum = sum + wordData;
         }
         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
     /**
      * Gets the number of bits for the fieldname specified
-     * If the fieldname has variable length like "Options", then this value is computed using the
-     * options length and the header length
+     * If the fieldname has variable length like "Options", then this value is computed using the header length
      * @param fieldname - String
      * @return number of bits for fieldname - int
      */
     public int getfieldnumBits(String fieldName) {
         if (fieldName.equals(OPTIONS)) {
-            byte[] options = getOptions();
-            return ((options == null) ? 0 : (options.length - getHeaderLen()));
+            return (getHeaderLen() - MIN_HEADER_SIZE) * NetUtils.NumBitsInAByte;
         }
-        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(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(serializedBytes, checkSum, startOffset,
-                    numBits);
+            BitBufferHelper.setBytes(data, checkSum, getfieldOffset(CHECKSUM),
+                    getfieldnumBits(CHECKSUM));
         } catch (BufferException e) {
             throw new PacketException(e.getMessage());
         }
@@ -527,33 +544,47 @@ 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);
+        if (payload != null) {
+            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;
         }
     }
+
+    @Override
+    public void populateMatch(Match match) {
+        match.setField(MatchType.NW_SRC, NetUtils.getInetAddress(this.getSourceAddress()));
+        match.setField(MatchType.NW_DST, NetUtils.getInetAddress(this.getDestinationAddress()));
+        match.setField(MatchType.NW_PROTO, this.getProtocol());
+        match.setField(MatchType.NW_TOS, this.getDiffServ());
+    }
 }