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