b6798cdfaf2944d85feab00292ffca1704cb95e9
[openflowjava.git] / third-party / openflow-codec / src / main / java / org / openflow / codec / protocol / OXMField.java
1 package org.openflow.codec.protocol;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.Arrays;
6 import java.util.List;
7
8 import org.openflow.codec.io.IDataBuffer;
9 import org.openflow.codec.util.U16;
10
11 /**
12  * OXM Field in TLV format to handle Basic and Experimenter it has
13  * implementation of ofp_oxm_experimenter_header
14  *
15  * @author AnilGujele
16  *
17  */
18 public class OXMField implements Cloneable, Serializable {
19
20     private static final long serialVersionUID = 1L;
21
22     private static byte MINIMUM_LENGTH = 4;
23
24     private OXMClass OXMClassType;
25     private OFBMatchFields matchField;
26     private boolean hasMask;
27     private byte[] data;
28     private byte length;
29     private int experimenterId;
30     private short experimenterField;
31
32     /**
33      * constructor
34      */
35     public OXMField() {
36         data = new byte[0];
37         length = MINIMUM_LENGTH;
38     }
39
40     /**
41      * constructor with arguments
42      *
43      * @param classType
44      * @param matchField
45      * @param hasMask
46      * @param data
47      */
48     public OXMField(OXMClass classType, OFBMatchFields matchField, boolean hasMask, byte[] data) {
49         length = MINIMUM_LENGTH;
50         this.setOXMClassType(classType);
51         this.setMatchField(matchField);
52         this.setHasMask(hasMask);
53         this.setData(data);
54     }
55
56     /**
57      * constructor with arguments, should use in case of experimenter
58      */
59     public OXMField(OXMClass classType, short expField, int expId) {
60         length = MINIMUM_LENGTH;
61         this.setOXMClassType(classType);
62         this.setExperimenterField(expField);
63         this.setExperimenterId(expId);
64         // set default match field to avoid any exception in case of
65         // experimenter
66         this.setMatchField(OFBMatchFields.ICMPV4_TYPE);
67     }
68
69     /**
70      * get the oxm match field class
71      *
72      * @return
73      */
74     public OXMClass getOXMClassType() {
75         return OXMClassType;
76     }
77
78     /**
79      * set the oxm match field class
80      *
81      * @param oXMClassType
82      *            - supported class type is OXMClass.OPENFLOW_BASIC
83      */
84     public void setOXMClassType(OXMClass oXMClassType) {
85         OXMClassType = oXMClassType;
86     }
87
88     /**
89      * get match field
90      *
91      * @return
92      */
93     public OFBMatchFields getMatchField() {
94         return matchField;
95     }
96
97     /**
98      * set match field
99      *
100      * @param matchField
101      */
102     public void setMatchField(OFBMatchFields matchField) {
103         this.matchField = matchField;
104     }
105
106     /**
107      * return the match field with has mask
108      */
109     public byte getMatchFieldValueWithHasMask() {
110         byte value = matchField.getValue();
111         // 7 left most bit is used for match field identification
112         value = (byte) (value << 1);
113         if (this.hasMask) {
114             // set the last bit as 1
115             value = (byte) (value | 1);
116         }
117         return value;
118     }
119
120     /**
121      * whether match field value has mask
122      *
123      * @return
124      */
125     public boolean isHasMask() {
126         return hasMask;
127     }
128
129     /**
130      * set the has mask
131      *
132      * @param hasMask
133      */
134     public void setHasMask(boolean hasMask) {
135         this.hasMask = hasMask;
136
137     }
138
139     /**
140      * get match field payload
141      *
142      * @return
143      */
144     public byte[] getData() {
145         return data;
146     }
147
148     /**
149      * set value for match field payload
150      *
151      * @param data
152      */
153     public void setData(byte[] data) {
154         if (null == data) {
155             this.data = new byte[0];
156         } else {
157             this.data = data;
158         }
159     }
160
161     /**
162      * to get the total length of match field (or TLV) including header in bytes
163      *
164      * @return
165      */
166     public byte getLength() {
167         // Type + Lenghth + Value
168         return (byte) (length + data.length);
169     }
170
171     /**
172      * Read this field from the specified DataBuffer
173      *
174      * @param data
175      *            - data to read to construct match object
176      * @return true - reading is successful , false - failed to read as data is
177      *         not proper
178      */
179     public boolean readFrom(IDataBuffer data) {
180
181         boolean result = false;
182         // read match field class type
183         int classType = U16.f(data.getShort());
184         // check for supported class type
185         if (classType == OXMClass.OPENFLOW_BASIC.getValue()) {
186             // read match field including hasMask
187             byte field = data.get();
188             boolean hasMask = ((field & 1) == 1);
189             /*
190              * TBD - if mask is set, then data length will be double, so driver
191              * need to handle masking to set the field value or plugin or App
192              * will handle
193              */
194             field = (byte) (field >>> 1);
195             OFBMatchFields matchField = OFBMatchFields.valueOf(field);
196             // read length of value
197             byte dataLength = data.get();
198             // if data is negative number, then return
199             if (dataLength < 0) {
200                 return result;
201             }
202             if (null != matchField) {
203
204                 int matchFieldDefineSize = matchField.getLengthInBytes();
205                 int dataLenghtSize = hasMask ? dataLength / 2 : dataLength;
206                 byte[] dataValue = new byte[dataLength];
207                 data.get(dataValue);
208                 boolean isMaskingValid = hasMask ? (matchField.isHasMask() == hasMask) : true;
209                 // check if field is as per the open flow spec 1.3, if not then
210                 // only moving the data pointer is enough
211                 // datalength can be zero in case only header is required.
212                 if (0 != dataLenghtSize && (dataLenghtSize == matchFieldDefineSize) && isMaskingValid) {
213
214                     this.setOXMClassType(OXMClass.getOXMClass(classType));
215                     this.setMatchField(matchField);
216                     this.setHasMask(hasMask);
217                     this.setData(dataValue);
218
219                 }
220             } else {
221                 // unknown match field, better to log and move the pointer
222                 // read data
223                 byte[] dataValue = new byte[dataLength];
224                 data.get(dataValue);
225             }
226             result = true;
227
228         } else if (classType == OXMClass.EXPERIMENTER.getValue()) {
229             this.setExperimenterField(data.getShort());
230             this.setExperimenterId(data.getInt());
231         }
232
233         return result;
234
235     }
236
237     /**
238      * Write this match binary format to the specified DataBuffer
239      *
240      * @param data
241      */
242     public void writeTo(IDataBuffer data) {
243         data.putShort(U16.t(this.getOXMClassType().getValue()));
244         if (this.getOXMClassType().equals(OXMClass.OPENFLOW_BASIC)) {
245             data.put(this.getMatchFieldValueWithHasMask());
246             data.put((byte) this.getData().length); // actual length of data
247             data.put(this.getData());
248         } else if (this.getOXMClassType().equals(OXMClass.EXPERIMENTER)) {
249             data.putShort(this.getExperimenterField());
250             data.putInt(this.getExperimenterId());
251
252         }
253     }
254
255     /**
256      * create the clone of this OXMTLVField
257      *
258      */
259     @Override
260     public OXMField clone() {
261         try {
262             OXMField field = (OXMField) super.clone();
263             field.data = this.data.clone();
264             return field;
265
266         } catch (CloneNotSupportedException e) {
267             throw new RuntimeException(e);
268         }
269     }
270
271     /**
272      * to get the object hashcode
273      *
274      * @return
275      */
276     public int hashCode() {
277         final int prime = 721;
278         int result = prime + ((data == null) ? 0 : Arrays.hashCode(data));
279         result = prime * result + this.getLength();
280         result = prime * result + this.matchField.getValue();
281         result = prime * result + this.getOXMClassType().getValue();
282         result = prime * result + this.getExperimenterField();
283         result = prime * result + this.getExperimenterId();
284         return result;
285     }
286
287     /**
288      * to check object equality
289      */
290     public boolean equals(Object obj) {
291         if (this == obj) {
292             return true;
293         }
294         if (null == obj) {
295             return false;
296         }
297         if (!(obj instanceof OXMField)) {
298             return false;
299         }
300         OXMField other = (OXMField) obj;
301         if (0 != this.matchField.compareTo(other.matchField)) {
302             return false;
303         }
304         if (0 != this.OXMClassType.compareTo(other.OXMClassType)) {
305             return false;
306         }
307         if (!(this.isHasMask() == other.isHasMask())) {
308             return false;
309         }
310         if (this.getLength() != other.getLength()) {
311             return false;
312         }
313         if (this.getExperimenterField() != other.getExperimenterField()) {
314             return false;
315         }
316         if (this.getExperimenterId() != other.getExperimenterId()) {
317             return false;
318         }
319
320         if (!Arrays.equals(this.getData(), other.getData())) {
321             return false;
322         }
323         return true;
324     }
325
326     /**
327      * String representation of OXMTLVField Object ex:
328      * Type=OPENFLOW_BASIC-IPV4_SRC, Length=8, Value=[1, 2, 3, 4]
329      */
330
331     public String toString() {
332         StringBuffer buffer = new StringBuffer(30);
333         buffer.append("Type=").append(this.getOXMClassType().name());
334         buffer.append("-").append(this.matchField.name());
335         buffer.append(", Length=").append(this.getLength());
336         buffer.append(", Value=").append(Arrays.toString(data));
337         return buffer.toString();
338     }
339
340     /**
341      * read OXMField header from data till specified length
342      *
343      * @param data
344      * @param length
345      * @return
346      */
347     public static List<OXMField> readOXMFieldHeader(IDataBuffer data, int length) {
348         List<OXMField> oxmList = new ArrayList<OXMField>();
349         int end = data.position() + length;
350         while (data.position() < end) {
351             // read match field class type
352             int classType = U16.f(data.getShort());
353             // read match field including hasMask
354             byte field = data.get();
355             boolean hasMask = ((field & 1) == 1);
356             field = (byte) (field >>> 1);
357             OFBMatchFields matchField = OFBMatchFields.valueOf(field);
358             // check for supported class type
359             if (classType == OXMClass.OPENFLOW_BASIC.getValue()) {
360                 OXMField oxmfield = new OXMField(OXMClass.OPENFLOW_BASIC, matchField, hasMask, new byte[0]);
361                 oxmList.add(oxmfield);
362             } else if (classType == OXMClass.EXPERIMENTER.getValue()) {
363                 OXMField oxmfield = new OXMField(OXMClass.OPENFLOW_BASIC, data.getShort(), data.getInt());
364                 oxmList.add(oxmfield);
365             }
366             data.get(); // ignore length field in header.
367         }
368
369         return oxmList;
370     }
371
372     /**
373      * write oxmfield header in data buffer
374      *
375      * @param data
376      * @param oxmList
377      */
378     public static void writeOXMFieldHeader(IDataBuffer data, List<OXMField> oxmList) {
379         for (OXMField field : oxmList) {
380             OXMClass clazz = field.getOXMClassType();
381             if (clazz.getValue() == OXMClass.OPENFLOW_BASIC.getValue()) {
382                 data.putShort(U16.t(OXMClass.OPENFLOW_BASIC.getValue()));
383                 data.put(field.getMatchFieldValueWithHasMask());
384                 data.put(field.getLength());
385             } else if (clazz.getValue() == OXMClass.EXPERIMENTER.getValue()) {
386                 data.putShort(U16.t(OXMClass.EXPERIMENTER.getValue()));
387                 data.putShort(field.getExperimenterField());
388                 data.putInt(field.getExperimenterId());
389             }
390
391         }
392     }
393
394     /**
395      * get the total length in bytes for the OXMField List
396      *
397      * @param list
398      * @return
399      */
400     public static int getTotalLength(List<OXMField> list) {
401         int result = 0;
402         for (OXMField field : list) {
403             if (field.getOXMClassType().equals(OXMClass.OPENFLOW_BASIC)) {
404                 result = result + OXMField.MINIMUM_LENGTH;
405             } else if (field.getOXMClassType().equals(OXMClass.EXPERIMENTER)) {
406                 // for experimenter, header is 8 byte
407                 result = result + OXMField.MINIMUM_LENGTH * 2;
408             }
409         }
410         return result;
411
412     }
413
414     /**
415      *
416      * @return
417      */
418     public int getExperimenterId() {
419         return experimenterId;
420     }
421
422     /**
423      *
424      * @param experimenterId
425      */
426     public void setExperimenterId(int experimenterId) {
427         this.experimenterId = experimenterId;
428     }
429
430     public short getExperimenterField() {
431         return experimenterField;
432     }
433
434     public void setExperimenterField(short experimenterField) {
435         this.experimenterField = experimenterField;
436     }
437
438 }