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