2 * Copyright (c) 2013 - 2015 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.genius.mdsalutil.packet;
10 import java.net.InetAddress;
11 import java.util.HashMap;
12 import java.util.LinkedHashMap;
14 import java.util.Random;
15 import org.apache.commons.lang3.tuple.ImmutablePair;
16 import org.apache.commons.lang3.tuple.Pair;
17 import org.opendaylight.openflowplugin.libraries.liblldp.BitBufferHelper;
18 import org.opendaylight.openflowplugin.libraries.liblldp.BufferException;
19 import org.opendaylight.openflowplugin.libraries.liblldp.Packet;
20 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
25 * Class that represents the IPv4 packet objects.
27 public class IPv4 extends Packet {
29 private static final Logger LOG = LoggerFactory.getLogger(IPv4.class);
31 private static final String VERSION = "Version";
32 private static final String HEADERLENGTH = "HeaderLength";
33 private static final String DIFFSERV = "DiffServ";
34 private static final String ECN = "ECN";
35 private static final String TOTLENGTH = "TotalLength";
36 private static final String IDENTIFICATION = "Identification";
37 private static final String FLAGS = "Flags";
38 private static final String FRAGOFFSET = "FragmentOffset";
39 private static final String TTL = "TTL";
40 private static final String PROTOCOL = "Protocol";
41 private static final String CHECKSUM = "Checksum";
42 private static final String SIP = "SourceIPAddress";
43 private static final String DIP = "DestinationIPAddress";
44 private static final String OPTIONS = "Options";
46 private static final int UNIT_SIZE_SHIFT = 2;
47 private static final int UNIT_SIZE = 1 << UNIT_SIZE_SHIFT;
48 private static final int MIN_HEADER_SIZE = 20;
50 private static final Map<Byte, Class<? extends Packet>> PROTOCOL_CLASS_MAP;
53 PROTOCOL_CLASS_MAP = new HashMap<>();
54 PROTOCOL_CLASS_MAP.put(IPProtocols.ICMP.byteValue(), ICMP.class);
55 PROTOCOL_CLASS_MAP.put(IPProtocols.UDP.byteValue(), UDP.class);
56 PROTOCOL_CLASS_MAP.put(IPProtocols.TCP.byteValue(), TCP.class);
59 @SuppressWarnings("serial")
60 private static final Map<String, Pair<Integer, Integer>> FIELD_COORDINATES
61 = new LinkedHashMap<String, Pair<Integer, Integer>>() { {
62 put(VERSION, new ImmutablePair<>(0, 4));
63 put(HEADERLENGTH, new ImmutablePair<>(4, 4));
64 put(DIFFSERV, new ImmutablePair<>(8, 6));
65 put(ECN, new ImmutablePair<>(14, 2));
66 put(TOTLENGTH, new ImmutablePair<>(16, 16));
67 put(IDENTIFICATION, new ImmutablePair<>(32, 16));
68 put(FLAGS, new ImmutablePair<>(48, 3));
69 put(FRAGOFFSET, new ImmutablePair<>(51, 13));
70 put(TTL, new ImmutablePair<>(64, 8));
71 put(PROTOCOL, new ImmutablePair<>(72, 8));
72 put(CHECKSUM, new ImmutablePair<>(80, 16));
73 put(SIP, new ImmutablePair<>(96, 32));
74 put(DIP, new ImmutablePair<>(128, 32));
75 put(OPTIONS, new ImmutablePair<>(160, 0));
79 private final Map<String, byte[]> fieldValues;
83 * Default constructor that sets the version to 4, headerLength to 5,
84 * and flags to 2. The default value for the identification is set to a
85 * random number and the remaining fields are set to 0.
88 fieldValues = new HashMap<>();
89 hdrFieldCoordMap = FIELD_COORDINATES;
90 hdrFieldsMap = fieldValues;
94 setHeaderLength((byte) 5);
95 setDiffServ((byte) 0);
97 setIdentification(generateId());
99 setFragmentOffset((short) 0);
103 * The write access to the packet is set in this constructor.
104 * Constructor that sets the version to 4, headerLength to 5,
105 * and flags to 2. The default value for the identification is set to a
106 * random number and the remaining fields are set to 0.
107 * @param writeAccess - boolean
109 public IPv4(boolean writeAccess) {
111 fieldValues = new HashMap<>();
112 hdrFieldCoordMap = FIELD_COORDINATES;
113 hdrFieldsMap = fieldValues;
116 setVersion((byte) 4);
117 setHeaderLength((byte) 5);
118 setDiffServ((byte) 0);
120 setIdentification(generateId());
122 setFragmentOffset((short) 0);
126 * Gets the IP version stored.
127 * @return the version
129 public byte getVersion() {
130 return BitBufferHelper.getByte(fieldValues.get(VERSION));
134 * Gets the IP header length stored.
135 * @return the headerLength in bytes
137 public int getHeaderLen() {
138 return 4 * BitBufferHelper.getByte(fieldValues.get(HEADERLENGTH));
142 * Gets the header size in bits.
143 * @return The number of bits constituting the header
146 public int getHeaderSize() {
147 int headerLen = this.getHeaderLen();
148 if (headerLen == 0) {
149 headerLen = MIN_HEADER_SIZE;
152 return headerLen * Byte.SIZE;
156 * Gets the differential services value stored.
157 * @return the diffServ
159 public byte getDiffServ() {
160 return BitBufferHelper.getByte(fieldValues.get(DIFFSERV));
164 * Gets the ecn bits stored.
165 * @return the ecn bits
167 public byte getECN() {
168 return BitBufferHelper.getByte(fieldValues.get(ECN));
172 * Gets the total length of the IP header in bytes.
173 * @return the totalLength
175 public short getTotalLength() {
176 return BitBufferHelper.getShort(fieldValues.get(TOTLENGTH));
180 * Gets the identification value stored.
181 * @return the identification
183 public short getIdentification() {
184 return BitBufferHelper.getShort(fieldValues.get(IDENTIFICATION));
188 * Gets the flag values stored.
191 public byte getFlags() {
192 return BitBufferHelper.getByte(fieldValues.get(FLAGS));
196 * Gets the TTL value stored.
199 public byte getTtl() {
200 return BitBufferHelper.getByte(fieldValues.get(TTL));
204 * Gets the protocol value stored.
205 * @return the protocol
207 public byte getProtocol() {
208 return BitBufferHelper.getByte(fieldValues.get(PROTOCOL));
212 * Gets the checksum value stored.
213 * @return the checksum
215 public short getChecksum() {
216 return BitBufferHelper.getShort(fieldValues.get(CHECKSUM));
220 * Gets the fragment offset stored.
221 * @return the fragmentOffset
223 public short getFragmentOffset() {
224 return BitBufferHelper.getShort(fieldValues.get(FRAGOFFSET));
228 * Gets the source IP address stored.
229 * @return the sourceAddress
231 public int getSourceAddress() {
232 return BitBufferHelper.getInt(fieldValues.get(SIP));
236 * Gets the destination IP address stored.
237 * @return the destinationAddress
239 public int getDestinationAddress() {
240 return BitBufferHelper.getInt(fieldValues.get(DIP));
244 * gets the Options stored.
245 * @return the options
247 public byte[] getOptions() {
248 return fieldValues.get(OPTIONS);
253 * Stores the value of fields read from data stream
254 * Variable header value like payload protocol, is stored here
258 public void setHeaderField(String headerField, byte[] readValue) {
259 if (headerField.equals(PROTOCOL)) {
260 // Don't set payloadClass if framgment offset is not zero.
261 byte[] fragoff = hdrFieldsMap.get(FRAGOFFSET);
262 if (fragoff == null || BitBufferHelper.getShort(fragoff) == 0) {
263 payloadClass = PROTOCOL_CLASS_MAP.get(readValue[0]);
265 } else if (headerField.equals(FRAGOFFSET)) {
266 if (readValue != null && BitBufferHelper.getShort(readValue) != 0) {
267 // Clear payloadClass because protocol header is not present
271 } else if (headerField.equals(OPTIONS)
272 && (readValue == null || readValue.length == 0)) {
273 hdrFieldsMap.remove(headerField);
276 hdrFieldsMap.put(headerField, readValue);
280 * Stores the IP version from the header.
281 * @param ipVersion the version to set
284 public IPv4 setVersion(byte ipVersion) {
285 byte[] version = BitBufferHelper.toByteArray(ipVersion);
286 fieldValues.put(VERSION, version);
291 * Stores the length of IP header in words (4 bytes).
292 * @param ipheaderLength the headerLength to set
295 public IPv4 setHeaderLength(byte ipheaderLength) {
296 byte[] headerLength = BitBufferHelper.toByteArray(ipheaderLength);
297 fieldValues.put(HEADERLENGTH, headerLength);
302 * Stores the differential services value from the IP header.
303 * @param ipdiffServ the diffServ to set
306 public IPv4 setDiffServ(byte ipdiffServ) {
307 byte[] diffServ = BitBufferHelper.toByteArray(ipdiffServ);
308 fieldValues.put(DIFFSERV, diffServ);
313 * Stores the ECN bits from the header.
314 * @param ecn ECN bits to set
317 public IPv4 setECN(byte ecn) {
318 byte[] ecnbytes = BitBufferHelper.toByteArray(ecn);
319 fieldValues.put(ECN, ecnbytes);
324 * Stores the total length of IP header in bytes.
325 * @param iptotalLength the totalLength to set
328 public IPv4 setTotalLength(short iptotalLength) {
329 byte[] totalLength = BitBufferHelper.toByteArray(iptotalLength);
330 fieldValues.put(TOTLENGTH, totalLength);
335 * Stores the identification number from the header.
336 * @param ipIdentification the identification to set
339 public IPv4 setIdentification(short ipIdentification) {
340 byte[] identification = BitBufferHelper.toByteArray(ipIdentification);
341 fieldValues.put(IDENTIFICATION, identification);
346 * Stores the IP flags value.
347 * @param ipFlags the flags to set
350 public IPv4 setFlags(byte ipFlags) {
351 byte[] flags = { ipFlags };
352 fieldValues.put(FLAGS, flags);
357 * Stores the IP fragmentation offset value.
358 * @param ipFragmentOffset the fragmentOffset to set
361 public IPv4 setFragmentOffset(short ipFragmentOffset) {
362 byte[] fragmentOffset = BitBufferHelper.toByteArray(ipFragmentOffset);
363 fieldValues.put(FRAGOFFSET, fragmentOffset);
368 * Stores the TTL value.
369 * @param ipTtl the ttl to set
372 public IPv4 setTtl(byte ipTtl) {
373 byte[] ttl = BitBufferHelper.toByteArray(ipTtl);
374 fieldValues.put(TTL, ttl);
379 * Stores the protocol value of the IP payload.
380 * @param ipProtocol the protocol to set
383 public IPv4 setProtocol(byte ipProtocol) {
384 byte[] protocol = BitBufferHelper.toByteArray(ipProtocol);
385 fieldValues.put(PROTOCOL, protocol);
390 * @param checksum the checksum to set
392 public IPv4 setChecksum() {
393 short ipChecksum = computeChecksum();
394 byte[] checksum = BitBufferHelper.toByteArray(ipChecksum);
395 fieldValues.put(CHECKSUM, checksum);
400 * Stores the IP source address from the header.
401 * @param ipSourceAddress the sourceAddress to set
404 public IPv4 setSourceAddress(InetAddress ipSourceAddress) {
405 byte[] sourceAddress = ipSourceAddress.getAddress();
406 fieldValues.put(SIP, sourceAddress);
411 * Stores the IP destination address from the header.
412 * @param ipDestinationAddress the destination Address to set
415 public IPv4 setDestinationAddress(InetAddress ipDestinationAddress) {
416 byte[] sourceAddress = ipDestinationAddress.getAddress();
417 fieldValues.put(DIP, sourceAddress);
422 * Stores the IP destination address from the header.
423 * @param ipDestinationAddress the destinationAddress to set
426 public IPv4 setDestinationAddress(int ipDestinationAddress) {
427 byte[] destinationAddress = BitBufferHelper
428 .toByteArray(ipDestinationAddress);
429 fieldValues.put(DIP, destinationAddress);
434 * Generate a random number to set the Identification field
438 private static short generateId() {
439 Random randomgen = new Random();
440 return (short) randomgen.nextInt(Short.MAX_VALUE + 1);
444 * Store the options from IP header.
445 * @param options - byte[]
448 public IPv4 setOptions(byte[] options) {
449 byte newIHL = (byte)(MIN_HEADER_SIZE >>> UNIT_SIZE_SHIFT);
450 if (options == null || options.length == 0) {
451 fieldValues.remove(OPTIONS);
453 int len = options.length;
454 int rlen = len + UNIT_SIZE - 1 & ~(UNIT_SIZE - 1);
456 // Padding is required.
457 byte[] newopt = new byte[rlen];
458 System.arraycopy(options, 0, newopt, 0, len);
462 fieldValues.put(OPTIONS, options);
463 newIHL += len >>> UNIT_SIZE_SHIFT;
466 setHeaderLength(newIHL);
472 * Computes the IPv4 header checksum on the passed stream of bytes
473 * representing the packet.
478 * The byte offset from where the IPv4 packet starts
479 * @return The computed checksum
481 short computeChecksum(byte[] data, int start) {
482 int end = start + getHeaderLen();
486 int checksumStart = start + getfieldOffset(CHECKSUM) / Byte.SIZE;
488 for (int i = start; i <= end - 1; i = i + 2) {
489 // Skip, if the current bytes are checkSum bytes
490 if (i == checksumStart) {
493 wordData = (data[i] << 8 & 0xFF00) + (data[i + 1] & 0xFF);
494 sum = sum + wordData;
496 int carry = sum >> 16 & 0xFF;
497 int finalSum = (sum & 0xFFFF) + carry;
498 checkSum = (short) ~((short) finalSum & 0xFFFF);
505 * Gets the number of bits for the fieldname specified
506 * If the fieldname has variable length like "Options", then this value is computed using the header length
507 * @param fieldname - String
508 * @return number of bits for fieldname - int
510 public int getfieldnumBits(String fieldName) {
511 if (fieldName.equals(OPTIONS)) {
512 return (getHeaderLen() - MIN_HEADER_SIZE) * Byte.SIZE;
514 return hdrFieldCoordMap.get(fieldName).getRight();
519 * Method to perform post serialization - like computation of checksum of serialized header
522 * @Exception throws PacketException
524 protected void postSerializeCustomOperation(byte[] data)
525 throws PacketException {
527 // Recompute the total length field here
528 byte[] totalLength = BitBufferHelper.toByteArray((short) data.length);
530 BitBufferHelper.setBytes(data, totalLength, getfieldOffset(TOTLENGTH),
531 getfieldnumBits(TOTLENGTH));
532 } catch (BufferException e) {
533 throw new PacketException(e.getMessage(), e);
536 // Now compute the Header Checksum
537 byte[] checkSum = BitBufferHelper.toByteArray(computeChecksum(data, 0));
540 BitBufferHelper.setBytes(data, checkSum, getfieldOffset(CHECKSUM),
541 getfieldnumBits(CHECKSUM));
542 } catch (BufferException e) {
543 throw new PacketException(e.getMessage(), e);
549 * Stores the payload of IP, serializes it and stores the length of serialized payload
550 * bytes in Total Length
551 * @param payload - Packet
554 * Set the total length field in the IPv4 Object
555 * Note: this field will get overwritten during serialization phase.
557 public void setPayload(Packet payload) {
558 this.payload = payload;
560 * Deriving the Total Length here
562 int payloadLength = 0;
563 if (payload != null) {
565 payloadLength = payload.serialize().length;
566 } catch (PacketException e) {
567 LOG.error("Exception in setpayload", e);
571 this.setTotalLength((short) (this.getHeaderLen() + payloadLength));
576 * Method to perform post deserialization - like compare computed checksum with
577 * the one obtained from IP header.
580 protected void postDeserializeCustomOperation(byte[] data, int startBitOffset) {
581 int start = startBitOffset / Byte.SIZE;
582 short computedChecksum = computeChecksum(data, start);
583 short actualChecksum = BitBufferHelper.getShort(fieldValues.get(CHECKSUM));
584 if (computedChecksum != actualChecksum) {