Bug 1245: Dropped Binding prefix from Binding Data APIs.
[controller.git] / opendaylight / sal / api / src / main / java / org / opendaylight / controller / sal / packet / IPv4.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
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
8  */
9
10 /**
11  *
12  */
13 package org.opendaylight.controller.sal.packet;
14
15 import java.net.InetAddress;
16 import java.util.HashMap;
17 import java.util.LinkedHashMap;
18 import java.util.Map;
19 import java.util.Random;
20
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;
27
28 /**
29  * Class that represents the IPv4  packet objects
30  */
31
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";
49
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;
53
54     public static final Map<Byte, Class<? extends Packet>> protocolClassMap;
55     static {
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);
60     }
61     private static Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
62         private static final long serialVersionUID = 1L;
63         {
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));
78         }
79     };
80
81     private final Map<String, byte[]> fieldValues;
82
83
84     /**
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.
88      */
89     public IPv4() {
90         super();
91         fieldValues = new HashMap<String, byte[]>();
92         hdrFieldCoordMap = fieldCoordinates;
93         hdrFieldsMap = fieldValues;
94         corrupted = false;
95
96         setVersion((byte) 4);
97         setHeaderLength((byte) 5);
98         setDiffServ((byte) 0);
99         setECN((byte) 0);
100         setIdentification(generateId());
101         setFlags((byte) 2);
102         setFragmentOffset((short) 0);
103     }
104
105     /**
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.
110      * @param boolean
111      */
112     public IPv4(boolean writeAccess) {
113         super(writeAccess);
114         fieldValues = new HashMap<String, byte[]>();
115         hdrFieldCoordMap = fieldCoordinates;
116         hdrFieldsMap = fieldValues;
117         corrupted = false;
118
119         setVersion((byte) 4);
120         setHeaderLength((byte) 5);
121         setDiffServ((byte) 0);
122         setECN((byte) 0);
123         setIdentification(generateId());
124         setFlags((byte) 2);
125         setFragmentOffset((short) 0);
126     }
127
128     /**
129      * Gets the IP version stored
130      * @return the version
131      */
132     public byte getVersion() {
133         return (BitBufferHelper.getByte(fieldValues.get(VERSION)));
134     }
135
136     /**
137      * Gets the IP header length stored
138      * @return the headerLength in bytes
139      */
140     public int getHeaderLen() {
141         return (4 * BitBufferHelper.getByte(fieldValues.get(HEADERLENGTH)));
142     }
143
144     /**
145      * Gets the header size in bits
146      * @return The number of bits constituting the header
147      */
148     @Override
149     public int getHeaderSize() {
150         int headerLen = this.getHeaderLen();
151         if (headerLen == 0) {
152             headerLen = MIN_HEADER_SIZE;
153         }
154
155         return headerLen * NetUtils.NumBitsInAByte;
156     }
157
158     /**
159      * Gets the differential services value stored
160      * @return the diffServ
161      */
162     public byte getDiffServ() {
163         return BitBufferHelper.getByte(fieldValues.get(DIFFSERV));
164     }
165
166     /**
167      * Gets the ecn bits stored
168      * @return the ecn bits
169      */
170     public byte getECN() {
171         return BitBufferHelper.getByte(fieldValues.get(ECN));
172     }
173
174     /**
175      * Gets the total length of the IP header in bytes
176      * @return the totalLength
177      */
178     public short getTotalLength() {
179         return (BitBufferHelper.getShort(fieldValues.get(TOTLENGTH)));
180     }
181
182     /**
183      * Gets the identification value stored
184      * @return the identification
185      */
186     public short getIdentification() {
187         return (BitBufferHelper.getShort(fieldValues.get(IDENTIFICATION)));
188     }
189
190     /**
191      * Gets the flag values stored
192      * @return the flags
193      */
194     public byte getFlags() {
195         return (BitBufferHelper.getByte(fieldValues.get(FLAGS)));
196     }
197
198     /**
199      * Gets the TTL value stored
200      * @return the ttl
201      */
202     public byte getTtl() {
203         return (BitBufferHelper.getByte(fieldValues.get(TTL)));
204     }
205
206     /**
207      * Gets the protocol value stored
208      * @return the protocol
209      */
210     public byte getProtocol() {
211         return (BitBufferHelper.getByte(fieldValues.get(PROTOCOL)));
212     }
213
214     /**
215      * Gets the checksum value stored
216      * @return the checksum
217      */
218     public short getChecksum() {
219         return (BitBufferHelper.getShort(fieldValues.get(CHECKSUM)));
220     }
221
222     /**
223      * Gets the fragment offset stored
224      * @return the fragmentOffset
225      */
226     public short getFragmentOffset() {
227         return (BitBufferHelper.getShort(fieldValues.get(FRAGOFFSET)));
228     }
229
230     /**
231      * Gets the source IP address stored
232      * @return the sourceAddress
233      */
234     public int getSourceAddress() {
235         return (BitBufferHelper.getInt(fieldValues.get(SIP)));
236     }
237
238     /**
239      * Gets the destination IP address stored
240      * @return the destinationAddress
241      */
242     public int getDestinationAddress() {
243         return (BitBufferHelper.getInt(fieldValues.get(DIP)));
244     }
245
246     /**
247      * gets the Options stored
248      * @return the options
249      */
250     public byte[] getOptions() {
251         return (fieldValues.get(OPTIONS));
252     }
253
254     @Override
255     /**
256      * Stores the value of fields read from data stream
257      * Variable header value like payload protocol, is stored here
258      */
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);
265             return;
266         }
267         hdrFieldsMap.put(headerField, readValue);
268     }
269
270     /**
271      * Stores the IP version from the header
272      * @param version the version to set
273      * @return @IPv4
274      */
275     public IPv4 setVersion(byte ipVersion) {
276         byte[] version = BitBufferHelper.toByteArray(ipVersion);
277         fieldValues.put(VERSION, version);
278         return this;
279     }
280
281     /**
282      * Stores the length of IP header in words (4 bytes)
283      * @param headerLength the headerLength to set
284      * @return IPv4
285      */
286     public IPv4 setHeaderLength(byte ipheaderLength) {
287         byte[] headerLength = BitBufferHelper.toByteArray(ipheaderLength);
288         fieldValues.put(HEADERLENGTH, headerLength);
289         return this;
290     }
291
292     /**
293      * Stores the differential services value from the IP header
294      * @param diffServ the diffServ to set
295      * @return IPv4
296      */
297     public IPv4 setDiffServ(byte ipdiffServ) {
298         byte[] diffServ = BitBufferHelper.toByteArray(ipdiffServ);
299         fieldValues.put(DIFFSERV, diffServ);
300         return this;
301     }
302
303     /**
304      * Stores the ECN bits from the header
305      * @param ECN bits to set
306      * @return IPv4
307      */
308     public IPv4 setECN(byte ecn) {
309         byte[] ecnbytes = BitBufferHelper.toByteArray(ecn);
310         fieldValues.put(ECN, ecnbytes);
311         return this;
312     }
313
314     /**
315      * Stores the total length of IP header in bytes
316      * @param totalLength the totalLength to set
317      * @return IPv4
318      */
319     public IPv4 setTotalLength(short iptotalLength) {
320         byte[] totalLength = BitBufferHelper.toByteArray(iptotalLength);
321         fieldValues.put(TOTLENGTH, totalLength);
322         return this;
323     }
324
325     /**
326      * Stores the identification number from the header
327      * @param identification the identification to set
328      * @return IPv4
329      */
330     public IPv4 setIdentification(short ipIdentification) {
331         byte[] identification = BitBufferHelper.toByteArray(ipIdentification);
332         fieldValues.put(IDENTIFICATION, identification);
333         return this;
334     }
335
336     /**
337      * Stores the IP flags value
338      * @param flags the flags to set
339      * @return IPv4
340      */
341     public IPv4 setFlags(byte ipFlags) {
342         byte[] flags = { ipFlags };
343         fieldValues.put(FLAGS, flags);
344         return this;
345     }
346
347     /**
348      * Stores the IP fragmentation offset value
349      * @param fragmentOffset the fragmentOffset to set
350      * @return IPv4
351      */
352     public IPv4 setFragmentOffset(short ipFragmentOffset) {
353         byte[] fragmentOffset = BitBufferHelper.toByteArray(ipFragmentOffset);
354         fieldValues.put(FRAGOFFSET, fragmentOffset);
355         return this;
356     }
357
358     /**
359      * Stores the TTL value
360      * @param ttl the ttl to set
361      * @return IPv4
362      */
363     public IPv4 setTtl(byte ipTtl) {
364         byte[] ttl = BitBufferHelper.toByteArray(ipTtl);
365         fieldValues.put(TTL, ttl);
366         return this;
367     }
368
369     /**
370      * Stores the protocol value of the IP payload
371      * @param protocol the protocol to set
372      * @return IPv4
373      */
374     public IPv4 setProtocol(byte ipProtocol) {
375         byte[] protocol = BitBufferHelper.toByteArray(ipProtocol);
376         fieldValues.put(PROTOCOL, protocol);
377         return this;
378     }
379
380     /**
381      * @param checksum the checksum to set
382      */
383     /*public IPv4 setChecksum() {
384         short ipChecksum = computeChecksum();
385         byte[] checksum = BitBufferHelper.toByteArray(ipChecksum);
386         fieldValues.put(CHECKSUM, checksum);
387         return this;
388     }*/
389
390     /**
391      * Stores the IP source address from the header
392      * @param sourceAddress the sourceAddress to set
393      * @return IPv4
394      */
395     public IPv4 setSourceAddress(InetAddress ipSourceAddress) {
396         byte[] sourceAddress = ipSourceAddress.getAddress();
397         fieldValues.put(SIP, sourceAddress);
398         return this;
399     }
400
401     /**
402      * Stores the IP destination address from the header
403      * @param the destination Address to set
404      * @return IPv4
405      */
406     public IPv4 setDestinationAddress(InetAddress ipDestinationAddress) {
407         byte[] sourceAddress = ipDestinationAddress.getAddress();
408         fieldValues.put(DIP, sourceAddress);
409         return this;
410     }
411
412     /**
413      * Stores the IP destination address from the header
414      * @param destinationAddress the destinationAddress to set
415      * @return IPv4
416      */
417     public IPv4 setDestinationAddress(int ipDestinationAddress) {
418         byte[] destinationAddress = BitBufferHelper
419                 .toByteArray(ipDestinationAddress);
420         fieldValues.put(DIP, destinationAddress);
421         return this;
422     }
423
424     /**
425      * Generate a random number to set the Identification field
426      * in IPv4 Header
427      * @return short
428      */
429     private short generateId() {
430         Random randomgen = new Random();
431         return (short) (randomgen.nextInt(Short.MAX_VALUE + 1));
432     }
433
434     /**
435      * Store the options from IP header
436      * @param options - byte[]
437      * @return IPv4
438      */
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);
443         } else {
444             int len = options.length;
445             int rlen = (len + (UNIT_SIZE - 1)) & ~(UNIT_SIZE - 1);
446             if (rlen > len) {
447                 // Padding is required.
448                 byte[] newopt = new byte[rlen];
449                 System.arraycopy(options, 0, newopt, 0, len);
450                 options = newopt;
451                 len = rlen;
452             }
453             fieldValues.put(OPTIONS, options);
454             newIHL += (len >>> UNIT_SIZE_SHIFT);
455         }
456
457         setHeaderLength(newIHL);
458
459         return this;
460     }
461
462     /**
463      * Computes the IPv4 header checksum on the passed stream of bytes
464      * representing the packet
465      *
466      * @param data
467      *            The byte stream
468      * @param offset
469      *            The byte offset from where the IPv4 packet starts
470      * @return The computed checksum
471      */
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;
476         int wordData;
477         int checksumStart = start
478                 + (getfieldOffset(CHECKSUM) / NetUtils.NumBitsInAByte);
479
480         for (int i = start; i <= (end - 1); i = i + 2) {
481             // Skip, if the current bytes are checkSum bytes
482             if (i == checksumStart) {
483                 continue;
484             }
485             wordData = ((data[i] << 8) & 0xFF00) + (data[i + 1] & 0xFF);
486             sum = sum + wordData;
487         }
488         carry = (sum >> 16) & 0xFF;
489         finalSum = (sum & 0xFFFF) + carry;
490         checkSum = (short) ~((short) finalSum & 0xFFFF);
491
492         return checkSum;
493     }
494
495     @Override
496     /**
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
501      */
502     public int getfieldnumBits(String fieldName) {
503         if (fieldName.equals(OPTIONS)) {
504             return (getHeaderLen() - MIN_HEADER_SIZE) * NetUtils.NumBitsInAByte;
505         }
506         return hdrFieldCoordMap.get(fieldName).getRight();
507     }
508
509     @Override
510     /**
511      * Method to perform post serialization - like computation of checksum of serialized header
512      * @param data
513      * @return void
514      * @Exception throws PacketException
515      */
516     protected void postSerializeCustomOperation(byte[] data)
517             throws PacketException {
518
519         // Recompute the total length field here
520         byte[] totalLength = BitBufferHelper.toByteArray((short) data.length);
521         try {
522             BitBufferHelper.setBytes(data, totalLength, getfieldOffset(TOTLENGTH),
523                     getfieldnumBits(TOTLENGTH));
524         } catch (BufferException e) {
525             throw new PacketException(e.getMessage());
526         }
527
528         // Now compute the Header Checksum
529         byte[] checkSum = BitBufferHelper.toByteArray(computeChecksum(data, 0));
530
531         try {
532             BitBufferHelper.setBytes(data, checkSum, getfieldOffset(CHECKSUM),
533                     getfieldnumBits(CHECKSUM));
534         } catch (BufferException e) {
535             throw new PacketException(e.getMessage());
536         }
537     }
538
539     @Override
540     /**
541      * Stores the payload of IP, serializes it and stores the length of serialized payload
542      * bytes in Total Length
543      * @param payload - Packet
544      */
545     /**
546      * Set the total length field in the IPv4 Object
547      * Note: this field will get overwritten during serialization phase.
548      */
549     public void setPayload(Packet payload) {
550         this.payload = payload;
551         /*
552          * Deriving the Total Length here
553          */
554         int payloadLength = 0;
555         if (payload != null) {
556             try {
557                 payloadLength = payload.serialize().length;
558             } catch (PacketException e) {
559                 logger.error("", e);
560             }
561         }
562
563         this.setTotalLength((short) (this.getHeaderLen() + payloadLength));
564     }
565
566
567     /**
568      * Method to perform post deserialization - like compare computed checksum with
569      * the one obtained from IP header
570      */
571     @Override
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) {
577             corrupted = true;
578         }
579     }
580 }