Mark potentially-static methods as static
[genius.git] / mdsalutil / mdsalutil-api / src / main / java / org / opendaylight / genius / mdsalutil / packet / IPv4.java
1 /*
2  * Copyright (c) 2013 - 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.genius.mdsalutil.packet;
9
10 import java.net.InetAddress;
11 import java.util.HashMap;
12 import java.util.LinkedHashMap;
13 import java.util.Map;
14 import java.util.Random;
15 import org.apache.commons.lang3.tuple.ImmutablePair;
16 import org.apache.commons.lang3.tuple.Pair;
17 import org.opendaylight.openflowplugin.libraries.liblldp.BitBufferHelper;
18 import org.opendaylight.openflowplugin.libraries.liblldp.BufferException;
19 import org.opendaylight.openflowplugin.libraries.liblldp.Packet;
20 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 /**
25  * Class that represents the IPv4  packet objects.
26  */
27 public class IPv4 extends Packet {
28
29     private static final Logger LOG = LoggerFactory.getLogger(IPv4.class);
30
31     private static final String VERSION = "Version";
32     private static final String HEADERLENGTH = "HeaderLength";
33     private static final String DIFFSERV = "DiffServ";
34     private static final String ECN = "ECN";
35     private static final String TOTLENGTH = "TotalLength";
36     private static final String IDENTIFICATION = "Identification";
37     private static final String FLAGS = "Flags";
38     private static final String FRAGOFFSET = "FragmentOffset";
39     private static final String TTL = "TTL";
40     private static final String PROTOCOL = "Protocol";
41     private static final String CHECKSUM = "Checksum";
42     private static final String SIP = "SourceIPAddress";
43     private static final String DIP = "DestinationIPAddress";
44     private static final String OPTIONS = "Options";
45
46     private static final int UNIT_SIZE_SHIFT = 2;
47     private static final int UNIT_SIZE = 1 << UNIT_SIZE_SHIFT;
48     private static final int MIN_HEADER_SIZE = 20;
49
50     private static final Map<Byte, Class<? extends Packet>> PROTOCOL_CLASS_MAP;
51
52     static {
53         PROTOCOL_CLASS_MAP = new HashMap<>();
54         PROTOCOL_CLASS_MAP.put(IPProtocols.ICMP.byteValue(), ICMP.class);
55         PROTOCOL_CLASS_MAP.put(IPProtocols.UDP.byteValue(), UDP.class);
56         PROTOCOL_CLASS_MAP.put(IPProtocols.TCP.byteValue(), TCP.class);
57     }
58
59     @SuppressWarnings("serial")
60     private static final Map<String, Pair<Integer, Integer>> FIELD_COORDINATES
61         = new LinkedHashMap<String, Pair<Integer, Integer>>() { {
62                 put(VERSION, new ImmutablePair<>(0, 4));
63                 put(HEADERLENGTH, new ImmutablePair<>(4, 4));
64                 put(DIFFSERV, new ImmutablePair<>(8, 6));
65                 put(ECN, new ImmutablePair<>(14, 2));
66                 put(TOTLENGTH, new ImmutablePair<>(16, 16));
67                 put(IDENTIFICATION, new ImmutablePair<>(32, 16));
68                 put(FLAGS, new ImmutablePair<>(48, 3));
69                 put(FRAGOFFSET, new ImmutablePair<>(51, 13));
70                 put(TTL, new ImmutablePair<>(64, 8));
71                 put(PROTOCOL, new ImmutablePair<>(72, 8));
72                 put(CHECKSUM, new ImmutablePair<>(80, 16));
73                 put(SIP, new ImmutablePair<>(96, 32));
74                 put(DIP, new ImmutablePair<>(128, 32));
75                 put(OPTIONS, new ImmutablePair<>(160, 0));
76             }
77         };
78
79     private final Map<String, byte[]> fieldValues;
80
81
82     /**
83      * Default constructor that sets the version to 4, headerLength to 5,
84      * and flags to 2.  The default value for the identification is set to a
85      * random number and the remaining fields are set to 0.
86      */
87     public IPv4() {
88         fieldValues = new HashMap<>();
89         hdrFieldCoordMap = FIELD_COORDINATES;
90         hdrFieldsMap = fieldValues;
91         corrupted = false;
92
93         setVersion((byte) 4);
94         setHeaderLength((byte) 5);
95         setDiffServ((byte) 0);
96         setECN((byte) 0);
97         setIdentification(generateId());
98         setFlags((byte) 2);
99         setFragmentOffset((short) 0);
100     }
101
102     /**
103      * The write access to the packet is set in this constructor.
104      * Constructor that sets the version to 4, headerLength to 5,
105      * and flags to 2.  The default value for the identification is set to a
106      * random number and the remaining fields are set to 0.
107      * @param writeAccess - boolean
108      */
109     public IPv4(boolean writeAccess) {
110         super(writeAccess);
111         fieldValues = new HashMap<>();
112         hdrFieldCoordMap = FIELD_COORDINATES;
113         hdrFieldsMap = fieldValues;
114         corrupted = false;
115
116         setVersion((byte) 4);
117         setHeaderLength((byte) 5);
118         setDiffServ((byte) 0);
119         setECN((byte) 0);
120         setIdentification(generateId());
121         setFlags((byte) 2);
122         setFragmentOffset((short) 0);
123     }
124
125     /**
126      * Gets the IP version stored.
127      * @return the version
128      */
129     public byte getVersion() {
130         return BitBufferHelper.getByte(fieldValues.get(VERSION));
131     }
132
133     /**
134      * Gets the IP header length stored.
135      * @return the headerLength in bytes
136      */
137     public int getHeaderLen() {
138         return 4 * BitBufferHelper.getByte(fieldValues.get(HEADERLENGTH));
139     }
140
141     /**
142      * Gets the header size in bits.
143      * @return The number of bits constituting the header
144      */
145     @Override
146     public int getHeaderSize() {
147         int headerLen = this.getHeaderLen();
148         if (headerLen == 0) {
149             headerLen = MIN_HEADER_SIZE;
150         }
151
152         return headerLen * Byte.SIZE;
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      * @param headerField
256      * @param readValue
257      */
258     public void setHeaderField(String headerField, byte[] readValue) {
259         if (headerField.equals(PROTOCOL)) {
260             // Don't set payloadClass if framgment offset is not zero.
261             byte[] fragoff = hdrFieldsMap.get(FRAGOFFSET);
262             if (fragoff == null || BitBufferHelper.getShort(fragoff) == 0) {
263                 payloadClass = PROTOCOL_CLASS_MAP.get(readValue[0]);
264             }
265         } else if (headerField.equals(FRAGOFFSET)) {
266             if (readValue != null && BitBufferHelper.getShort(readValue) != 0) {
267                 // Clear payloadClass because protocol header is not present
268                 // in this packet.
269                 payloadClass = null;
270             }
271         } else if (headerField.equals(OPTIONS)
272                    && (readValue == null || readValue.length == 0)) {
273             hdrFieldsMap.remove(headerField);
274             return;
275         }
276         hdrFieldsMap.put(headerField, readValue);
277     }
278
279     /**
280      * Stores the IP version from the header.
281      * @param ipVersion the version to set
282      * @return IPv4
283      */
284     public IPv4 setVersion(byte ipVersion) {
285         byte[] version = BitBufferHelper.toByteArray(ipVersion);
286         fieldValues.put(VERSION, version);
287         return this;
288     }
289
290     /**
291      * Stores the length of IP header in words (4 bytes).
292      * @param ipheaderLength the headerLength to set
293      * @return IPv4
294      */
295     public IPv4 setHeaderLength(byte ipheaderLength) {
296         byte[] headerLength = BitBufferHelper.toByteArray(ipheaderLength);
297         fieldValues.put(HEADERLENGTH, headerLength);
298         return this;
299     }
300
301     /**
302      * Stores the differential services value from the IP header.
303      * @param ipdiffServ the diffServ to set
304      * @return IPv4
305      */
306     public IPv4 setDiffServ(byte ipdiffServ) {
307         byte[] diffServ = BitBufferHelper.toByteArray(ipdiffServ);
308         fieldValues.put(DIFFSERV, diffServ);
309         return this;
310     }
311
312     /**
313      * Stores the ECN bits from the header.
314      * @param ecn ECN bits to set
315      * @return IPv4
316      */
317     public IPv4 setECN(byte ecn) {
318         byte[] ecnbytes = BitBufferHelper.toByteArray(ecn);
319         fieldValues.put(ECN, ecnbytes);
320         return this;
321     }
322
323     /**
324      * Stores the total length of IP header in bytes.
325      * @param iptotalLength the totalLength to set
326      * @return IPv4
327      */
328     public IPv4 setTotalLength(short iptotalLength) {
329         byte[] totalLength = BitBufferHelper.toByteArray(iptotalLength);
330         fieldValues.put(TOTLENGTH, totalLength);
331         return this;
332     }
333
334     /**
335      * Stores the identification number from the header.
336      * @param ipIdentification the identification to set
337      * @return IPv4
338      */
339     public IPv4 setIdentification(short ipIdentification) {
340         byte[] identification = BitBufferHelper.toByteArray(ipIdentification);
341         fieldValues.put(IDENTIFICATION, identification);
342         return this;
343     }
344
345     /**
346      * Stores the IP flags value.
347      * @param ipFlags the flags to set
348      * @return IPv4
349      */
350     public IPv4 setFlags(byte ipFlags) {
351         byte[] flags = { ipFlags };
352         fieldValues.put(FLAGS, flags);
353         return this;
354     }
355
356     /**
357      * Stores the IP fragmentation offset value.
358      * @param ipFragmentOffset the fragmentOffset to set
359      * @return IPv4
360      */
361     public IPv4 setFragmentOffset(short ipFragmentOffset) {
362         byte[] fragmentOffset = BitBufferHelper.toByteArray(ipFragmentOffset);
363         fieldValues.put(FRAGOFFSET, fragmentOffset);
364         return this;
365     }
366
367     /**
368      * Stores the TTL value.
369      * @param ipTtl the ttl to set
370      * @return IPv4
371      */
372     public IPv4 setTtl(byte ipTtl) {
373         byte[] ttl = BitBufferHelper.toByteArray(ipTtl);
374         fieldValues.put(TTL, ttl);
375         return this;
376     }
377
378     /**
379      * Stores the protocol value of the IP payload.
380      * @param ipProtocol the protocol to set
381      * @return IPv4
382      */
383     public IPv4 setProtocol(byte ipProtocol) {
384         byte[] protocol = BitBufferHelper.toByteArray(ipProtocol);
385         fieldValues.put(PROTOCOL, protocol);
386         return this;
387     }
388
389     /*
390      * @param checksum the checksum to set
391      * /
392     public IPv4 setChecksum() {
393         short ipChecksum = computeChecksum();
394         byte[] checksum = BitBufferHelper.toByteArray(ipChecksum);
395         fieldValues.put(CHECKSUM, checksum);
396         return this;
397     }*/
398
399     /**
400      * Stores the IP source address from the header.
401      * @param ipSourceAddress the sourceAddress to set
402      * @return IPv4
403      */
404     public IPv4 setSourceAddress(InetAddress ipSourceAddress) {
405         byte[] sourceAddress = ipSourceAddress.getAddress();
406         fieldValues.put(SIP, sourceAddress);
407         return this;
408     }
409
410     /**
411      * Stores the IP destination address from the header.
412      * @param ipDestinationAddress the destination Address to set
413      * @return IPv4
414      */
415     public IPv4 setDestinationAddress(InetAddress ipDestinationAddress) {
416         byte[] sourceAddress = ipDestinationAddress.getAddress();
417         fieldValues.put(DIP, sourceAddress);
418         return this;
419     }
420
421     /**
422      * Stores the IP destination address from the header.
423      * @param ipDestinationAddress the destinationAddress to set
424      * @return IPv4
425      */
426     public IPv4 setDestinationAddress(int ipDestinationAddress) {
427         byte[] destinationAddress = BitBufferHelper
428                 .toByteArray(ipDestinationAddress);
429         fieldValues.put(DIP, destinationAddress);
430         return this;
431     }
432
433     /**
434      * Generate a random number to set the Identification field
435      * in IPv4 Header.
436      * @return short
437      */
438     private static short generateId() {
439         Random randomgen = new Random();
440         return (short) randomgen.nextInt(Short.MAX_VALUE + 1);
441     }
442
443     /**
444      * Store the options from IP header.
445      * @param options - byte[]
446      * @return IPv4
447      */
448     public IPv4 setOptions(byte[] options) {
449         byte newIHL = (byte)(MIN_HEADER_SIZE >>> UNIT_SIZE_SHIFT);
450         if (options == null || options.length == 0) {
451             fieldValues.remove(OPTIONS);
452         } else {
453             int len = options.length;
454             int rlen = len + UNIT_SIZE - 1 & ~(UNIT_SIZE - 1);
455             if (rlen > len) {
456                 // Padding is required.
457                 byte[] newopt = new byte[rlen];
458                 System.arraycopy(options, 0, newopt, 0, len);
459                 options = newopt;
460                 len = rlen;
461             }
462             fieldValues.put(OPTIONS, options);
463             newIHL += len >>> UNIT_SIZE_SHIFT;
464         }
465
466         setHeaderLength(newIHL);
467
468         return this;
469     }
470
471     /**
472      * Computes the IPv4 header checksum on the passed stream of bytes
473      * representing the packet.
474      *
475      * @param data
476      *            The byte stream
477      * @param start
478      *            The byte offset from where the IPv4 packet starts
479      * @return The computed checksum
480      */
481     short computeChecksum(byte[] data, int start) {
482         int end = start + getHeaderLen();
483         short checkSum;
484         int sum = 0;
485         int wordData;
486         int checksumStart = start + getfieldOffset(CHECKSUM) / Byte.SIZE;
487
488         for (int i = start; i <= end - 1; i = i + 2) {
489             // Skip, if the current bytes are checkSum bytes
490             if (i == checksumStart) {
491                 continue;
492             }
493             wordData = (data[i] << 8 & 0xFF00) + (data[i + 1] & 0xFF);
494             sum = sum + wordData;
495         }
496         int carry = sum >> 16 & 0xFF;
497         int finalSum = (sum & 0xFFFF) + carry;
498         checkSum = (short) ~((short) finalSum & 0xFFFF);
499
500         return checkSum;
501     }
502
503     @Override
504     /**
505      * Gets the number of bits for the fieldname specified
506      * If the fieldname has variable length like "Options", then this value is computed using the header length
507      * @param fieldname - String
508      * @return number of bits for fieldname - int
509      */
510     public int getfieldnumBits(String fieldName) {
511         if (fieldName.equals(OPTIONS)) {
512             return (getHeaderLen() - MIN_HEADER_SIZE) * Byte.SIZE;
513         }
514         return hdrFieldCoordMap.get(fieldName).getRight();
515     }
516
517     @Override
518     /**
519      * Method to perform post serialization - like computation of checksum of serialized header
520      * @param data
521      * @return void
522      * @Exception throws PacketException
523      */
524     protected void postSerializeCustomOperation(byte[] data)
525             throws PacketException {
526
527         // Recompute the total length field here
528         byte[] totalLength = BitBufferHelper.toByteArray((short) data.length);
529         try {
530             BitBufferHelper.setBytes(data, totalLength, getfieldOffset(TOTLENGTH),
531                     getfieldnumBits(TOTLENGTH));
532         } catch (BufferException e) {
533             throw new PacketException(e.getMessage(), e);
534         }
535
536         // Now compute the Header Checksum
537         byte[] checkSum = BitBufferHelper.toByteArray(computeChecksum(data, 0));
538
539         try {
540             BitBufferHelper.setBytes(data, checkSum, getfieldOffset(CHECKSUM),
541                     getfieldnumBits(CHECKSUM));
542         } catch (BufferException e) {
543             throw new PacketException(e.getMessage(), e);
544         }
545     }
546
547     @Override
548     /**
549      * Stores the payload of IP, serializes it and stores the length of serialized payload
550      * bytes in Total Length
551      * @param payload - Packet
552      */
553     /**
554      * Set the total length field in the IPv4 Object
555      * Note: this field will get overwritten during serialization phase.
556      */
557     public void setPayload(Packet payload) {
558         this.payload = payload;
559         /*
560          * Deriving the Total Length here
561          */
562         int payloadLength = 0;
563         if (payload != null) {
564             try {
565                 payloadLength = payload.serialize().length;
566             } catch (PacketException e) {
567                 LOG.error("Exception in setpayload", e);
568             }
569         }
570
571         this.setTotalLength((short) (this.getHeaderLen() + payloadLength));
572     }
573
574
575     /**
576      * Method to perform post deserialization - like compare computed checksum with
577      * the one obtained from IP header.
578      */
579     @Override
580     protected void postDeserializeCustomOperation(byte[] data, int startBitOffset) {
581         int start = startBitOffset / Byte.SIZE;
582         short computedChecksum = computeChecksum(data, start);
583         short actualChecksum = BitBufferHelper.getShort(fieldValues.get(CHECKSUM));
584         if (computedChecksum != actualChecksum) {
585             corrupted = true;
586         }
587     }
588
589 }