Fix NPE in ICMP.computeChecksum()
[controller.git] / opendaylight / sal / api / src / main / java / org / opendaylight / controller / sal / packet / ICMP.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 package org.opendaylight.controller.sal.packet;
11
12 import java.util.HashMap;
13 import java.util.LinkedHashMap;
14 import java.util.Map;
15
16 import org.apache.commons.lang3.tuple.ImmutablePair;
17 import org.apache.commons.lang3.tuple.Pair;
18 import org.opendaylight.controller.sal.utils.NetUtils;
19
20 /**
21  * Class that represents the ICMP packet objects
22  */
23
24 public class ICMP extends Packet {
25     private static final String TYPE = "Type";
26     private static final String CODE = "Code";
27     private static final String CHECKSUM = "Checksum";
28     private static final String IDENTIFIER = "Identifier";
29     private static final String SEQNUMBER = "SequenceNumber";
30
31     private static Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
32         private static final long serialVersionUID = 1L;
33         {
34             put(TYPE, new ImmutablePair<Integer, Integer>(0, 8));
35             put(CODE, new ImmutablePair<Integer, Integer>(8, 8));
36             put(CHECKSUM, new ImmutablePair<Integer, Integer>(16, 16));
37             put(IDENTIFIER, new ImmutablePair<Integer, Integer>(32, 16));
38             put(SEQNUMBER, new ImmutablePair<Integer, Integer>(48, 16));
39         }
40     };
41
42     /**
43      * Default constructor that creates and sets the hash map values
44      */
45     public ICMP() {
46         super();
47         fieldValues = new HashMap<String, byte[]>();
48         hdrFieldCoordMap = fieldCoordinates;
49         hdrFieldsMap = fieldValues;
50     }
51
52     /**
53      * Constructor that sets the access level for the packet
54      */
55     public ICMP(boolean writeAccess) {
56         super(writeAccess);
57         fieldValues = new HashMap<String, byte[]>();
58         hdrFieldCoordMap = fieldCoordinates;
59         hdrFieldsMap = fieldValues;
60     }
61
62     private final Map<String, byte[]> fieldValues;
63
64     @Override
65     public void setHeaderField(String headerField, byte[] readValue) {
66         hdrFieldsMap.put(headerField, readValue);
67     }
68
69     /**
70      * Sets the type for the current ICMP message
71      *
72      * @param type
73      *            The ICMP message type
74      * @return This ICMP object
75      */
76     public ICMP setType(byte type) {
77         byte[] icmpType = BitBufferHelper.toByteArray(type);
78         fieldValues.put(TYPE, icmpType);
79         return this;
80     }
81
82     /**
83      * Sets the ICMP code (type subtype) for the current ICMP object instance
84      *
85      * @param code
86      *            The ICMP message type subtype
87      * @return This ICMP object
88      */
89     public ICMP setCode(byte code) {
90         byte[] icmpCode = BitBufferHelper.toByteArray(code);
91         fieldValues.put(CODE, icmpCode);
92         return this;
93     }
94
95     /**
96      * Sets the ICMP checksum  for the current ICMP object instance
97      * @param short - checksum
98      * @return ICMP
99      */
100     public ICMP setChecksum(short checksum) {
101         byte[] icmpChecksum = BitBufferHelper.toByteArray(checksum);
102         fieldValues.put(CHECKSUM, icmpChecksum);
103         return this;
104     }
105
106     /**
107      * Sets the ICMP identifier  for the current ICMP object instance
108      * @param short - identifier
109      * @return ICMP
110      */
111     public ICMP setIdentifier(short identifier) {
112         byte[] icmpIdentifier = BitBufferHelper.toByteArray(identifier);
113         fieldValues.put(IDENTIFIER, icmpIdentifier);
114         return this;
115     }
116
117     /**
118      * Sets the ICMP sequence number for the current ICMP object instance
119      * @param short - seqNumber
120      * @return ICMP
121      */
122     public ICMP setSequenceNumber(short seqNumber) {
123         byte[] icmpSeqNumber = BitBufferHelper.toByteArray(seqNumber);
124         fieldValues.put(SEQNUMBER, icmpSeqNumber);
125         return this;
126     }
127
128     /**
129      * Gets the header size in bits
130      * @return The ICMP header size in bits
131      */
132     @Override
133     public int getHeaderSize() {
134         return 64;
135     }
136
137     /**
138      * Computes the ICMP checksum on the serialized ICMP message
139      *
140      * @param serialized
141      *            The data stream
142      * @param start
143      *            The byte index on the data stream from which the ICMP packet
144      *            starts
145      * @return The checksum
146      */
147     short computeChecksum(byte[] data, int start) {
148         int sum = 0, carry = 0, finalSum = 0;
149         int wordData;
150         int end = start + this.getHeaderSize() / NetUtils.NumBitsInAByte;
151         if (rawPayload != null) {
152             end += rawPayload.length;
153         }
154         int checksumStartByte = start + getfieldOffset(CHECKSUM) / NetUtils.NumBitsInAByte;
155
156         for (int i = start; i <= (end - 1); i = i + 2) {
157             // Skip, if the current bytes are checkSum bytes
158             if (i == checksumStartByte) {
159                 continue;
160             }
161             wordData = ((data[i] << 8) & 0xFF00) + (data[i + 1] & 0xFF);
162             sum = sum + wordData;
163         }
164         carry = (sum >> 16) & 0xFF;
165         finalSum = (sum & 0xFFFF) + carry;
166         return (short) ~((short) finalSum & 0xFFFF);
167     }
168
169     @Override
170     protected void postSerializeCustomOperation(byte[] serializedBytes)
171             throws PacketException {
172         byte[] checkSum = BitBufferHelper
173                 .toByteArray(computeChecksum(serializedBytes, 0));
174         try {
175             BitBufferHelper.setBytes(serializedBytes, checkSum,
176                     getfieldOffset(CHECKSUM), getfieldnumBits(CHECKSUM));
177         } catch (BufferException e) {
178             throw new PacketException(e.getMessage());
179         }
180     }
181
182     @Override
183     protected void postDeserializeCustomOperation(byte[] data, int endBitOffset) {
184         short computedChecksum = computeChecksum(data, endBitOffset / NetUtils.NumBitsInAByte);
185         short actualChecksum = BitBufferHelper.getShort(fieldValues.get(CHECKSUM));
186
187         if (computedChecksum != actualChecksum) {
188             corrupted = true;
189         }
190     }
191
192     /**
193      * Gets the checksum value stored
194      * @return the checksum
195      */
196     public short getChecksum() {
197         return (BitBufferHelper.getShort(fieldValues.get(CHECKSUM)));
198     }
199 }