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