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