OPNFLWPLUG-1032: Neon-MRI: Bump odlparent, yangtools, mdsal
[openflowplugin.git] / libraries / liblldp / src / main / java / org / opendaylight / openflowplugin / libraries / liblldp / Packet.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.openflowplugin.libraries.liblldp;
9
10 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
11 import java.util.Arrays;
12 import java.util.Map;
13 import java.util.Map.Entry;
14 import org.apache.commons.lang3.tuple.Pair;
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17
18 /**
19  * Abstract class which represents the generic network packet object. It provides
20  * the basic methods which are common for all the packets, like serialize and
21  * deserialize.
22  */
23 public abstract class Packet {
24
25     private static final Logger LOG = LoggerFactory.getLogger(Packet.class);
26
27     // Access level granted to this packet
28     protected boolean writeAccess;
29
30     // When deserialized from wire, packet could result corrupted
31     protected boolean corrupted;
32
33     // The packet that encapsulate this packet
34     protected Packet parent;
35
36     // The packet encapsulated by this packet
37     protected Packet payload;
38
39     // The unparsed raw payload carried by this packet
40     protected byte[] rawPayload;
41
42     // Bit coordinates of packet header fields
43     protected Map<String, Pair<Integer, Integer>> hdrFieldCoordMap;
44
45     // Header fields values: Map<FieldName,Value>
46     protected Map<String, byte[]> hdrFieldsMap;
47
48     // The class of the encapsulated packet object
49     protected Class<? extends Packet> payloadClass;
50
51     public Packet() {
52         writeAccess = false;
53         corrupted = false;
54     }
55
56     public Packet(final boolean writeAccess) {
57         this.writeAccess = writeAccess;
58         corrupted = false;
59     }
60
61     public Packet getParent() {
62         return parent;
63     }
64
65     public Packet getPayload() {
66         return payload;
67     }
68
69     public void setParent(final Packet parent) {
70         this.parent = parent;
71     }
72
73     public void setPayload(final Packet payload) {
74         this.payload = payload;
75     }
76
77     public void setHeaderField(final String headerField, final byte[] readValue) {
78         hdrFieldsMap.put(headerField, readValue);
79     }
80
81     /**
82      * This method deserializes the data bits obtained from the wire into the
83      * respective header and payload which are of type Packet.
84      *
85      * @param data - data from wire to deserialize
86      * @param bitOffset bit position where packet header starts in data
87      *        array
88      * @param size size of packet in bits
89      * @return Packet
90      * @throws PacketException if deserialization fails
91      */
92     public Packet deserialize(final byte[] data, final int bitOffset, final int size)
93             throws PacketException {
94
95         // Deserialize the header fields one by one
96         int startOffset = 0;
97         int numBits = 0;
98         for (Entry<String, Pair<Integer, Integer>> pairs : hdrFieldCoordMap
99                 .entrySet()) {
100             String hdrField = pairs.getKey();
101             startOffset = bitOffset + this.getfieldOffset(hdrField);
102             numBits = this.getfieldnumBits(hdrField);
103
104             byte[] hdrFieldBytes;
105             try {
106                 hdrFieldBytes = BitBufferHelper.getBits(data, startOffset,
107                         numBits);
108             } catch (final BufferException e) {
109                 throw new PacketException("getBits failed", e);
110             }
111
112             /*
113              * Store the raw read value, checks the payload type and set the
114              * payloadClass accordingly
115              */
116             this.setHeaderField(hdrField, hdrFieldBytes);
117
118             if (LOG.isTraceEnabled()) {
119                 LOG.trace("{}: {}: {} (offset {} bitsize {})", this.getClass().getSimpleName(), hdrField,
120                         HexEncode.bytesToHexString(hdrFieldBytes), startOffset, numBits);
121             }
122         }
123
124         // Deserialize the payload now
125         int payloadStart = startOffset + numBits;
126         int payloadSize = data.length * NetUtils.NUM_BITS_IN_A_BYTE - payloadStart;
127
128         if (payloadClass != null) {
129             try {
130                 payload = payloadClass.newInstance();
131             } catch (InstantiationException | IllegalAccessException e) {
132                 throw new PacketException("Error parsing payload for Ethernet packet", e);
133             }
134             payload.deserialize(data, payloadStart, payloadSize);
135             payload.setParent(this);
136         } else {
137             /*
138              *  The payload class was not set, it means no class for parsing
139              *  this payload is present. Let's store the raw payload if any.
140              */
141             int start = payloadStart / NetUtils.NUM_BITS_IN_A_BYTE;
142             int stop = start + payloadSize / NetUtils.NUM_BITS_IN_A_BYTE;
143             rawPayload = Arrays.copyOfRange(data, start, stop);
144         }
145
146
147         // Take care of computation that can be done only after deserialization
148         postDeserializeCustomOperation(data, payloadStart - getHeaderSize());
149
150         return this;
151     }
152
153     /**
154      * This method serializes the header and payload from the respective
155      * packet class, into a single stream of bytes to be sent on the wire.
156      *
157      * @return The byte array representing the serialized Packet
158      * @throws PacketException if serialization fails
159      */
160     public byte[] serialize() throws PacketException {
161
162         // Acquire or compute the serialized payload
163         byte[] payloadBytes = null;
164         if (payload != null) {
165             payloadBytes = payload.serialize();
166         } else if (rawPayload != null) {
167             payloadBytes = rawPayload;
168         }
169         int payloadSize = payloadBytes == null ? 0 : payloadBytes.length;
170
171         // Allocate the buffer to contain the full (header + payload) packet
172         int headerSize = this.getHeaderSize() / NetUtils.NUM_BITS_IN_A_BYTE;
173         byte[] packetBytes = new byte[headerSize + payloadSize];
174         if (payloadBytes != null) {
175             System.arraycopy(payloadBytes, 0, packetBytes, headerSize, payloadSize);
176         }
177
178         // Serialize this packet header, field by field
179         for (Map.Entry<String, Pair<Integer, Integer>> pairs : hdrFieldCoordMap
180                 .entrySet()) {
181             String field = pairs.getKey();
182             byte[] fieldBytes = hdrFieldsMap.get(field);
183             // Let's skip optional fields when not set
184             if (fieldBytes != null) {
185                 try {
186                     BitBufferHelper.setBytes(packetBytes, fieldBytes,
187                             getfieldOffset(field), getfieldnumBits(field));
188                 } catch (final BufferException e) {
189                     throw new PacketException("setBytes failed", e);
190                 }
191             }
192         }
193
194         // Perform post serialize operations (like checksum computation)
195         postSerializeCustomOperation(packetBytes);
196
197         if (LOG.isTraceEnabled()) {
198             LOG.trace("packet {}: {}", this.getClass().getSimpleName(),
199                     HexEncode.bytesToHexString(packetBytes));
200         }
201
202         return packetBytes;
203     }
204
205     /**
206      * This method gets called at the end of the serialization process It is
207      * intended for the child packets to insert some custom data into the output
208      * byte stream which cannot be done or cannot be done efficiently during the
209      * normal Packet.serialize() path. An example is the checksum computation
210      * for IPv4
211      *
212      * @param myBytes serialized bytes
213      * @throws PacketException on failure
214      */
215     protected void postSerializeCustomOperation(byte[] myBytes) throws PacketException {
216         // no op
217     }
218
219     /**
220      * This method re-computes the checksum of the bits received on the wire and
221      * validates it with the checksum in the bits received Since the computation
222      * of checksum varies based on the protocol, this method is overridden.
223      * Currently only IPv4 and ICMP do checksum computation and validation. TCP
224      * and UDP need to implement these if required.
225      *
226      * @param data The byte stream representing the Ethernet frame
227      * @param startBitOffset The bit offset from where the byte array corresponding to this Packet starts in the frame
228      * @throws PacketException on failure
229      */
230     protected void postDeserializeCustomOperation(byte[] data, int startBitOffset) throws PacketException {
231         // no op
232     }
233
234     /**
235      * Gets the header length in bits.
236      *
237      * @return int the header length in bits
238      */
239     public int getHeaderSize() {
240         int size = 0;
241         /*
242          * We need to iterate over the fields that were read in the frame
243          * (hdrFieldsMap) not all the possible ones described in
244          * hdrFieldCoordMap. For ex, 802.1Q may or may not be there
245          */
246         for (Map.Entry<String, byte[]> fieldEntry : hdrFieldsMap.entrySet()) {
247             if (fieldEntry.getValue() != null) {
248                 String field = fieldEntry.getKey();
249                 size += getfieldnumBits(field);
250             }
251         }
252         return size;
253     }
254
255     /**
256      * This method fetches the start bit offset for header field specified by
257      * 'fieldname'. The offset is present in the hdrFieldCoordMap of the
258      * respective packet class
259      *
260      * @return Integer - startOffset of the requested field
261      */
262     public int getfieldOffset(final String fieldName) {
263         return hdrFieldCoordMap.get(fieldName).getLeft();
264     }
265
266     /**
267      * This method fetches the number of bits for header field specified by
268      * 'fieldname'. The numBits are present in the hdrFieldCoordMap of the
269      * respective packet class
270      *
271      * @return Integer - number of bits of the requested field
272      */
273     public int getfieldnumBits(final String fieldName) {
274         return hdrFieldCoordMap.get(fieldName).getRight();
275     }
276
277     @Override
278     public String toString() {
279         StringBuilder ret = new StringBuilder();
280         ret.append(this.getClass().getSimpleName());
281         ret.append(": [");
282         for (String field : hdrFieldCoordMap.keySet()) {
283             byte[] value = hdrFieldsMap.get(field);
284             ret.append(field);
285             ret.append(": ");
286             ret.append(HexEncode.bytesToHexString(value));
287             ret.append(", ");
288         }
289         ret.replace(ret.length() - 2, ret.length() - 1, "]");
290         return ret.toString();
291     }
292
293     /**
294      * Returns the raw payload carried by this packet in case payload was not
295      * parsed. Caller can call this function in case the getPaylod() returns null.
296      *
297      * @return The raw payload if not parsable as an array of bytes, null otherwise
298      */
299     @SuppressFBWarnings("EI_EXPOSE_REP")
300     public byte[] getRawPayload() {
301         return rawPayload;
302     }
303
304     /**
305      * Set a raw payload in the packet class.
306      *
307      * @param bytes The raw payload as byte array
308      */
309     public void setRawPayload(final byte[] bytes) {
310         this.rawPayload = Arrays.copyOf(bytes, bytes.length);
311     }
312
313     /**
314      * Return whether the deserialized packet is to be considered corrupted.
315      * This is the case when the checksum computed after reconstructing the
316      * packet received from wire is not equal to the checksum read from the
317      * stream. For the Packet class which do not have a checksum field, this
318      * function will always return false.
319      *
320      *
321      * @return true if the deserialized packet's recomputed checksum is not
322      *         equal to the packet carried checksum
323      */
324     public boolean isCorrupted() {
325         return corrupted;
326     }
327
328     @Override
329     public int hashCode() {
330         final int prime = 31;
331         int result = super.hashCode();
332         result = prime * result
333                 + (this.hdrFieldsMap == null ? 0 : hdrFieldsMap.hashCode());
334         return result;
335     }
336
337     @Override
338     public boolean equals(final Object obj) {
339         if (obj == null) {
340             return false;
341         }
342
343         if (this == obj) {
344             return true;
345         }
346         if (getClass() != obj.getClass()) {
347             return false;
348         }
349         Packet other = (Packet) obj;
350         if (hdrFieldsMap == other.hdrFieldsMap) {
351             return true;
352         }
353         if (hdrFieldsMap == null || other.hdrFieldsMap == null) {
354             return false;
355         }
356         for (Entry<String, byte[]> entry : hdrFieldsMap.entrySet()) {
357             String field = entry.getKey();
358             if (!Arrays.equals(entry.getValue(), other.hdrFieldsMap.get(field))) {
359                 return false;
360             }
361         }
362         return true;
363     }
364 }