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