3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
13 package org.opendaylight.controller.sal.packet;
15 import java.net.InetAddress;
16 import java.util.HashMap;
17 import java.util.LinkedHashMap;
19 import java.util.Random;
21 import org.apache.commons.lang3.tuple.ImmutablePair;
22 import org.apache.commons.lang3.tuple.Pair;
23 import org.opendaylight.controller.sal.utils.IPProtocols;
24 import org.opendaylight.controller.sal.utils.NetUtils;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
29 * Class that represents the IPv4 packet objects
32 public class IPv4 extends Packet {
33 protected static final Logger logger = LoggerFactory
34 .getLogger(IPv4.class);
35 private static final String VERSION = "Version";
36 private static final String HEADERLENGTH = "HeaderLength";
37 private static final String DIFFSERV = "DiffServ";
38 private static final String ECN = "ECN";
39 private static final String TOTLENGTH = "TotalLength";
40 private static final String IDENTIFICATION = "Identification";
41 private static final String FLAGS = "Flags";
42 private static final String FRAGOFFSET = "FragmentOffset";
43 private static final String TTL = "TTL";
44 private static final String PROTOCOL = "Protocol";
45 private static final String CHECKSUM = "Checksum";
46 private static final String SIP = "SourceIPAddress";
47 private static final String DIP = "DestinationIPAddress";
48 private static final String OPTIONS = "Options";
50 private static final int UNIT_SIZE_SHIFT = 2;
51 private static final int UNIT_SIZE = (1 << UNIT_SIZE_SHIFT);
52 private static final int MIN_HEADER_SIZE = 20;
54 public static final Map<Byte, Class<? extends Packet>> protocolClassMap;
56 protocolClassMap = new HashMap<Byte, Class<? extends Packet>>();
57 protocolClassMap.put(IPProtocols.ICMP.byteValue(), ICMP.class);
58 protocolClassMap.put(IPProtocols.UDP.byteValue(), UDP.class);
59 protocolClassMap.put(IPProtocols.TCP.byteValue(), TCP.class);
61 private static Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
62 private static final long serialVersionUID = 1L;
64 put(VERSION, new ImmutablePair<Integer, Integer>(0, 4));
65 put(HEADERLENGTH, new ImmutablePair<Integer, Integer>(4, 4));
66 put(DIFFSERV, new ImmutablePair<Integer, Integer>(8, 6));
67 put(ECN, new ImmutablePair<Integer, Integer>(14, 2));
68 put(TOTLENGTH, new ImmutablePair<Integer, Integer>(16, 16));
69 put(IDENTIFICATION, new ImmutablePair<Integer, Integer>(32, 16));
70 put(FLAGS, new ImmutablePair<Integer, Integer>(48, 3));
71 put(FRAGOFFSET, new ImmutablePair<Integer, Integer>(51, 13));
72 put(TTL, new ImmutablePair<Integer, Integer>(64, 8));
73 put(PROTOCOL, new ImmutablePair<Integer, Integer>(72, 8));
74 put(CHECKSUM, new ImmutablePair<Integer, Integer>(80, 16));
75 put(SIP, new ImmutablePair<Integer, Integer>(96, 32));
76 put(DIP, new ImmutablePair<Integer, Integer>(128, 32));
77 put(OPTIONS, new ImmutablePair<Integer, Integer>(160, 0));
81 private final Map<String, byte[]> fieldValues;
85 * Default constructor that sets the version to 4, headerLength to 5,
86 * and flags to 2. The default value for the identification is set to a
87 * random number and the remaining fields are set to 0.
91 fieldValues = new HashMap<String, byte[]>();
92 hdrFieldCoordMap = fieldCoordinates;
93 hdrFieldsMap = fieldValues;
97 setHeaderLength((byte) 5);
98 setDiffServ((byte) 0);
100 setIdentification(generateId());
102 setFragmentOffset((short) 0);
106 * The write access to the packet is set in this constructor.
107 * Constructor that sets the version to 4, headerLength to 5,
108 * and flags to 2. The default value for the identification is set to a
109 * random number and the remaining fields are set to 0.
112 public IPv4(boolean writeAccess) {
114 fieldValues = new HashMap<String, byte[]>();
115 hdrFieldCoordMap = fieldCoordinates;
116 hdrFieldsMap = fieldValues;
119 setVersion((byte) 4);
120 setHeaderLength((byte) 5);
121 setDiffServ((byte) 0);
123 setIdentification(generateId());
125 setFragmentOffset((short) 0);
129 * Gets the IP version stored
130 * @return the version
132 public byte getVersion() {
133 return (BitBufferHelper.getByte(fieldValues.get(VERSION)));
137 * Gets the IP header length stored
138 * @return the headerLength in bytes
140 public int getHeaderLen() {
141 return (4 * BitBufferHelper.getByte(fieldValues.get(HEADERLENGTH)));
145 * Gets the header size in bits
146 * @return The number of bits constituting the header
149 public int getHeaderSize() {
150 int headerLen = this.getHeaderLen();
151 if (headerLen == 0) {
152 headerLen = MIN_HEADER_SIZE;
155 return headerLen * NetUtils.NumBitsInAByte;
159 * Gets the differential services value stored
160 * @return the diffServ
162 public byte getDiffServ() {
163 return BitBufferHelper.getByte(fieldValues.get(DIFFSERV));
167 * Gets the ecn bits stored
168 * @return the ecn bits
170 public byte getECN() {
171 return BitBufferHelper.getByte(fieldValues.get(ECN));
175 * Gets the total length of the IP header in bytes
176 * @return the totalLength
178 public short getTotalLength() {
179 return (BitBufferHelper.getShort(fieldValues.get(TOTLENGTH)));
183 * Gets the identification value stored
184 * @return the identification
186 public short getIdentification() {
187 return (BitBufferHelper.getShort(fieldValues.get(IDENTIFICATION)));
191 * Gets the flag values stored
194 public byte getFlags() {
195 return (BitBufferHelper.getByte(fieldValues.get(FLAGS)));
199 * Gets the TTL value stored
202 public byte getTtl() {
203 return (BitBufferHelper.getByte(fieldValues.get(TTL)));
207 * Gets the protocol value stored
208 * @return the protocol
210 public byte getProtocol() {
211 return (BitBufferHelper.getByte(fieldValues.get(PROTOCOL)));
215 * Gets the checksum value stored
216 * @return the checksum
218 public short getChecksum() {
219 return (BitBufferHelper.getShort(fieldValues.get(CHECKSUM)));
223 * Gets the fragment offset stored
224 * @return the fragmentOffset
226 public short getFragmentOffset() {
227 return (BitBufferHelper.getShort(fieldValues.get(FRAGOFFSET)));
231 * Gets the source IP address stored
232 * @return the sourceAddress
234 public int getSourceAddress() {
235 return (BitBufferHelper.getInt(fieldValues.get(SIP)));
239 * Gets the destination IP address stored
240 * @return the destinationAddress
242 public int getDestinationAddress() {
243 return (BitBufferHelper.getInt(fieldValues.get(DIP)));
247 * gets the Options stored
248 * @return the options
250 public byte[] getOptions() {
251 return (fieldValues.get(OPTIONS));
256 * Stores the value of fields read from data stream
257 * Variable header value like payload protocol, is stored here
259 public void setHeaderField(String headerField, byte[] readValue) {
260 if (headerField.equals(PROTOCOL)) {
261 payloadClass = protocolClassMap.get(readValue[0]);
262 } else if (headerField.equals(OPTIONS) &&
263 (readValue == null || readValue.length == 0)) {
264 hdrFieldsMap.remove(headerField);
267 hdrFieldsMap.put(headerField, readValue);
271 * Stores the IP version from the header
272 * @param version the version to set
275 public IPv4 setVersion(byte ipVersion) {
276 byte[] version = BitBufferHelper.toByteArray(ipVersion);
277 fieldValues.put(VERSION, version);
282 * Stores the length of IP header in words (4 bytes)
283 * @param headerLength the headerLength to set
286 public IPv4 setHeaderLength(byte ipheaderLength) {
287 byte[] headerLength = BitBufferHelper.toByteArray(ipheaderLength);
288 fieldValues.put(HEADERLENGTH, headerLength);
293 * Stores the differential services value from the IP header
294 * @param diffServ the diffServ to set
297 public IPv4 setDiffServ(byte ipdiffServ) {
298 byte[] diffServ = BitBufferHelper.toByteArray(ipdiffServ);
299 fieldValues.put(DIFFSERV, diffServ);
304 * Stores the ECN bits from the header
305 * @param ECN bits to set
308 public IPv4 setECN(byte ecn) {
309 byte[] ecnbytes = BitBufferHelper.toByteArray(ecn);
310 fieldValues.put(ECN, ecnbytes);
315 * Stores the total length of IP header in bytes
316 * @param totalLength the totalLength to set
319 public IPv4 setTotalLength(short iptotalLength) {
320 byte[] totalLength = BitBufferHelper.toByteArray(iptotalLength);
321 fieldValues.put(TOTLENGTH, totalLength);
326 * Stores the identification number from the header
327 * @param identification the identification to set
330 public IPv4 setIdentification(short ipIdentification) {
331 byte[] identification = BitBufferHelper.toByteArray(ipIdentification);
332 fieldValues.put(IDENTIFICATION, identification);
337 * Stores the IP flags value
338 * @param flags the flags to set
341 public IPv4 setFlags(byte ipFlags) {
342 byte[] flags = { ipFlags };
343 fieldValues.put(FLAGS, flags);
348 * Stores the IP fragmentation offset value
349 * @param fragmentOffset the fragmentOffset to set
352 public IPv4 setFragmentOffset(short ipFragmentOffset) {
353 byte[] fragmentOffset = BitBufferHelper.toByteArray(ipFragmentOffset);
354 fieldValues.put(FRAGOFFSET, fragmentOffset);
359 * Stores the TTL value
360 * @param ttl the ttl to set
363 public IPv4 setTtl(byte ipTtl) {
364 byte[] ttl = BitBufferHelper.toByteArray(ipTtl);
365 fieldValues.put(TTL, ttl);
370 * Stores the protocol value of the IP payload
371 * @param protocol the protocol to set
374 public IPv4 setProtocol(byte ipProtocol) {
375 byte[] protocol = BitBufferHelper.toByteArray(ipProtocol);
376 fieldValues.put(PROTOCOL, protocol);
381 * @param checksum the checksum to set
383 /*public IPv4 setChecksum() {
384 short ipChecksum = computeChecksum();
385 byte[] checksum = BitBufferHelper.toByteArray(ipChecksum);
386 fieldValues.put(CHECKSUM, checksum);
391 * Stores the IP source address from the header
392 * @param sourceAddress the sourceAddress to set
395 public IPv4 setSourceAddress(InetAddress ipSourceAddress) {
396 byte[] sourceAddress = ipSourceAddress.getAddress();
397 fieldValues.put(SIP, sourceAddress);
402 * Stores the IP destination address from the header
403 * @param the destination Address to set
406 public IPv4 setDestinationAddress(InetAddress ipDestinationAddress) {
407 byte[] sourceAddress = ipDestinationAddress.getAddress();
408 fieldValues.put(DIP, sourceAddress);
413 * Stores the IP destination address from the header
414 * @param destinationAddress the destinationAddress to set
417 public IPv4 setDestinationAddress(int ipDestinationAddress) {
418 byte[] destinationAddress = BitBufferHelper
419 .toByteArray(ipDestinationAddress);
420 fieldValues.put(DIP, destinationAddress);
425 * Generate a random number to set the Identification field
429 private short generateId() {
430 Random randomgen = new Random();
431 return (short) (randomgen.nextInt(Short.MAX_VALUE + 1));
435 * Store the options from IP header
436 * @param options - byte[]
439 public IPv4 setOptions(byte[] options) {
440 byte newIHL = (byte)(MIN_HEADER_SIZE >>> UNIT_SIZE_SHIFT);
441 if (options == null || options.length == 0) {
442 fieldValues.remove(OPTIONS);
444 int len = options.length;
445 int rlen = (len + (UNIT_SIZE - 1)) & ~(UNIT_SIZE - 1);
447 // Padding is required.
448 byte[] newopt = new byte[rlen];
449 System.arraycopy(options, 0, newopt, 0, len);
453 fieldValues.put(OPTIONS, options);
454 newIHL += (len >>> UNIT_SIZE_SHIFT);
457 setHeaderLength(newIHL);
463 * Computes the IPv4 header checksum on the passed stream of bytes
464 * representing the packet
469 * The byte offset from where the IPv4 packet starts
470 * @return The computed checksum
472 short computeChecksum(byte[] data, int start) {
473 int end = start + getHeaderLen();
474 short checkSum = (short) 0;
475 int sum = 0, carry = 0, finalSum = 0;
477 int checksumStart = start
478 + (getfieldOffset(CHECKSUM) / NetUtils.NumBitsInAByte);
480 for (int i = start; i <= (end - 1); i = i + 2) {
481 // Skip, if the current bytes are checkSum bytes
482 if (i == checksumStart) {
485 wordData = ((data[i] << 8) & 0xFF00) + (data[i + 1] & 0xFF);
486 sum = sum + wordData;
488 carry = (sum >> 16) & 0xFF;
489 finalSum = (sum & 0xFFFF) + carry;
490 checkSum = (short) ~((short) finalSum & 0xFFFF);
497 * Gets the number of bits for the fieldname specified
498 * If the fieldname has variable length like "Options", then this value is computed using the header length
499 * @param fieldname - String
500 * @return number of bits for fieldname - int
502 public int getfieldnumBits(String fieldName) {
503 if (fieldName.equals(OPTIONS)) {
504 return (getHeaderLen() - MIN_HEADER_SIZE) * NetUtils.NumBitsInAByte;
506 return hdrFieldCoordMap.get(fieldName).getRight();
511 * Method to perform post serialization - like computation of checksum of serialized header
514 * @Exception throws PacketException
516 protected void postSerializeCustomOperation(byte[] data)
517 throws PacketException {
519 // Recompute the total length field here
520 byte[] totalLength = BitBufferHelper.toByteArray((short) data.length);
522 BitBufferHelper.setBytes(data, totalLength, getfieldOffset(TOTLENGTH),
523 getfieldnumBits(TOTLENGTH));
524 } catch (BufferException e) {
525 throw new PacketException(e.getMessage());
528 // Now compute the Header Checksum
529 byte[] checkSum = BitBufferHelper.toByteArray(computeChecksum(data, 0));
532 BitBufferHelper.setBytes(data, checkSum, getfieldOffset(CHECKSUM),
533 getfieldnumBits(CHECKSUM));
534 } catch (BufferException e) {
535 throw new PacketException(e.getMessage());
541 * Stores the payload of IP, serializes it and stores the length of serialized payload
542 * bytes in Total Length
543 * @param payload - Packet
546 * Set the total length field in the IPv4 Object
547 * Note: this field will get overwritten during serialization phase.
549 public void setPayload(Packet payload) {
550 this.payload = payload;
552 * Deriving the Total Length here
554 int payloadLength = 0;
555 if (payload != null) {
557 payloadLength = payload.serialize().length;
558 } catch (PacketException e) {
563 this.setTotalLength((short) (this.getHeaderLen() + payloadLength));
568 * Method to perform post deserialization - like compare computed checksum with
569 * the one obtained from IP header
572 protected void postDeserializeCustomOperation(byte[] data, int startBitOffset) {
573 int start = startBitOffset / NetUtils.NumBitsInAByte;
574 short computedChecksum = computeChecksum(data, start);
575 short actualChecksum = BitBufferHelper.getShort(fieldValues.get(CHECKSUM));
576 if (computedChecksum != actualChecksum) {