X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=mdsalutil%2Fmdsalutil-api%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fvpnservice%2Fmdsalutil%2Fpacket%2FIPv4.java;fp=mdsalutil%2Fmdsalutil-api%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fvpnservice%2Fmdsalutil%2Fpacket%2FIPv4.java;h=2bc29a84b9390c63c40e5ca2553bf200dfd1f3db;hb=2dace9ee1047a897bc6c0f3d1a8b05301797bc99;hp=0000000000000000000000000000000000000000;hpb=0f032724da670d7ce19884bd41f7f839f71ad914;p=vpnservice.git diff --git a/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/mdsalutil/packet/IPv4.java b/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/mdsalutil/packet/IPv4.java new file mode 100644 index 00000000..2bc29a84 --- /dev/null +++ b/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/mdsalutil/packet/IPv4.java @@ -0,0 +1,595 @@ +/* + * Copyright (c) 2013 - 2015 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +/** + * + */ +package org.opendaylight.vpnservice.mdsalutil.packet; + +import java.net.InetAddress; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Random; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.opendaylight.controller.liblldp.BitBufferHelper; +import org.opendaylight.controller.liblldp.BufferException; +import org.opendaylight.controller.liblldp.NetUtils; +import org.opendaylight.controller.liblldp.Packet; +import org.opendaylight.controller.liblldp.PacketException; +import org.slf4j.Logger; +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); + private static final String VERSION = "Version"; + private static final String HEADERLENGTH = "HeaderLength"; + private static final String DIFFSERV = "DiffServ"; + private static final String ECN = "ECN"; + private static final String TOTLENGTH = "TotalLength"; + private static final String IDENTIFICATION = "Identification"; + private static final String FLAGS = "Flags"; + private static final String FRAGOFFSET = "FragmentOffset"; + private static final String TTL = "TTL"; + private static final String PROTOCOL = "Protocol"; + private static final String CHECKSUM = "Checksum"; + private static final String SIP = "SourceIPAddress"; + 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> protocolClassMap; + static { + protocolClassMap = new HashMap>(); + protocolClassMap.put(IPProtocols.ICMP.byteValue(), ICMP.class); + protocolClassMap.put(IPProtocols.UDP.byteValue(), UDP.class); + protocolClassMap.put(IPProtocols.TCP.byteValue(), TCP.class); + } + private static Map> fieldCoordinates = new LinkedHashMap>() { + private static final long serialVersionUID = 1L; + { + put(VERSION, new ImmutablePair(0, 4)); + put(HEADERLENGTH, new ImmutablePair(4, 4)); + put(DIFFSERV, new ImmutablePair(8, 6)); + put(ECN, new ImmutablePair(14, 2)); + put(TOTLENGTH, new ImmutablePair(16, 16)); + put(IDENTIFICATION, new ImmutablePair(32, 16)); + put(FLAGS, new ImmutablePair(48, 3)); + put(FRAGOFFSET, new ImmutablePair(51, 13)); + put(TTL, new ImmutablePair(64, 8)); + put(PROTOCOL, new ImmutablePair(72, 8)); + put(CHECKSUM, new ImmutablePair(80, 16)); + put(SIP, new ImmutablePair(96, 32)); + put(DIP, new ImmutablePair(128, 32)); + put(OPTIONS, new ImmutablePair(160, 0)); + } + }; + + private final Map fieldValues; + + + /** + * Default constructor that sets the version to 4, headerLength to 5, + * and flags to 2. The default value for the identification is set to a + * random number and the remaining fields are set to 0. + */ + public IPv4() { + super(); + 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); + } + + /** + * The write access to the packet is set in this constructor. + * Constructor that sets the version to 4, headerLength to 5, + * and flags to 2. The default value for the identification is set to a + * random number and the remaining fields are set to 0. + * @param writeAccess - boolean + */ + public IPv4(boolean writeAccess) { + super(writeAccess); + 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); + } + + /** + * Gets the IP version stored + * @return the version + */ + public byte getVersion() { + return (BitBufferHelper.getByte(fieldValues.get(VERSION))); + } + + /** + * Gets the IP header length stored + * @return the headerLength in bytes + */ + public int getHeaderLen() { + return (4 * BitBufferHelper.getByte(fieldValues.get(HEADERLENGTH))); + } + + /** + * 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 = MIN_HEADER_SIZE; + } + + return headerLen * NetUtils.NumBitsInAByte; + } + + /** + * Gets the differential services value stored + * @return the diffServ + */ + public byte getDiffServ() { + return BitBufferHelper.getByte(fieldValues.get(DIFFSERV)); + } + + /** + * Gets the ecn bits stored + * @return the ecn bits + */ + public byte getECN() { + return BitBufferHelper.getByte(fieldValues.get(ECN)); + } + + /** + * Gets the total length of the IP header in bytes + * @return the totalLength + */ + public short getTotalLength() { + return (BitBufferHelper.getShort(fieldValues.get(TOTLENGTH))); + } + + /** + * Gets the identification value stored + * @return the identification + */ + public short getIdentification() { + return (BitBufferHelper.getShort(fieldValues.get(IDENTIFICATION))); + } + + /** + * Gets the flag values stored + * @return the flags + */ + public byte getFlags() { + return (BitBufferHelper.getByte(fieldValues.get(FLAGS))); + } + + /** + * Gets the TTL value stored + * @return the ttl + */ + public byte getTtl() { + return (BitBufferHelper.getByte(fieldValues.get(TTL))); + } + + /** + * Gets the protocol value stored + * @return the protocol + */ + public byte getProtocol() { + return (BitBufferHelper.getByte(fieldValues.get(PROTOCOL))); + } + + /** + * Gets the checksum value stored + * @return the checksum + */ + public short getChecksum() { + return (BitBufferHelper.getShort(fieldValues.get(CHECKSUM))); + } + + /** + * Gets the fragment offset stored + * @return the fragmentOffset + */ + public short getFragmentOffset() { + return (BitBufferHelper.getShort(fieldValues.get(FRAGOFFSET))); + } + + /** + * Gets the source IP address stored + * @return the sourceAddress + */ + public int getSourceAddress() { + return (BitBufferHelper.getInt(fieldValues.get(SIP))); + } + + /** + * Gets the destination IP address stored + * @return the destinationAddress + */ + public int getDestinationAddress() { + return (BitBufferHelper.getInt(fieldValues.get(DIP))); + } + + /** + * gets the Options stored + * @return the options + */ + public byte[] getOptions() { + return (fieldValues.get(OPTIONS)); + } + + @Override + /** + * Stores the value of fields read from data stream + * Variable header value like payload protocol, is stored here + * @param headerField + * @param readValue + */ + public void setHeaderField(String headerField, byte[] readValue) { + if (headerField.equals(PROTOCOL)) { + // 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 IP version from the header + * @param ipVersion the version to set + * @return IPv4 + */ + public IPv4 setVersion(byte ipVersion) { + byte[] version = BitBufferHelper.toByteArray(ipVersion); + fieldValues.put(VERSION, version); + return this; + } + + /** + * Stores the length of IP header in words (4 bytes) + * @param ipheaderLength the headerLength to set + * @return IPv4 + */ + public IPv4 setHeaderLength(byte ipheaderLength) { + byte[] headerLength = BitBufferHelper.toByteArray(ipheaderLength); + fieldValues.put(HEADERLENGTH, headerLength); + return this; + } + + /** + * Stores the differential services value from the IP header + * @param ipdiffServ the diffServ to set + * @return IPv4 + */ + public IPv4 setDiffServ(byte ipdiffServ) { + byte[] diffServ = BitBufferHelper.toByteArray(ipdiffServ); + fieldValues.put(DIFFSERV, diffServ); + return this; + } + + /** + * Stores the ECN bits from the header + * @param ecn ECN bits to set + * @return IPv4 + */ + public IPv4 setECN(byte ecn) { + byte[] ecnbytes = BitBufferHelper.toByteArray(ecn); + fieldValues.put(ECN, ecnbytes); + return this; + } + + /** + * Stores the total length of IP header in bytes + * @param iptotalLength the totalLength to set + * @return IPv4 + */ + public IPv4 setTotalLength(short iptotalLength) { + byte[] totalLength = BitBufferHelper.toByteArray(iptotalLength); + fieldValues.put(TOTLENGTH, totalLength); + return this; + } + + /** + * Stores the identification number from the header + * @param ipIdentification the identification to set + * @return IPv4 + */ + public IPv4 setIdentification(short ipIdentification) { + byte[] identification = BitBufferHelper.toByteArray(ipIdentification); + fieldValues.put(IDENTIFICATION, identification); + return this; + } + + /** + * Stores the IP flags value + * @param ipFlags the flags to set + * @return IPv4 + */ + public IPv4 setFlags(byte ipFlags) { + byte[] flags = { ipFlags }; + fieldValues.put(FLAGS, flags); + return this; + } + + /** + * Stores the IP fragmentation offset value + * @param ipFragmentOffset the fragmentOffset to set + * @return IPv4 + */ + public IPv4 setFragmentOffset(short ipFragmentOffset) { + byte[] fragmentOffset = BitBufferHelper.toByteArray(ipFragmentOffset); + fieldValues.put(FRAGOFFSET, fragmentOffset); + return this; + } + + /** + * Stores the TTL value + * @param ipTtl the ttl to set + * @return IPv4 + */ + public IPv4 setTtl(byte ipTtl) { + byte[] ttl = BitBufferHelper.toByteArray(ipTtl); + fieldValues.put(TTL, ttl); + return this; + } + + /** + * Stores the protocol value of the IP payload + * @param ipProtocol the protocol to set + * @return IPv4 + */ + public IPv4 setProtocol(byte ipProtocol) { + byte[] protocol = BitBufferHelper.toByteArray(ipProtocol); + fieldValues.put(PROTOCOL, protocol); + return this; + } + + /** + * @param checksum the checksum to set + */ + /*public IPv4 setChecksum() { + short ipChecksum = computeChecksum(); + byte[] checksum = BitBufferHelper.toByteArray(ipChecksum); + fieldValues.put(CHECKSUM, checksum); + return this; + }*/ + + /** + * Stores the IP source address from the header + * @param ipSourceAddress the sourceAddress to set + * @return IPv4 + */ + public IPv4 setSourceAddress(InetAddress ipSourceAddress) { + byte[] sourceAddress = ipSourceAddress.getAddress(); + fieldValues.put(SIP, sourceAddress); + return this; + } + + /** + * Stores the IP destination address from the header + * @param ipDestinationAddress the destination Address to set + * @return IPv4 + */ + public IPv4 setDestinationAddress(InetAddress ipDestinationAddress) { + byte[] sourceAddress = ipDestinationAddress.getAddress(); + fieldValues.put(DIP, sourceAddress); + return this; + } + + /** + * Stores the IP destination address from the header + * @param ipDestinationAddress the destinationAddress to set + * @return IPv4 + */ + public IPv4 setDestinationAddress(int ipDestinationAddress) { + byte[] destinationAddress = BitBufferHelper + .toByteArray(ipDestinationAddress); + fieldValues.put(DIP, destinationAddress); + return this; + } + + /** + * Generate a random number to set the Identification field + * in IPv4 Header + * @return short + */ + private short generateId() { + Random randomgen = new Random(); + return (short) (randomgen.nextInt(Short.MAX_VALUE + 1)); + } + + /** + * Store the options from IP header + * @param options - byte[] + * @return IPv4 + */ + public IPv4 setOptions(byte[] options) { + 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 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 + */ + short computeChecksum(byte[] data, int start) { + int end = start + getHeaderLen(); + short checkSum = (short) 0; + int sum = 0, carry = 0, finalSum = 0; + int wordData; + int checksumStart = 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 == checksumStart) { + continue; + } + 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 + /** + * Gets the number of bits for the fieldname specified + * 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)) { + return (getHeaderLen() - MIN_HEADER_SIZE) * NetUtils.NumBitsInAByte; + } + return hdrFieldCoordMap.get(fieldName).getRight(); + } + + @Override + /** + * Method to perform post serialization - like computation of checksum of serialized header + * @param data + * @return void + * @Exception throws PacketException + */ + 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 + /** + * Stores the payload of IP, serializes it and stores the length of serialized payload + * 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 Length here + */ + int payloadLength = 0; + if (payload != null) { + try { + payloadLength = payload.serialize().length; + } catch (PacketException e) { + logger.error("", e); + } + } + + this.setTotalLength((short) (this.getHeaderLen() + payloadLength)); + } + + + /** + * Method to perform post deserialization - like compare computed checksum with + * the one obtained from IP header + */ + @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; + } + } + +}