3 * Copyright (c) 2013-2014 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.match.Match;
24 import org.opendaylight.controller.sal.match.MatchType;
25 import org.opendaylight.controller.sal.utils.IPProtocols;
26 import org.opendaylight.controller.sal.utils.NetUtils;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
31 * Class that represents the IPv4 packet objects
34 public class IPv4 extends Packet {
35 protected static final Logger logger = LoggerFactory
36 .getLogger(IPv4.class);
37 private static final String VERSION = "Version";
38 private static final String HEADERLENGTH = "HeaderLength";
39 private static final String DIFFSERV = "DiffServ";
40 private static final String ECN = "ECN";
41 private static final String TOTLENGTH = "TotalLength";
42 private static final String IDENTIFICATION = "Identification";
43 private static final String FLAGS = "Flags";
44 private static final String FRAGOFFSET = "FragmentOffset";
45 private static final String TTL = "TTL";
46 private static final String PROTOCOL = "Protocol";
47 private static final String CHECKSUM = "Checksum";
48 private static final String SIP = "SourceIPAddress";
49 private static final String DIP = "DestinationIPAddress";
50 private static final String OPTIONS = "Options";
52 private static final int UNIT_SIZE_SHIFT = 2;
53 private static final int UNIT_SIZE = (1 << UNIT_SIZE_SHIFT);
54 private static final int MIN_HEADER_SIZE = 20;
56 public static final Map<Byte, Class<? extends Packet>> protocolClassMap;
58 protocolClassMap = new HashMap<Byte, Class<? extends Packet>>();
59 protocolClassMap.put(IPProtocols.ICMP.byteValue(), ICMP.class);
60 protocolClassMap.put(IPProtocols.UDP.byteValue(), UDP.class);
61 protocolClassMap.put(IPProtocols.TCP.byteValue(), TCP.class);
63 private static Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
64 private static final long serialVersionUID = 1L;
66 put(VERSION, new ImmutablePair<Integer, Integer>(0, 4));
67 put(HEADERLENGTH, new ImmutablePair<Integer, Integer>(4, 4));
68 put(DIFFSERV, new ImmutablePair<Integer, Integer>(8, 6));
69 put(ECN, new ImmutablePair<Integer, Integer>(14, 2));
70 put(TOTLENGTH, new ImmutablePair<Integer, Integer>(16, 16));
71 put(IDENTIFICATION, new ImmutablePair<Integer, Integer>(32, 16));
72 put(FLAGS, new ImmutablePair<Integer, Integer>(48, 3));
73 put(FRAGOFFSET, new ImmutablePair<Integer, Integer>(51, 13));
74 put(TTL, new ImmutablePair<Integer, Integer>(64, 8));
75 put(PROTOCOL, new ImmutablePair<Integer, Integer>(72, 8));
76 put(CHECKSUM, new ImmutablePair<Integer, Integer>(80, 16));
77 put(SIP, new ImmutablePair<Integer, Integer>(96, 32));
78 put(DIP, new ImmutablePair<Integer, Integer>(128, 32));
79 put(OPTIONS, new ImmutablePair<Integer, Integer>(160, 0));
83 private final Map<String, byte[]> fieldValues;
87 * Default constructor that sets the version to 4, headerLength to 5,
88 * and flags to 2. The default value for the identification is set to a
89 * random number and the remaining fields are set to 0.
93 fieldValues = new HashMap<String, byte[]>();
94 hdrFieldCoordMap = fieldCoordinates;
95 hdrFieldsMap = fieldValues;
99 setHeaderLength((byte) 5);
100 setDiffServ((byte) 0);
102 setIdentification(generateId());
104 setFragmentOffset((short) 0);
108 * The write access to the packet is set in this constructor.
109 * Constructor that sets the version to 4, headerLength to 5,
110 * and flags to 2. The default value for the identification is set to a
111 * random number and the remaining fields are set to 0.
114 public IPv4(boolean writeAccess) {
116 fieldValues = new HashMap<String, byte[]>();
117 hdrFieldCoordMap = fieldCoordinates;
118 hdrFieldsMap = fieldValues;
121 setVersion((byte) 4);
122 setHeaderLength((byte) 5);
123 setDiffServ((byte) 0);
125 setIdentification(generateId());
127 setFragmentOffset((short) 0);
131 * Gets the IP version stored
132 * @return the version
134 public byte getVersion() {
135 return (BitBufferHelper.getByte(fieldValues.get(VERSION)));
139 * Gets the IP header length stored
140 * @return the headerLength in bytes
142 public int getHeaderLen() {
143 return (4 * BitBufferHelper.getByte(fieldValues.get(HEADERLENGTH)));
147 * Gets the header size in bits
148 * @return The number of bits constituting the header
151 public int getHeaderSize() {
152 int headerLen = this.getHeaderLen();
153 if (headerLen == 0) {
154 headerLen = MIN_HEADER_SIZE;
157 return headerLen * NetUtils.NumBitsInAByte;
161 * Gets the differential services value stored
162 * @return the diffServ
164 public byte getDiffServ() {
165 return BitBufferHelper.getByte(fieldValues.get(DIFFSERV));
169 * Gets the ecn bits stored
170 * @return the ecn bits
172 public byte getECN() {
173 return BitBufferHelper.getByte(fieldValues.get(ECN));
177 * Gets the total length of the IP header in bytes
178 * @return the totalLength
180 public short getTotalLength() {
181 return (BitBufferHelper.getShort(fieldValues.get(TOTLENGTH)));
185 * Gets the identification value stored
186 * @return the identification
188 public short getIdentification() {
189 return (BitBufferHelper.getShort(fieldValues.get(IDENTIFICATION)));
193 * Gets the flag values stored
196 public byte getFlags() {
197 return (BitBufferHelper.getByte(fieldValues.get(FLAGS)));
201 * Gets the TTL value stored
204 public byte getTtl() {
205 return (BitBufferHelper.getByte(fieldValues.get(TTL)));
209 * Gets the protocol value stored
210 * @return the protocol
212 public byte getProtocol() {
213 return (BitBufferHelper.getByte(fieldValues.get(PROTOCOL)));
217 * Gets the checksum value stored
218 * @return the checksum
220 public short getChecksum() {
221 return (BitBufferHelper.getShort(fieldValues.get(CHECKSUM)));
225 * Gets the fragment offset stored
226 * @return the fragmentOffset
228 public short getFragmentOffset() {
229 return (BitBufferHelper.getShort(fieldValues.get(FRAGOFFSET)));
233 * Gets the source IP address stored
234 * @return the sourceAddress
236 public int getSourceAddress() {
237 return (BitBufferHelper.getInt(fieldValues.get(SIP)));
241 * Gets the destination IP address stored
242 * @return the destinationAddress
244 public int getDestinationAddress() {
245 return (BitBufferHelper.getInt(fieldValues.get(DIP)));
249 * gets the Options stored
250 * @return the options
252 public byte[] getOptions() {
253 return (fieldValues.get(OPTIONS));
258 * Stores the value of fields read from data stream
259 * Variable header value like payload protocol, is stored here
261 public void setHeaderField(String headerField, byte[] readValue) {
262 if (headerField.equals(PROTOCOL)) {
263 // Don't set payloadClass if framgment offset is not zero.
264 byte[] fragoff = hdrFieldsMap.get(FRAGOFFSET);
265 if (fragoff == null || BitBufferHelper.getShort(fragoff) == 0) {
266 payloadClass = protocolClassMap.get(readValue[0]);
268 } else if (headerField.equals(FRAGOFFSET)) {
269 if (readValue != null && BitBufferHelper.getShort(readValue) != 0) {
270 // Clear payloadClass because protocol header is not present
274 } else if (headerField.equals(OPTIONS) &&
275 (readValue == null || readValue.length == 0)) {
276 hdrFieldsMap.remove(headerField);
279 hdrFieldsMap.put(headerField, readValue);
283 * Stores the IP version from the header
284 * @param version the version to set
287 public IPv4 setVersion(byte ipVersion) {
288 byte[] version = BitBufferHelper.toByteArray(ipVersion);
289 fieldValues.put(VERSION, version);
294 * Stores the length of IP header in words (4 bytes)
295 * @param headerLength the headerLength to set
298 public IPv4 setHeaderLength(byte ipheaderLength) {
299 byte[] headerLength = BitBufferHelper.toByteArray(ipheaderLength);
300 fieldValues.put(HEADERLENGTH, headerLength);
305 * Stores the differential services value from the IP header
306 * @param diffServ the diffServ to set
309 public IPv4 setDiffServ(byte ipdiffServ) {
310 byte[] diffServ = BitBufferHelper.toByteArray(ipdiffServ);
311 fieldValues.put(DIFFSERV, diffServ);
316 * Stores the ECN bits from the header
317 * @param ECN bits to set
320 public IPv4 setECN(byte ecn) {
321 byte[] ecnbytes = BitBufferHelper.toByteArray(ecn);
322 fieldValues.put(ECN, ecnbytes);
327 * Stores the total length of IP header in bytes
328 * @param totalLength the totalLength to set
331 public IPv4 setTotalLength(short iptotalLength) {
332 byte[] totalLength = BitBufferHelper.toByteArray(iptotalLength);
333 fieldValues.put(TOTLENGTH, totalLength);
338 * Stores the identification number from the header
339 * @param identification the identification to set
342 public IPv4 setIdentification(short ipIdentification) {
343 byte[] identification = BitBufferHelper.toByteArray(ipIdentification);
344 fieldValues.put(IDENTIFICATION, identification);
349 * Stores the IP flags value
350 * @param flags the flags to set
353 public IPv4 setFlags(byte ipFlags) {
354 byte[] flags = { ipFlags };
355 fieldValues.put(FLAGS, flags);
360 * Stores the IP fragmentation offset value
361 * @param fragmentOffset the fragmentOffset to set
364 public IPv4 setFragmentOffset(short ipFragmentOffset) {
365 byte[] fragmentOffset = BitBufferHelper.toByteArray(ipFragmentOffset);
366 fieldValues.put(FRAGOFFSET, fragmentOffset);
371 * Stores the TTL value
372 * @param ttl the ttl to set
375 public IPv4 setTtl(byte ipTtl) {
376 byte[] ttl = BitBufferHelper.toByteArray(ipTtl);
377 fieldValues.put(TTL, ttl);
382 * Stores the protocol value of the IP payload
383 * @param protocol the protocol to set
386 public IPv4 setProtocol(byte ipProtocol) {
387 byte[] protocol = BitBufferHelper.toByteArray(ipProtocol);
388 fieldValues.put(PROTOCOL, protocol);
393 * @param checksum the checksum to set
395 /*public IPv4 setChecksum() {
396 short ipChecksum = computeChecksum();
397 byte[] checksum = BitBufferHelper.toByteArray(ipChecksum);
398 fieldValues.put(CHECKSUM, checksum);
403 * Stores the IP source address from the header
404 * @param sourceAddress the sourceAddress to set
407 public IPv4 setSourceAddress(InetAddress ipSourceAddress) {
408 byte[] sourceAddress = ipSourceAddress.getAddress();
409 fieldValues.put(SIP, sourceAddress);
414 * Stores the IP destination address from the header
415 * @param the destination Address to set
418 public IPv4 setDestinationAddress(InetAddress ipDestinationAddress) {
419 byte[] sourceAddress = ipDestinationAddress.getAddress();
420 fieldValues.put(DIP, sourceAddress);
425 * Stores the IP destination address from the header
426 * @param destinationAddress the destinationAddress to set
429 public IPv4 setDestinationAddress(int ipDestinationAddress) {
430 byte[] destinationAddress = BitBufferHelper
431 .toByteArray(ipDestinationAddress);
432 fieldValues.put(DIP, destinationAddress);
437 * Generate a random number to set the Identification field
441 private short generateId() {
442 Random randomgen = new Random();
443 return (short) (randomgen.nextInt(Short.MAX_VALUE + 1));
447 * Store the options from IP header
448 * @param options - byte[]
451 public IPv4 setOptions(byte[] options) {
452 byte newIHL = (byte)(MIN_HEADER_SIZE >>> UNIT_SIZE_SHIFT);
453 if (options == null || options.length == 0) {
454 fieldValues.remove(OPTIONS);
456 int len = options.length;
457 int rlen = (len + (UNIT_SIZE - 1)) & ~(UNIT_SIZE - 1);
459 // Padding is required.
460 byte[] newopt = new byte[rlen];
461 System.arraycopy(options, 0, newopt, 0, len);
465 fieldValues.put(OPTIONS, options);
466 newIHL += (len >>> UNIT_SIZE_SHIFT);
469 setHeaderLength(newIHL);
475 * Computes the IPv4 header checksum on the passed stream of bytes
476 * representing the packet
481 * The byte offset from where the IPv4 packet starts
482 * @return The computed checksum
484 short computeChecksum(byte[] data, int start) {
485 int end = start + getHeaderLen();
486 short checkSum = (short) 0;
487 int sum = 0, carry = 0, finalSum = 0;
489 int checksumStart = start
490 + (getfieldOffset(CHECKSUM) / NetUtils.NumBitsInAByte);
492 for (int i = start; i <= (end - 1); i = i + 2) {
493 // Skip, if the current bytes are checkSum bytes
494 if (i == checksumStart) {
497 wordData = ((data[i] << 8) & 0xFF00) + (data[i + 1] & 0xFF);
498 sum = sum + wordData;
500 carry = (sum >> 16) & 0xFF;
501 finalSum = (sum & 0xFFFF) + carry;
502 checkSum = (short) ~((short) finalSum & 0xFFFF);
509 * Gets the number of bits for the fieldname specified
510 * If the fieldname has variable length like "Options", then this value is computed using the header length
511 * @param fieldname - String
512 * @return number of bits for fieldname - int
514 public int getfieldnumBits(String fieldName) {
515 if (fieldName.equals(OPTIONS)) {
516 return (getHeaderLen() - MIN_HEADER_SIZE) * NetUtils.NumBitsInAByte;
518 return hdrFieldCoordMap.get(fieldName).getRight();
523 * Method to perform post serialization - like computation of checksum of serialized header
526 * @Exception throws PacketException
528 protected void postSerializeCustomOperation(byte[] data)
529 throws PacketException {
531 // Recompute the total length field here
532 byte[] totalLength = BitBufferHelper.toByteArray((short) data.length);
534 BitBufferHelper.setBytes(data, totalLength, getfieldOffset(TOTLENGTH),
535 getfieldnumBits(TOTLENGTH));
536 } catch (BufferException e) {
537 throw new PacketException(e.getMessage());
540 // Now compute the Header Checksum
541 byte[] checkSum = BitBufferHelper.toByteArray(computeChecksum(data, 0));
544 BitBufferHelper.setBytes(data, checkSum, getfieldOffset(CHECKSUM),
545 getfieldnumBits(CHECKSUM));
546 } catch (BufferException e) {
547 throw new PacketException(e.getMessage());
553 * Stores the payload of IP, serializes it and stores the length of serialized payload
554 * bytes in Total Length
555 * @param payload - Packet
558 * Set the total length field in the IPv4 Object
559 * Note: this field will get overwritten during serialization phase.
561 public void setPayload(Packet payload) {
562 this.payload = payload;
564 * Deriving the Total Length here
566 int payloadLength = 0;
567 if (payload != null) {
569 payloadLength = payload.serialize().length;
570 } catch (PacketException e) {
575 this.setTotalLength((short) (this.getHeaderLen() + payloadLength));
580 * Method to perform post deserialization - like compare computed checksum with
581 * the one obtained from IP header
584 protected void postDeserializeCustomOperation(byte[] data, int startBitOffset) {
585 int start = startBitOffset / NetUtils.NumBitsInAByte;
586 short computedChecksum = computeChecksum(data, start);
587 short actualChecksum = BitBufferHelper.getShort(fieldValues.get(CHECKSUM));
588 if (computedChecksum != actualChecksum) {
594 public void populateMatch(Match match) {
595 match.setField(MatchType.NW_SRC, NetUtils.getInetAddress(this.getSourceAddress()));
596 match.setField(MatchType.NW_DST, NetUtils.getInetAddress(this.getDestinationAddress()));
597 match.setField(MatchType.NW_PROTO, this.getProtocol());
598 match.setField(MatchType.NW_TOS, this.getDiffServ());