61d6d248c8108c9fd4b263d8cca9207ea1ffd3ef
[controller.git] / opendaylight / sal / api / src / main / java / org / opendaylight / controller / sal / packet / Packet.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.net.InetAddress;
13 import java.net.UnknownHostException;
14 import java.util.Map;
15 import java.util.Map.Entry;
16
17 import org.apache.commons.lang3.tuple.Pair;
18 import org.opendaylight.controller.sal.utils.HexEncode;
19 import org.opendaylight.controller.sal.utils.NetUtils;
20
21 /**
22  * Abstract class which represents the generic network packet object
23  * It provides the basic methods which are common for all the packets,
24  * like serialize and deserialize
25  *
26  *
27  */
28
29 public abstract class Packet {
30     // Access level granted to this packet
31     protected boolean writeAccess;
32     // When deserialized from wire, packet could result corrupted
33     protected boolean corrupted;
34     // The packet that encapsulate this packet
35     protected Packet parent;
36     // The packet encapsulated by this packet
37     protected Packet payload;
38     // Bit coordinates of packet header fields
39     protected Map<String, Pair<Integer, Integer>> hdrFieldCoordMap;
40     // Header fields values: Map<FieldName,Value>
41     protected Map<String, byte[]> hdrFieldsMap;
42     // The class of the encapsulated packet object
43     protected Class<? extends Packet> payloadClass;
44
45     public Packet() {
46         writeAccess = false;
47         corrupted = false;
48     }
49
50     public Packet(boolean writeAccess) {
51         this.writeAccess = writeAccess;
52         this.corrupted = false;
53     }
54
55     public Packet getParent() {
56         return parent;
57     }
58
59     public Packet getPayload() {
60         return payload;
61     }
62
63     public void setParent(Packet parent) {
64         this.parent = parent;
65     }
66
67     public void setPayload(Packet payload) {
68         this.payload = payload;
69     }
70
71     public void setHeaderField(String headerField, byte[] readValue) {
72         hdrFieldsMap.put(headerField, readValue);
73     }
74
75     /**
76      * This method deserializes the data bits obtained from the wire
77      * into the respective header and payload which are of type Packet
78      * @param byte[] data - data from wire to deserialize
79      * @param int bitOffset bit position where packet header starts in data array
80      * @param int size of packet in bits
81      * @return Packet
82      * @throws Exception
83      */
84
85     public Packet deserialize(byte[] data, int bitOffset, int size)
86             throws Exception {
87         String hdrField;
88         Integer startOffset = 0, numBits = 0;
89         byte[] hdrFieldBytes;
90
91         for (Entry<String, Pair<Integer, Integer>> pairs : hdrFieldCoordMap
92                 .entrySet()) {
93             hdrField = pairs.getKey();
94             startOffset = bitOffset + this.getfieldOffset(hdrField);
95             numBits = this.getfieldnumBits(hdrField);
96
97             hdrFieldBytes = BitBufferHelper.getBits(data, startOffset, numBits);
98             /*
99              * Store the raw read value, checks the payload type and
100              * set the payloadClass accordingly
101              */
102             this.setHeaderField(hdrField, hdrFieldBytes);
103         }
104
105         postDeserializeCustomOperation(data, startOffset);
106
107         int payloadStart = startOffset + numBits;
108         //int payloadSize = size - payloadStart;
109         int payloadSize = data.length * NetUtils.NumBitsInAByte - payloadStart;
110
111         if (payloadClass != null) {
112             try {
113                 payload = payloadClass.newInstance();
114             } catch (Exception e) {
115                 throw new RuntimeException(
116                         "Error parsing payload for Ethernet packet", e);
117             }
118             payload.deserialize(data, payloadStart, payloadSize);
119             payload.setParent(this);
120         } else {
121             // For now let's discard unparsable payload
122         }
123         return this;
124     }
125
126     /**
127      * This method serializes the header and payload bytes from
128      * the respective packet class, into a single stream of bytes
129      * to be sent on the wire
130      * @return byte[] - serialized bytes
131      * @throws Exception
132      */
133
134     public byte[] serialize() throws Exception {
135         byte[] payloadBytes = null;
136         int payloadSize = 0;
137         int headerSize = this.getHeaderSize();
138         int payloadByteOffset = headerSize / NetUtils.NumBitsInAByte;
139         int size = 0;
140
141         if (payload != null) {
142             payloadBytes = payload.serialize();
143             payloadSize = payloadBytes.length * NetUtils.NumBitsInAByte;
144         }
145
146         size = headerSize + payloadSize;
147         int length = size / NetUtils.NumBitsInAByte;
148         byte headerBytes[] = new byte[length];
149
150         if (payload != null) {
151             System.arraycopy(payloadBytes, 0, headerBytes, payloadByteOffset,
152                     payloadBytes.length);
153         }
154
155         String field;
156         byte[] fieldBytes;
157         Integer startOffset, numBits;
158
159         for (Map.Entry<String, Pair<Integer, Integer>> pairs : hdrFieldCoordMap
160                 .entrySet()) {
161             field = pairs.getKey();
162             fieldBytes = hdrFieldsMap.get(field);
163             // Let's skip optional fields when not set
164             if (fieldBytes != null) {
165                 startOffset = this.getfieldOffset(field);
166                 numBits = this.getfieldnumBits(field);
167                 BitBufferHelper.setBytes(headerBytes, fieldBytes, startOffset,
168                         numBits);
169             }
170         }
171         postSerializeCustomOperation(headerBytes);
172
173         return headerBytes;
174     }
175
176     /**
177      * This method gets called at the end of the serialization process
178      * It is intended for the child packets to insert some custom data
179      * into the output byte stream which cannot be done or cannot be done
180      * efficiently during the normal Packet.serialize() path.
181      * An example is the checksum computation for IPv4
182      * @param byte[] - serialized bytes
183      */
184     protected void postSerializeCustomOperation(byte[] myBytes)
185             throws Exception {
186         // no op
187     }
188
189     /**
190      * This method re-computes the checksum of the bits received on the
191      * wire and validates it with the checksum in the bits received
192      * Since the computation of checksum varies based on the protocol,
193      * this method is overridden
194      * Currently only IPv4 does checksum computation and validation
195      * TCP and UDP need to implement these if required
196      * @param byte[] data
197      * @param int endBitOffset
198      * @return void
199      */
200     protected void postDeserializeCustomOperation(byte[] data, int endBitOffset)
201             throws Exception {
202         //              no op
203     }
204
205     /**
206      * Gets the header length in bits
207      * @return int
208      * @throws Exception
209      */
210     public int getHeaderSize() throws Exception {
211         int size = 0;
212         /*
213          *  We need to iterate over the fields that were read in the frame (hdrFieldsMap)
214          *  not all the possible ones described in hdrFieldCoordMap.
215          *  For ex, 802.1Q may or may not be there
216          */
217         for (Map.Entry<String, byte[]> fieldEntry : hdrFieldsMap.entrySet()) {
218             if (fieldEntry.getValue() != null) {
219                 String field = fieldEntry.getKey();
220                 size += getfieldnumBits(field);
221             }
222         }
223         return size;
224     }
225
226     /**
227      * This method fetches the start bit offset for header field specified by
228      * 'fieldname'.  The offset is present in the hdrFieldCoordMap of the respective
229      * packet class
230      * @param String fieldName
231      * @return Integer - startOffset of the requested field
232      */
233     public int getfieldOffset(String fieldName) {
234         return (((Pair<Integer, Integer>) hdrFieldCoordMap.get(fieldName))
235                 .getLeft());
236     }
237
238     /**
239      * This method fetches the number of bits for header field specified by
240      * 'fieldname'.  The numBits are present in the hdrFieldCoordMap of the respective
241      * packet class
242      * @param String fieldName
243      * @return Integer - number of bits of the requested field
244      */
245     public int getfieldnumBits(String fieldName) throws Exception {
246         return (((Pair<Integer, Integer>) hdrFieldCoordMap.get(fieldName))
247                 .getRight());
248     }
249
250     @Override
251     public String toString() {
252         StringBuffer ret = new StringBuffer();
253         for (Map.Entry<String, byte[]> entry : hdrFieldsMap.entrySet()) {
254             ret.append(entry.getKey() + ": ");
255             if (entry.getValue().length == 6) {
256                 ret.append(HexEncode.bytesToHexString(entry.getValue()) + " ");
257             } else if (entry.getValue().length == 4) {
258                 try {
259                     ret.append(InetAddress.getByAddress(entry.getValue())
260                             .getHostAddress()
261                             + " ");
262                 } catch (UnknownHostException e) {
263                     e.printStackTrace();
264                 }
265             } else {
266                 ret.append(((Long) BitBufferHelper.getLong(entry.getValue()))
267                         .toString()
268                         + " ");
269             }
270         }
271         return ret.toString();
272     }
273
274     /**
275      * Returns true if the packet is corrupted
276      * @return boolean
277      */
278     protected boolean isPacketCorrupted() {
279         return corrupted;
280     }
281 }