/*
- * 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,
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;
/**
* 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";
private static final String DIP = "DestinationIPAddress";
private static final String OPTIONS = "Options";
- public static Map<Byte, Class<? extends Packet>> protocolClassMap;
+ 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>>();
protocolClassMap.put(IPProtocols.ICMP.byteValue(), ICMP.class);
}
};
- private Map<String, byte[]> fieldValues;
+ private final Map<String, byte[]> fieldValues;
+
/**
* Default constructor that sets the version to 4, headerLength to 5,
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);
}
/**
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);
}
/**
}
/**
- * 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;
-
}
/**
*/
public void setHeaderField(String headerField, byte[] readValue) {
if (headerField.equals(PROTOCOL)) {
- payloadClass = protocolClassMap.get(readValue[0]);
+ // Don't set payloadClass if framgment offset is not zero.
+ byte[] fragoff = hdrFieldsMap.get(FRAGOFFSET);
+ if (fragoff == null || BitBufferHelper.getShort(fragoff) == 0) {
+ payloadClass = protocolClassMap.get(readValue[0]);
+ }
+ } else if (headerField.equals(FRAGOFFSET)) {
+ if (readValue != null && BitBufferHelper.getShort(readValue) != 0) {
+ // Clear payloadClass because protocol header is not present
+ // in this packet.
+ payloadClass = null;
+ }
+ } else if (headerField.equals(OPTIONS) &&
+ (readValue == null || readValue.length == 0)) {
+ hdrFieldsMap.remove(headerField);
+ return;
}
hdrFieldsMap.put(headerField, readValue);
}
}
/**
- * 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
*/
* @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;
}*/
* @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 exception
+ * @Exception throws PacketException
*/
- protected void postSerializeCustomOperation(byte[] serializedBytes)
- throws Exception {
- int startOffset = this.getfieldOffset(CHECKSUM);
- int numBits = this.getfieldnumBits(CHECKSUM);
- byte[] checkSum = BitBufferHelper.toByteArray(computeChecksum(
- serializedBytes, serializedBytes.length));
- BitBufferHelper.setBytes(serializedBytes, checkSum, startOffset,
- numBits);
- return;
+ protected void postSerializeCustomOperation(byte[] data)
+ throws PacketException {
+
+ // 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(data, checkSum, getfieldOffset(CHECKSUM),
+ getfieldnumBits(CHECKSUM));
+ } catch (BufferException e) {
+ throw new PacketException(e.getMessage());
+ }
}
@Override
* 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 (Exception 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));
- if (computedChecksum != actualChecksum)
+ @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());
}
}