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 public static final Map<Byte, Class<? extends Packet>> protocolClassMap;
52 protocolClassMap = new HashMap<Byte, Class<? extends Packet>>();
53 protocolClassMap.put(IPProtocols.ICMP.byteValue(), ICMP.class);
54 protocolClassMap.put(IPProtocols.UDP.byteValue(), UDP.class);
55 protocolClassMap.put(IPProtocols.TCP.byteValue(), TCP.class);
57 private static Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
58 private static final long serialVersionUID = 1L;
60 put(VERSION, new ImmutablePair<Integer, Integer>(0, 4));
61 put(HEADERLENGTH, new ImmutablePair<Integer, Integer>(4, 4));
62 put(DIFFSERV, new ImmutablePair<Integer, Integer>(8, 6));
63 put(ECN, new ImmutablePair<Integer, Integer>(14, 2));
64 put(TOTLENGTH, new ImmutablePair<Integer, Integer>(16, 16));
65 put(IDENTIFICATION, new ImmutablePair<Integer, Integer>(32, 16));
66 put(FLAGS, new ImmutablePair<Integer, Integer>(48, 3));
67 put(FRAGOFFSET, new ImmutablePair<Integer, Integer>(51, 13));
68 put(TTL, new ImmutablePair<Integer, Integer>(64, 8));
69 put(PROTOCOL, new ImmutablePair<Integer, Integer>(72, 8));
70 put(CHECKSUM, new ImmutablePair<Integer, Integer>(80, 16));
71 put(SIP, new ImmutablePair<Integer, Integer>(96, 32));
72 put(DIP, new ImmutablePair<Integer, Integer>(128, 32));
73 put(OPTIONS, new ImmutablePair<Integer, Integer>(160, 0));
77 private final Map<String, byte[]> fieldValues;
81 * Default constructor that sets the version to 4, headerLength to 5,
82 * and flags to 2. The default value for the identification is set to a
83 * random number and the remaining fields are set to 0.
87 fieldValues = new HashMap<String, byte[]>();
88 hdrFieldCoordMap = fieldCoordinates;
89 hdrFieldsMap = fieldValues;
93 setHeaderLength((byte) 5);
94 setDiffServ((byte) 0);
96 setIdentification(generateId());
98 setFragmentOffset((short) 0);
102 * The write access to the packet is set in this constructor.
103 * Constructor that sets the version to 4, headerLength to 5,
104 * and flags to 2. The default value for the identification is set to a
105 * random number and the remaining fields are set to 0.
108 public IPv4(boolean writeAccess) {
110 fieldValues = new HashMap<String, byte[]>();
111 hdrFieldCoordMap = fieldCoordinates;
112 hdrFieldsMap = fieldValues;
115 setVersion((byte) 4);
116 setHeaderLength((byte) 5);
117 setDiffServ((byte) 0);
119 setIdentification(generateId());
121 setFragmentOffset((short) 0);
125 * Gets the IP version stored
126 * @return the version
128 public byte getVersion() {
129 return (BitBufferHelper.getByte(fieldValues.get(VERSION)));
133 * Gets the IP header length stored
134 * @return the headerLength in bytes
136 public int getHeaderLen() {
137 return (4 * BitBufferHelper.getByte(fieldValues.get(HEADERLENGTH)));
141 * Gets the header size in bits
142 * @return The number of bits constituting the header
145 public int getHeaderSize() {
146 int headerLen = this.getHeaderLen();
147 if (headerLen == 0) {
151 byte[] options = hdrFieldsMap.get(OPTIONS);
152 if (options != null) {
153 headerLen += options.length;
156 return headerLen * NetUtils.NumBitsInAByte;
160 * Gets the differential services value stored
161 * @return the diffServ
163 public byte getDiffServ() {
164 return BitBufferHelper.getByte(fieldValues.get(DIFFSERV));
168 * Gets the ecn bits stored
169 * @return the ecn bits
171 public byte getECN() {
172 return BitBufferHelper.getByte(fieldValues.get(ECN));
176 * Gets the total length of the IP header in bytes
177 * @return the totalLength
179 public short getTotalLength() {
180 return (BitBufferHelper.getShort(fieldValues.get(TOTLENGTH)));
184 * Gets the identification value stored
185 * @return the identification
187 public short getIdentification() {
188 return (BitBufferHelper.getShort(fieldValues.get(IDENTIFICATION)));
192 * Gets the flag values stored
195 public byte getFlags() {
196 return (BitBufferHelper.getByte(fieldValues.get(FLAGS)));
200 * Gets the TTL value stored
203 public byte getTtl() {
204 return (BitBufferHelper.getByte(fieldValues.get(TTL)));
208 * Gets the protocol value stored
209 * @return the protocol
211 public byte getProtocol() {
212 return (BitBufferHelper.getByte(fieldValues.get(PROTOCOL)));
216 * Gets the checksum value stored
217 * @return the checksum
219 public short getChecksum() {
220 return (BitBufferHelper.getShort(fieldValues.get(CHECKSUM)));
224 * Gets the fragment offset stored
225 * @return the fragmentOffset
227 public short getFragmentOffset() {
228 return (BitBufferHelper.getShort(fieldValues.get(FRAGOFFSET)));
232 * Gets the source IP address stored
233 * @return the sourceAddress
235 public int getSourceAddress() {
236 return (BitBufferHelper.getInt(fieldValues.get(SIP)));
240 * Gets the destination IP address stored
241 * @return the destinationAddress
243 public int getDestinationAddress() {
244 return (BitBufferHelper.getInt(fieldValues.get(DIP)));
248 * gets the Options stored
249 * @return the options
251 public byte[] getOptions() {
252 return (fieldValues.get(OPTIONS));
257 * Stores the value of fields read from data stream
258 * Variable header value like payload protocol, is stored here
260 public void setHeaderField(String headerField, byte[] readValue) {
261 if (headerField.equals(PROTOCOL)) {
262 payloadClass = protocolClassMap.get(readValue[0]);
264 hdrFieldsMap.put(headerField, readValue);
268 * Stores the IP version from the header
269 * @param version the version to set
272 public IPv4 setVersion(byte ipVersion) {
273 byte[] version = BitBufferHelper.toByteArray(ipVersion);
274 fieldValues.put(VERSION, version);
279 * Stores the length of IP header in words (2 bytes)
280 * @param headerLength the headerLength to set
283 public IPv4 setHeaderLength(byte ipheaderLength) {
284 byte[] headerLength = BitBufferHelper.toByteArray(ipheaderLength);
285 fieldValues.put(HEADERLENGTH, headerLength);
290 * Stores the differential services value from the IP header
291 * @param diffServ the diffServ to set
294 public IPv4 setDiffServ(byte ipdiffServ) {
295 byte[] diffServ = BitBufferHelper.toByteArray(ipdiffServ);
296 fieldValues.put(DIFFSERV, diffServ);
301 * Stores the ECN bits from the header
302 * @param ECN bits to set
305 public IPv4 setECN(byte ecn) {
306 byte[] ecnbytes = BitBufferHelper.toByteArray(ecn);
307 fieldValues.put(ECN, ecnbytes);
312 * Stores the total length of IP header in bytes
313 * @param totalLength the totalLength to set
316 public IPv4 setTotalLength(short iptotalLength) {
317 byte[] totalLength = BitBufferHelper.toByteArray(iptotalLength);
318 fieldValues.put(TOTLENGTH, totalLength);
323 * Stores the identification number from the header
324 * @param identification the identification to set
327 public IPv4 setIdentification(short ipIdentification) {
328 byte[] identification = BitBufferHelper.toByteArray(ipIdentification);
329 fieldValues.put(IDENTIFICATION, identification);
334 * Stores the IP flags value
335 * @param flags the flags to set
338 public IPv4 setFlags(byte ipFlags) {
339 byte[] flags = { ipFlags };
340 fieldValues.put(FLAGS, flags);
345 * Stores the IP fragmentation offset value
346 * @param fragmentOffset the fragmentOffset to set
349 public IPv4 setFragmentOffset(short ipFragmentOffset) {
350 byte[] fragmentOffset = BitBufferHelper.toByteArray(ipFragmentOffset);
351 fieldValues.put(FRAGOFFSET, fragmentOffset);
356 * Stores the TTL value
357 * @param ttl the ttl to set
360 public IPv4 setTtl(byte ipTtl) {
361 byte[] ttl = BitBufferHelper.toByteArray(ipTtl);
362 fieldValues.put(TTL, ttl);
367 * Stores the protocol value of the IP payload
368 * @param protocol the protocol to set
371 public IPv4 setProtocol(byte ipProtocol) {
372 byte[] protocol = BitBufferHelper.toByteArray(ipProtocol);
373 fieldValues.put(PROTOCOL, protocol);
378 * @param checksum the checksum to set
380 /*public IPv4 setChecksum() {
381 short ipChecksum = computeChecksum();
382 byte[] checksum = BitBufferHelper.toByteArray(ipChecksum);
383 fieldValues.put(CHECKSUM, checksum);
388 * Stores the IP source address from the header
389 * @param sourceAddress the sourceAddress to set
392 public IPv4 setSourceAddress(InetAddress ipSourceAddress) {
393 byte[] sourceAddress = ipSourceAddress.getAddress();
394 fieldValues.put(SIP, sourceAddress);
399 * Stores the IP destination address from the header
400 * @param the destination Address to set
403 public IPv4 setDestinationAddress(InetAddress ipDestinationAddress) {
404 byte[] sourceAddress = ipDestinationAddress.getAddress();
405 fieldValues.put(DIP, sourceAddress);
410 * Stores the IP destination address from the header
411 * @param destinationAddress the destinationAddress to set
414 public IPv4 setDestinationAddress(int ipDestinationAddress) {
415 byte[] destinationAddress = BitBufferHelper
416 .toByteArray(ipDestinationAddress);
417 fieldValues.put(DIP, destinationAddress);
422 * Generate a random number to set the Identification field
426 private short generateId() {
427 Random randomgen = new Random();
428 return (short) (randomgen.nextInt(Short.MAX_VALUE + 1));
432 * Store the options from IP header
433 * @param options - byte[]
436 public IPv4 setOptions(byte[] options) {
437 fieldValues.put(OPTIONS, options);
438 byte newIHL = (byte) (5 + options.length);
439 setHeaderLength(newIHL);
445 * Computes the IPv4 header checksum on the passed stream of bytes
446 * representing the packet
451 * The byte offset from where the IPv4 packet starts
452 * @return The computed checksum
454 short computeChecksum(byte[] data, int start) {
455 int end = start + getHeaderLen();
456 short checkSum = (short) 0;
457 int sum = 0, carry = 0, finalSum = 0;
459 int checksumStart = start
460 + (getfieldOffset(CHECKSUM) / NetUtils.NumBitsInAByte);
462 for (int i = start; i <= (end - 1); i = i + 2) {
463 // Skip, if the current bytes are checkSum bytes
464 if (i == checksumStart) {
467 StringBuffer sbuffer = new StringBuffer();
468 sbuffer.append(String.format("%02X", data[i]));
469 if (i < (data.length - 1)) {
470 sbuffer.append(String.format("%02X", data[i + 1]));
473 parsedHex = Integer.valueOf(sbuffer.toString(), 16);
476 carry = (sum >> 16) & 0xFF;
477 finalSum = (sum & 0xFFFF) + carry;
478 checkSum = (short) ~((short) finalSum & 0xFFFF);
485 * Gets the number of bits for the fieldname specified
486 * If the fieldname has variable length like "Options", then this value is computed using the
487 * options length and the header length
488 * @param fieldname - String
489 * @return number of bits for fieldname - int
491 public int getfieldnumBits(String fieldName) {
492 if (fieldName.equals(OPTIONS)) {
493 byte[] options = getOptions();
494 return ((options == null) ? 0 : (options.length - getHeaderLen()));
496 return hdrFieldCoordMap.get(fieldName).getRight();
501 * Method to perform post serialization - like computation of checksum of serialized header
504 * @Exception throws PacketException
506 protected void postSerializeCustomOperation(byte[] data)
507 throws PacketException {
509 // Recompute the total length field here
510 byte[] totalLength = BitBufferHelper.toByteArray((short) data.length);
512 BitBufferHelper.setBytes(data, totalLength, getfieldOffset(TOTLENGTH),
513 getfieldnumBits(TOTLENGTH));
514 } catch (BufferException e) {
515 throw new PacketException(e.getMessage());
518 // Now compute the Header Checksum
519 byte[] checkSum = BitBufferHelper.toByteArray(computeChecksum(data, 0));
522 BitBufferHelper.setBytes(data, checkSum, getfieldOffset(CHECKSUM),
523 getfieldnumBits(CHECKSUM));
524 } catch (BufferException e) {
525 throw new PacketException(e.getMessage());
531 * Stores the payload of IP, serializes it and stores the length of serialized payload
532 * bytes in Total Length
533 * @param payload - Packet
536 * Set the total length field in the IPv4 Object
537 * Note: this field will get overwritten during serialization phase.
539 public void setPayload(Packet payload) {
540 this.payload = payload;
542 * Deriving the Total Length here
544 int payloadLength = 0;
545 if (payload != null) {
547 payloadLength = payload.serialize().length;
548 } catch (PacketException e) {
553 this.setTotalLength((short) (this.getHeaderLen() + payloadLength));
558 * Method to perform post deserialization - like compare computed checksum with
559 * the one obtained from IP header
562 protected void postDeserializeCustomOperation(byte[] data, int startBitOffset) {
563 int start = startBitOffset / NetUtils.NumBitsInAByte;
564 short computedChecksum = computeChecksum(data, start);
565 short actualChecksum = BitBufferHelper.getShort(fieldValues.get(CHECKSUM));
566 if (computedChecksum != actualChecksum) {