1 package org.openflow.codec.protocol;
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.Arrays;
8 import org.openflow.codec.io.IDataBuffer;
9 import org.openflow.codec.util.U16;
12 * OXM Field in TLV format to handle Basic and Experimenter it has
13 * implementation of ofp_oxm_experimenter_header
18 public class OXMField implements Cloneable, Serializable {
20 private static final long serialVersionUID = 1L;
22 private static byte MINIMUM_LENGTH = 4;
24 private OXMClass OXMClassType;
25 private OFBMatchFields matchField;
26 private boolean hasMask;
29 private int experimenterId;
30 private short experimenterField;
37 length = MINIMUM_LENGTH;
41 * constructor with arguments
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);
57 * constructor with arguments, should use in case of experimenter
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
66 this.setMatchField(OFBMatchFields.ICMPV4_TYPE);
70 * get the oxm match field class
74 public OXMClass getOXMClassType() {
79 * set the oxm match field class
82 * - supported class type is OXMClass.OPENFLOW_BASIC
84 public void setOXMClassType(OXMClass oXMClassType) {
85 OXMClassType = oXMClassType;
93 public OFBMatchFields getMatchField() {
102 public void setMatchField(OFBMatchFields matchField) {
103 this.matchField = matchField;
107 * return the match field with has mask
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);
114 // set the last bit as 1
115 value = (byte) (value | 1);
121 * whether match field value has mask
125 public boolean isHasMask() {
134 public void setHasMask(boolean hasMask) {
135 this.hasMask = hasMask;
140 * get match field payload
144 public byte[] getData() {
149 * set value for match field payload
153 public void setData(byte[] data) {
155 this.data = new byte[0];
162 * to get the total length of match field (or TLV) including header in bytes
166 public byte getLength() {
167 // Type + Lenghth + Value
168 return (byte) (length + data.length);
172 * Read this field from the specified DataBuffer
175 * - data to read to construct match object
176 * @return true - reading is successful , false - failed to read as data is
179 public boolean readFrom(IDataBuffer data) {
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);
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
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) {
202 if (null != matchField) {
204 int matchFieldDefineSize = matchField.getLengthInBytes();
205 int dataLenghtSize = hasMask ? dataLength / 2 : dataLength;
206 byte[] dataValue = new byte[dataLength];
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) {
214 this.setOXMClassType(OXMClass.getOXMClass(classType));
215 this.setMatchField(matchField);
216 this.setHasMask(hasMask);
217 this.setData(dataValue);
221 // unknown match field, better to log and move the pointer
223 byte[] dataValue = new byte[dataLength];
228 } else if (classType == OXMClass.EXPERIMENTER.getValue()) {
229 this.setExperimenterField(data.getShort());
230 this.setExperimenterId(data.getInt());
238 * Write this match binary format to the specified DataBuffer
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());
256 * create the clone of this OXMTLVField
260 public OXMField clone() {
262 OXMField field = (OXMField) super.clone();
263 field.data = this.data.clone();
266 } catch (CloneNotSupportedException e) {
267 throw new RuntimeException(e);
272 * to get the object hashcode
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();
288 * to check object equality
290 public boolean equals(Object obj) {
297 if (!(obj instanceof OXMField)) {
300 OXMField other = (OXMField) obj;
301 if (0 != this.matchField.compareTo(other.matchField)) {
304 if (0 != this.OXMClassType.compareTo(other.OXMClassType)) {
307 if (!(this.isHasMask() == other.isHasMask())) {
310 if (this.getLength() != other.getLength()) {
313 if (this.getExperimenterField() != other.getExperimenterField()) {
316 if (this.getExperimenterId() != other.getExperimenterId()) {
320 if (!Arrays.equals(this.getData(), other.getData())) {
327 * String representation of OXMTLVField Object ex:
328 * Type=OPENFLOW_BASIC-IPV4_SRC, Length=8, Value=[1, 2, 3, 4]
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();
341 * read OXMField header from data till specified length
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);
366 data.get(); // ignore length field in header.
373 * write oxmfield header in data buffer
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());
395 * get the total length in bytes for the OXMField List
400 public static int getTotalLength(List<OXMField> list) {
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;
418 public int getExperimenterId() {
419 return experimenterId;
424 * @param experimenterId
426 public void setExperimenterId(int experimenterId) {
427 this.experimenterId = experimenterId;
430 public short getExperimenterField() {
431 return experimenterField;
434 public void setExperimenterField(short experimenterField) {
435 this.experimenterField = experimenterField;