From 24b34553e5687f4bb929353f9f697e6ecbfc0f0b Mon Sep 17 00:00:00 2001
From: Rob Vaterlaus <rob.vaterlaus@bigswitch.com>
Date: Wed, 15 Feb 2012 16:51:53 -0800
Subject: [PATCH] Support for parsing the vendor data associated with OFVendor
 messages. Support for the role request/reply vendor extensions used in OVS.

---
 .../java/org/openflow/protocol/OFError.java   |  36 +++-
 .../java/org/openflow/protocol/OFVendor.java  |  68 ++++---
 .../protocol/factory/BasicFactory.java        |  47 ++++-
 .../protocol/factory/OFVendorDataFactory.java |  69 +++++++
 .../factory/OFVendorDataFactoryAware.java     |  28 +++
 .../vendor/OFBasicVendorDataType.java         |  71 +++++++
 .../protocol/vendor/OFBasicVendorId.java      | 162 ++++++++++++++++
 .../vendor/OFByteArrayVendorData.java         |  94 +++++++++
 .../protocol/vendor/OFVendorData.java         |  44 +++++
 .../protocol/vendor/OFVendorDataType.java     |  79 ++++++++
 .../openflow/protocol/vendor/OFVendorId.java  |  85 ++++++++
 .../vendor/nicira/OFNiciraVendorData.java     |  97 ++++++++++
 .../vendor/nicira/OFRoleReplyVendorData.java  |  66 +++++++
 .../nicira/OFRoleRequestVendorData.java       |  66 +++++++
 .../vendor/nicira/OFRoleVendorData.java       | 113 +++++++++++
 .../org/openflow/protocol/OFErrorTest.java    |   6 +-
 .../org/openflow/protocol/OFVendorTest.java   | 183 +++++++++++++++++-
 17 files changed, 1278 insertions(+), 36 deletions(-)
 create mode 100644 src/main/java/org/openflow/protocol/factory/OFVendorDataFactory.java
 create mode 100644 src/main/java/org/openflow/protocol/factory/OFVendorDataFactoryAware.java
 create mode 100644 src/main/java/org/openflow/protocol/vendor/OFBasicVendorDataType.java
 create mode 100644 src/main/java/org/openflow/protocol/vendor/OFBasicVendorId.java
 create mode 100644 src/main/java/org/openflow/protocol/vendor/OFByteArrayVendorData.java
 create mode 100644 src/main/java/org/openflow/protocol/vendor/OFVendorData.java
 create mode 100644 src/main/java/org/openflow/protocol/vendor/OFVendorDataType.java
 create mode 100644 src/main/java/org/openflow/protocol/vendor/OFVendorId.java
 create mode 100644 src/main/java/org/openflow/vendor/nicira/OFNiciraVendorData.java
 create mode 100644 src/main/java/org/openflow/vendor/nicira/OFRoleReplyVendorData.java
 create mode 100644 src/main/java/org/openflow/vendor/nicira/OFRoleRequestVendorData.java
 create mode 100644 src/main/java/org/openflow/vendor/nicira/OFRoleVendorData.java

diff --git a/src/main/java/org/openflow/protocol/OFError.java b/src/main/java/org/openflow/protocol/OFError.java
index 9cddce6de..4dccfe47e 100644
--- a/src/main/java/org/openflow/protocol/OFError.java
+++ b/src/main/java/org/openflow/protocol/OFError.java
@@ -35,7 +35,27 @@ public class OFError extends OFMessage implements OFMessageFactoryAware {
     public static int MINIMUM_LENGTH = 12;
 
     public enum OFErrorType {
-        OFPET_HELLO_FAILED, OFPET_BAD_REQUEST, OFPET_BAD_ACTION, OFPET_FLOW_MOD_FAILED, OFPET_PORT_MOD_FAILED, OFPET_QUEUE_OP_FAILED
+        // OFPET_VENDOR_ERROR is an extension that was added in Open vSwitch and isn't
+        // in the OF 1.0 spec, but it was easier to add it here instead of adding
+        // generic support for extensible vendor-defined error messages. 
+        // It uses the random value 0xb0c2 to avoid conflicts with other possible new
+        // error types. Support for vendor-defined extended errors has been standardized
+        // in the OF 1.2 spec, so this workaround in only needed for 1.0.
+        OFPET_HELLO_FAILED, OFPET_BAD_REQUEST, OFPET_BAD_ACTION, OFPET_FLOW_MOD_FAILED, OFPET_PORT_MOD_FAILED, OFPET_QUEUE_OP_FAILED, OFPET_VENDOR_ERROR((short)0xb0c2);
+        
+        protected short value;
+        
+        private OFErrorType() {
+            this.value = (short) this.ordinal();
+        }
+        
+        private OFErrorType(short value) {
+            this.value = value;
+        }
+        
+        public short getValue() {
+            return value;
+        }
     }
 
     public enum OFHelloFailedCode {
@@ -64,6 +84,9 @@ public class OFError extends OFMessage implements OFMessageFactoryAware {
 
     protected short errorType;
     protected short errorCode;
+    protected int vendor;
+    protected int vendorErrorType;
+    protected short vendorErrorCode;
     protected OFMessageFactory factory;
     protected byte[] error;
     protected boolean errorIsAscii;
@@ -90,9 +113,16 @@ public class OFError extends OFMessage implements OFMessageFactoryAware {
     }
 
     public void setErrorType(OFErrorType type) {
-        this.errorType = (short) type.ordinal();
+        this.errorType = type.getValue();
     }
 
+    /**
+     * @return true if the error is an extended vendor error
+     */
+    public boolean isVendorError() {
+        return errorType == OFErrorType.OFPET_VENDOR_ERROR.getValue();
+    }
+    
     /**
      * @return the errorCode
      */
@@ -210,7 +240,7 @@ public class OFError extends OFMessage implements OFMessageFactoryAware {
         if (dataLength > 0) {
             this.error = new byte[dataLength];
             data.readBytes(this.error);
-            if (this.errorType == OFErrorType.OFPET_HELLO_FAILED.ordinal())
+            if (this.errorType == OFErrorType.OFPET_HELLO_FAILED.getValue())
                 this.errorIsAscii = true;
         }
     }
diff --git a/src/main/java/org/openflow/protocol/OFVendor.java b/src/main/java/org/openflow/protocol/OFVendor.java
index 49fa4f1f3..8ecb862be 100644
--- a/src/main/java/org/openflow/protocol/OFVendor.java
+++ b/src/main/java/org/openflow/protocol/OFVendor.java
@@ -17,20 +17,22 @@
 
 package org.openflow.protocol;
 
-import java.util.Arrays;
-
 import org.jboss.netty.buffer.ChannelBuffer;
 import org.openflow.util.U16;
+import org.openflow.protocol.factory.OFVendorDataFactory;
+import org.openflow.protocol.factory.OFVendorDataFactoryAware;
+import org.openflow.protocol.vendor.OFVendorData;
 
 /**
  * Represents ofp_vendor_header
  * @author David Erickson (daviderickson@cs.stanford.edu)
  */
-public class OFVendor extends OFMessage {
+public class OFVendor extends OFMessage implements OFVendorDataFactoryAware {
     public static int MINIMUM_LENGTH = 12;
 
     protected int vendor;
-    protected byte[] data;
+    protected OFVendorData vendorData;
+    protected OFVendorDataFactory vendorDataFactory;
 
     public OFVendor() {
         super();
@@ -52,36 +54,42 @@ public class OFVendor extends OFMessage {
         this.vendor = vendor;
     }
 
+    /**
+     * @return the data
+     */
+    public OFVendorData getVendorData() {
+        return vendorData;
+    }
+
+    /**
+     * @param data the data to set
+     */
+    public void setVendorData(OFVendorData vendorData) {
+        this.vendorData = vendorData;
+    }
+
+    @Override
+    public void setVendorDataFactory(OFVendorDataFactory vendorDataFactory) {
+        this.vendorDataFactory = vendorDataFactory;
+    }
+      
     @Override
     public void readFrom(ChannelBuffer data) {
         super.readFrom(data);
         this.vendor = data.readInt();
-        if (this.length > MINIMUM_LENGTH) {
-            this.data = new byte[this.length - MINIMUM_LENGTH];
-            data.readBytes(this.data);
-        }
+        if (vendorDataFactory == null)
+            throw new RuntimeException("OFVendorDataFactory not set");
+            
+        this.vendorData = vendorDataFactory.parseVendorData(vendor,
+                data, super.getLengthU() - MINIMUM_LENGTH);
     }
 
     @Override
     public void writeTo(ChannelBuffer data) {
         super.writeTo(data);
         data.writeInt(this.vendor);
-        if (this.data != null)
-            data.writeBytes(this.data);
-    }
-
-    /**
-     * @return the data
-     */
-    public byte[] getData() {
-        return data;
-    }
-
-    /**
-     * @param data the data to set
-     */
-    public void setData(byte[] data) {
-        this.data = data;
+        if (vendorData != null)
+            vendorData.writeTo(data);
     }
 
     /* (non-Javadoc)
@@ -91,8 +99,9 @@ public class OFVendor extends OFMessage {
     public int hashCode() {
         final int prime = 337;
         int result = super.hashCode();
-        result = prime * result + Arrays.hashCode(data);
         result = prime * result + vendor;
+        if (vendorData != null)
+            result = prime * result + vendorData.hashCode();
         return result;
     }
 
@@ -108,10 +117,15 @@ public class OFVendor extends OFMessage {
         if (getClass() != obj.getClass())
             return false;
         OFVendor other = (OFVendor) obj;
-        if (!Arrays.equals(data, other.data))
-            return false;
         if (vendor != other.vendor)
             return false;
+        if (vendorData == null) {
+            if (other.vendorData != null) {
+                return false;
+            }
+        } else if (!vendorData.equals(other.vendorData)) {
+            return false;
+        }
         return true;
     }
 }
diff --git a/src/main/java/org/openflow/protocol/factory/BasicFactory.java b/src/main/java/org/openflow/protocol/factory/BasicFactory.java
index ac4c43275..e39732ade 100644
--- a/src/main/java/org/openflow/protocol/factory/BasicFactory.java
+++ b/src/main/java/org/openflow/protocol/factory/BasicFactory.java
@@ -28,6 +28,10 @@ import org.openflow.protocol.action.OFActionType;
 import org.openflow.protocol.statistics.OFStatistics;
 import org.openflow.protocol.statistics.OFStatisticsType;
 import org.openflow.protocol.statistics.OFVendorStatistics;
+import org.openflow.protocol.vendor.OFByteArrayVendorData;
+import org.openflow.protocol.vendor.OFVendorData;
+import org.openflow.protocol.vendor.OFVendorDataType;
+import org.openflow.protocol.vendor.OFVendorId;
 
 
 /**
@@ -39,7 +43,7 @@ import org.openflow.protocol.statistics.OFVendorStatistics;
  *
  */
 public class BasicFactory implements OFMessageFactory, OFActionFactory,
-        OFStatisticsFactory {
+        OFStatisticsFactory, OFVendorDataFactory {
     @Override
     public OFMessage getMessage(OFType t) {
         return t.newInstance();
@@ -74,6 +78,9 @@ public class BasicFactory implements OFMessageFactory, OFActionFactory,
             if (ofm instanceof OFStatisticsFactoryAware) {
                 ((OFStatisticsFactoryAware)ofm).setStatisticsFactory(this);
             }
+            if (ofm instanceof OFVendorDataFactoryAware) {
+                ((OFVendorDataFactoryAware)ofm).setVendorDataFactory(this);
+            }
             ofm.readFrom(data);
             if (OFMessage.class.equals(ofm.getClass())) {
                 // advance the position for un-implemented messages
@@ -203,4 +210,42 @@ public class BasicFactory implements OFMessageFactory, OFActionFactory,
         }
         return results; // empty; no statistics at all
     }
+
+
+    @Override
+    public OFVendorData getVendorData(OFVendorId vendorId,
+                                      OFVendorDataType vendorDataType) {
+        if (vendorDataType == null)
+            return null;
+        
+        return vendorDataType.newInstance();
+    }
+
+    /**
+     * Attempts to parse and return the OFVendorData contained in the given
+     * ChannelBuffer, beginning right after the vendor id.
+     * @param vendor the vendor id that was parsed from the OFVendor message.
+     * @param data the ChannelBuffer from which to parse the vendor data
+     * @param length the length to the end of the enclosing message.
+     * @return an OFVendorData instance
+     */
+    public OFVendorData parseVendorData(int vendor, ChannelBuffer data,
+            int length) {
+        OFVendorDataType vendorDataType = null;
+        OFVendorId vendorId = OFVendorId.lookupVendorId(vendor);
+        if (vendorId != null) {
+            data.markReaderIndex();
+            vendorDataType = vendorId.parseVendorDataType(data, length);
+            data.resetReaderIndex();
+        }
+        
+        OFVendorData vendorData = getVendorData(vendorId, vendorDataType);
+        if (vendorData == null)
+            vendorData = new OFByteArrayVendorData();
+
+        vendorData.readFrom(data, length);
+        
+        return vendorData;
+    }
+
 }
diff --git a/src/main/java/org/openflow/protocol/factory/OFVendorDataFactory.java b/src/main/java/org/openflow/protocol/factory/OFVendorDataFactory.java
new file mode 100644
index 000000000..d754a4a31
--- /dev/null
+++ b/src/main/java/org/openflow/protocol/factory/OFVendorDataFactory.java
@@ -0,0 +1,69 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson & Rob Sherwood, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package org.openflow.protocol.factory;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.openflow.protocol.vendor.OFVendorData;
+import org.openflow.protocol.vendor.OFVendorDataType;
+import org.openflow.protocol.vendor.OFVendorId;
+
+/**
+ * The interface to factories used for parsing/creating OFVendorData instances.
+ * All methods are expected to be thread-safe.
+ * 
+ * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com)
+ */
+public interface OFVendorDataFactory {
+    /**
+     * Retrieves an OFVendorData instance corresponding to the specified
+     * OFVendorId and OFVendorDataType. There are 3 possible cases for
+     * how this will be called:
+     * 
+     * 1) If the vendor id in the OFVendor message is an unknown value, 
+     *    then this method is called with both vendorId and vendorDataType
+     *    set to null. In this case typically the factory method should
+     *    return an instance of OFGenericVendorData that just contains
+     *    the raw byte array of the vendor data.
+     *    
+     * 2) If the vendor id is known but no vendor data type has been
+     *    registered for the data in the message, then vendorId is set to
+     *    the appropriate OFVendorId instance and OFVendorDataType is set
+     *    to null. This would typically be handled the same way as #1
+     *    
+     * 3) If both the vendor id and and vendor data type are known, then
+     *    typically you'd just call the method in OFVendorDataType to
+     *    instantiate the appropriate subclass of OFVendorData.
+     *    
+     * @param vendorId the vendorId of the containing OFVendor message
+     * @param vendorDataType the type of the OFVendorData to be retrieved
+     * @return an OFVendorData instance
+     */
+    public OFVendorData getVendorData(OFVendorId vendorId,
+            OFVendorDataType vendorDataType);
+    
+    /**
+     * Attempts to parse and return the OFVendorData contained in the given
+     * ChannelBuffer, beginning right after the vendor id.
+     * @param vendorId the vendor id that was parsed from the OFVendor message.
+     * @param data the ChannelBuffer from which to parse the vendor data
+     * @param length the length to the end of the enclosing message.
+     * @return an OFVendorData instance
+     */
+    public OFVendorData parseVendorData(int vendorId, ChannelBuffer data,
+            int length);
+}
diff --git a/src/main/java/org/openflow/protocol/factory/OFVendorDataFactoryAware.java b/src/main/java/org/openflow/protocol/factory/OFVendorDataFactoryAware.java
new file mode 100644
index 000000000..23614b0d0
--- /dev/null
+++ b/src/main/java/org/openflow/protocol/factory/OFVendorDataFactoryAware.java
@@ -0,0 +1,28 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson & Rob Sherwood, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package org.openflow.protocol.factory;
+
+/**
+ * Classes implementing this interface are expected to be instantiated with an
+ * instance of an OFVendorDataFactory
+ * 
+ * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com)
+ */
+public interface OFVendorDataFactoryAware {
+    public void setVendorDataFactory(OFVendorDataFactory vendorDataFactory);
+}
diff --git a/src/main/java/org/openflow/protocol/vendor/OFBasicVendorDataType.java b/src/main/java/org/openflow/protocol/vendor/OFBasicVendorDataType.java
new file mode 100644
index 000000000..1f0e14b2d
--- /dev/null
+++ b/src/main/java/org/openflow/protocol/vendor/OFBasicVendorDataType.java
@@ -0,0 +1,71 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson & Rob Sherwood, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package org.openflow.protocol.vendor;
+
+import org.openflow.protocol.Instantiable;
+
+/**
+ * Subclass of OFVendorDataType that works with any vendor data format that
+ * begins with a integral value to indicate the format of the remaining data.
+ * It maps from the per-vendor-id integral data type code to the object
+ * used to instantiate the class associated with that vendor data type.
+ * 
+ * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com)
+ */
+public class OFBasicVendorDataType extends OFVendorDataType {
+    
+    /**
+     * The data type value at the beginning of the vendor data.
+     */
+    protected long type;
+    
+    /**
+     * Construct an empty (i.e. no specified data type value) vendor data type.
+     */
+    public OFBasicVendorDataType() {
+        super();
+        this.type = 0;
+    }
+    
+    /**
+     * Store some information about the vendor data type, including wire protocol
+     * type number, derived class and instantiator.
+     *
+     * @param type Wire protocol number associated with this vendor data type
+     * @param instantiator An Instantiator<OFVendorData> implementation that
+     *              creates an instance of an appropriate subclass of OFVendorData.
+     */
+    public OFBasicVendorDataType(long type, Instantiable<OFVendorData> instantiator) {
+        super(instantiator);
+        this.type = type;
+    }
+
+    /**
+     * @return Returns the wire protocol value corresponding to this OFVendorDataType
+     */
+    public long getTypeValue() {
+        return this.type;
+    }
+    
+    /**
+     * @param type the wire protocol value for this data type
+     */
+    public void setTypeValue(long type) {
+        this.type = type;
+    }
+}
diff --git a/src/main/java/org/openflow/protocol/vendor/OFBasicVendorId.java b/src/main/java/org/openflow/protocol/vendor/OFBasicVendorId.java
new file mode 100644
index 000000000..09365d7c5
--- /dev/null
+++ b/src/main/java/org/openflow/protocol/vendor/OFBasicVendorId.java
@@ -0,0 +1,162 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson & Rob Sherwood, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package org.openflow.protocol.vendor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.openflow.protocol.Instantiable;
+
+/**
+ * Basic subclass of OFVendorId that works with any vendor data format where
+ * the data begins with an integral data type value.
+ * 
+ * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com)
+ */
+public class OFBasicVendorId extends OFVendorId {
+    
+    /**
+     * The size of the data type value at the beginning of all vendor
+     * data associated with this vendor id. The data type size must be
+     * either 1, 2, 4 or 8.
+     */
+    protected int dataTypeSize;
+    
+    /**
+     * Map of the vendor data types that have been registered for this
+     * vendor id.
+     */
+    protected Map<Long, OFBasicVendorDataType> dataTypeMap =
+            new HashMap<Long, OFBasicVendorDataType>();
+    
+    /**
+     * Construct an OFVendorId that where the vendor data begins
+     * with a data type value whose size is dataTypeSize.
+     * @param id the id of the vendor, typically the OUI of a vendor
+     *     prefixed with 0.
+     * @param dataTypeSize the size of the integral data type value
+     *     at the beginning of the vendor data. The value must be the
+     *     size of an integeral data type (i.e. either 1,2,4 or 8).
+     */
+    public OFBasicVendorId(int id, int dataTypeSize) {
+        super(id);
+        assert (dataTypeSize == 1) || (dataTypeSize == 2) ||
+               (dataTypeSize == 4) || (dataTypeSize == 8);
+        this.dataTypeSize = dataTypeSize;
+    }
+
+    /**
+     * Get the size of the data type value at the beginning of the vendor
+     * data. OFBasicVendorId assumes that this value is common across all of
+     * the vendor data formats associated with a given vendor id.
+     * @return
+     */
+    public int getDataTypeSize() {
+        return dataTypeSize;
+    }
+    
+    /**
+     * Register a vendor data type with this vendor id.
+     * @param vendorDataType
+     */
+    public void registerVendorDataType(OFBasicVendorDataType vendorDataType) {
+        dataTypeMap.put(vendorDataType.getTypeValue(), vendorDataType);
+    }
+    
+    /**
+     * Lookup the OFVendorDataType instance that has been registered with
+     * this vendor id.
+     * 
+     * @param vendorDataType the integer code that was parsed from the 
+     * @return
+     */
+    public OFVendorDataType lookupVendorDataType(int vendorDataType) {
+        return dataTypeMap.get(vendorDataType);
+    }
+
+    /**
+     * This function parses enough of the data from the buffer to be able
+     * to determine the appropriate OFVendorDataType for the data. It is meant
+     * to be a reasonably generic implementation that will work for most
+     * formats of vendor extensions. If the vendor data doesn't fit the
+     * assumptions listed below, then this method will need to be overridden
+     * to implement custom parsing.
+     * 
+     * This implementation assumes that the vendor data begins with a data
+     * type code that is used to distinguish different formats of vendor
+     * data associated with a particular vendor ID.
+     * The exact format of the data is vendor-defined, so we don't know how
+     * how big the code is (or really even if there is a code). This code
+     * assumes that the common case will be that the data does include
+     * an initial type code (i.e. so that the vendor can have multiple
+     * message/data types) and that the size is either 1, 2 or 4 bytes.
+     * The size of the initial type code is configured by the subclass of
+     * OFVendorId.
+     * 
+     * @param data the channel buffer containing the vendor data.
+     * @param length the length to the end of the enclosing message
+     * @return the OFVendorDataType that can be used to instantiate the
+     *         appropriate subclass of OFVendorData.
+     */
+    public OFVendorDataType parseVendorDataType(ChannelBuffer data, int length) {
+        OFVendorDataType vendorDataType = null;
+        
+        // Parse out the type code from the vendor data.
+        long dataTypeValue = 0;
+        if ((length == 0) || (length >= dataTypeSize)) {
+            switch (dataTypeSize) {
+                case 1:
+                    dataTypeValue = data.readByte();
+                    break;
+                case 2:
+                    dataTypeValue = data.readShort();
+                    break;
+                case 4:
+                    dataTypeValue = data.readInt();
+                    break;
+                case 8:
+                    dataTypeValue = data.readLong();
+                    break;
+                default:
+                    // This would be indicative of a coding error where the
+                    // dataTypeSize was specified incorrectly. This should have been
+                    // caught in the constructor for OFVendorId.
+                    assert false;
+            }
+            
+            vendorDataType = dataTypeMap.get(dataTypeValue);
+        }
+        
+        // If we weren't able to parse/map the data to a known OFVendorDataType,
+        // then map it to a generic vendor data type.
+        if (vendorDataType == null) {
+            vendorDataType = new OFBasicVendorDataType(dataTypeValue,
+                new Instantiable<OFVendorData>() {
+                    @Override
+                    public OFVendorData instantiate() {
+                        return new OFByteArrayVendorData();
+                    }
+                }
+            );
+        }
+        
+        return vendorDataType;
+    }
+
+}
diff --git a/src/main/java/org/openflow/protocol/vendor/OFByteArrayVendorData.java b/src/main/java/org/openflow/protocol/vendor/OFByteArrayVendorData.java
new file mode 100644
index 000000000..08fa00317
--- /dev/null
+++ b/src/main/java/org/openflow/protocol/vendor/OFByteArrayVendorData.java
@@ -0,0 +1,94 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson & Rob Sherwood, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package org.openflow.protocol.vendor;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+
+/**
+ * Basic implementation of OFVendorData that just treats the data as a
+ * byte array. This is used if there's an OFVendor message where there's
+ * no registered OFVendorId or no specific OFVendorDataType that can be
+ * determined from the data.
+ * 
+ * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com)
+ */
+public class OFByteArrayVendorData implements OFVendorData {
+
+    protected byte[] bytes;
+    
+    /**
+     * Construct vendor data with an empty byte array.
+     */
+    public OFByteArrayVendorData() {
+    }
+    
+    /**
+     * Construct vendor data with the specified byte array.
+     * @param bytes
+     */
+    public OFByteArrayVendorData(byte[] bytes) {
+        this.bytes = bytes;
+    }
+    
+    /**
+     * Get the associated byte array for this vendor data.
+     * @return the byte array containing the raw vendor data.
+     */
+    public byte[] getBytes() {
+        return bytes;
+    }
+    
+    /**
+     * Set the byte array for the vendor data.
+     * @param bytes the raw byte array containing the vendor data.
+     */
+    public void setBytes(byte[] bytes) {
+        this.bytes = bytes;
+    }
+    
+    /**
+     * Get the length of the vendor data. In this case it's just then length
+     * of the underlying byte array.
+     * @return the length of the vendor data
+     */
+    @Override
+    public int getLength() {
+        return (bytes != null) ? bytes.length : 0;
+    }
+
+    /**
+     * Read the vendor data from the ChannelBuffer into the byte array.
+     * @param data the channel buffer from which we're deserializing
+     * @param length the length to the end of the enclosing message
+     */
+    @Override
+    public void readFrom(ChannelBuffer data, int length) {
+        bytes = new byte[length];
+        data.readBytes(bytes);
+    }
+
+    /**
+     * Write the vendor data bytes to the ChannelBuffer
+     * @param data the channel buffer to which we're serializing
+     */
+    @Override
+    public void writeTo(ChannelBuffer data) {
+        if (bytes != null)
+            data.writeBytes(bytes);
+    }
+}
diff --git a/src/main/java/org/openflow/protocol/vendor/OFVendorData.java b/src/main/java/org/openflow/protocol/vendor/OFVendorData.java
new file mode 100644
index 000000000..6dfb4e6cb
--- /dev/null
+++ b/src/main/java/org/openflow/protocol/vendor/OFVendorData.java
@@ -0,0 +1,44 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson & Rob Sherwood, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package org.openflow.protocol.vendor;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+
+/**
+ * The base class for all vendor data.
+ *
+ * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com)
+ */
+public interface OFVendorData {
+    /**
+     * @return length of the data
+     */
+    public int getLength();
+    
+    /**
+     * Read the vendor data from the specified ChannelBuffer
+     * @param data
+     */
+    public void readFrom(ChannelBuffer data, int length);
+
+    /**
+     * Write the vendor data to the specified ChannelBuffer
+     * @param data
+     */
+    public void writeTo(ChannelBuffer data);
+}
diff --git a/src/main/java/org/openflow/protocol/vendor/OFVendorDataType.java b/src/main/java/org/openflow/protocol/vendor/OFVendorDataType.java
new file mode 100644
index 000000000..ecae48239
--- /dev/null
+++ b/src/main/java/org/openflow/protocol/vendor/OFVendorDataType.java
@@ -0,0 +1,79 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson & Rob Sherwood, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package org.openflow.protocol.vendor;
+
+import org.openflow.protocol.Instantiable;
+
+/**
+ * Class that represents a specific vendor data type format in an
+ * OFVendor message. Typically the vendor data will begin with an integer
+ * code that determines the format of the rest of the data, but this
+ * class does not assume that. It's basically just a holder for an
+ * instantiator of the appropriate subclass of OFVendorData.
+ *
+ * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com)
+ */
+public class OFVendorDataType {
+
+    /**
+     * Object that instantiates the subclass of OFVendorData 
+     * associated with this data type.
+     */
+    protected Instantiable<OFVendorData> instantiable;
+
+    /**
+     * Construct an empty vendor data type.
+     */
+    public OFVendorDataType() {
+        super();
+    }
+
+    /**
+     * Construct a vendor data type with the specified instantiable.
+     * @param instantiable object that creates the subclass of OFVendorData
+     *     associated with this data type.
+     */
+    public OFVendorDataType(Instantiable<OFVendorData> instantiable) {
+        this.instantiable = instantiable;
+    }
+    
+    /**
+     * Returns a new instance of a subclass of OFVendorData associated with
+     * this OFVendorDataType.
+     * 
+     * @return the new object
+     */
+    public OFVendorData newInstance() {
+        return instantiable.instantiate();
+    }
+
+    /**
+     * @return the instantiable
+     */
+    public Instantiable<OFVendorData> getInstantiable() {
+        return instantiable;
+    }
+
+    /**
+     * @param instantiable the instantiable to set
+     */
+    public void setInstantiable(Instantiable<OFVendorData> instantiable) {
+        this.instantiable = instantiable;
+    }
+
+}
diff --git a/src/main/java/org/openflow/protocol/vendor/OFVendorId.java b/src/main/java/org/openflow/protocol/vendor/OFVendorId.java
new file mode 100644
index 000000000..f0af8a763
--- /dev/null
+++ b/src/main/java/org/openflow/protocol/vendor/OFVendorId.java
@@ -0,0 +1,85 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson & Rob Sherwood, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package org.openflow.protocol.vendor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+
+/**
+ * Base class for the vendor ID corresponding to vendor extensions from a
+ * given vendor. It is responsible for knowing how to parse out some sort of
+ * data type value from the vendor data in an OFVendor message so that we can
+ * dispatch to the different subclasses of OFVendorData corresponding to the
+ * different formats of data for the vendor extensions.
+ * 
+ * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com)
+ */
+public abstract class OFVendorId {
+    static Map<Integer, OFVendorId> mapping = new HashMap<Integer, OFVendorId>();
+
+    /**
+     * The vendor id value, typically the OUI of the vendor prefixed with 0.
+     */
+    protected int id;
+    
+    /**
+     * Register a new vendor id.
+     * @param vendorId the vendor id to register
+     */
+    public static void registerVendorId(OFVendorId vendorId) {
+        mapping.put(vendorId.getId(), vendorId);
+    }
+    
+    /**
+     * Lookup the OFVendorId instance corresponding to the given id value.
+     * @param id the integer vendor id value
+     * @return the corresponding OFVendorId that's been registered for the
+     *     given value, or null if there id has not been registered.
+     */
+    public static OFVendorId lookupVendorId(int id) {
+        return mapping.get(id);
+    }
+    
+    /**
+     * Create an OFVendorId with the give vendor id value
+     * @param id
+     */
+    public OFVendorId(int id) {
+        this.id = id;
+    }
+    
+    /**
+     * @return the vendor id value
+     */
+    public int getId() {
+        return id;
+    }
+    
+    /**
+     * This function parses enough of the data from the channel buffer to be
+     * able to determine the appropriate OFVendorDataType for the data.
+     * 
+     * @param data the channel buffer containing the vendor data.
+     * @param length the length to the end of the enclosing message
+     * @return the OFVendorDataType that can be used to instantiate the
+     *         appropriate subclass of OFVendorData.
+     */
+    public abstract OFVendorDataType parseVendorDataType(ChannelBuffer data, int length);
+}
diff --git a/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorData.java b/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorData.java
new file mode 100644
index 000000000..d6946a7b9
--- /dev/null
+++ b/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorData.java
@@ -0,0 +1,97 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson & Rob Sherwood, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package org.openflow.vendor.nicira;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.openflow.protocol.vendor.OFVendorData;
+
+/**
+ * Base class for vendor data corresponding to a Nicira vendor extension.
+ * Nicira vendor data always starts with a 4-byte integer data type value.
+ * 
+ * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com)
+ */
+public class OFNiciraVendorData implements OFVendorData {
+
+    /**
+     * The value of the integer data type at the beginning of the vendor data
+     */
+    protected int dataType;
+    
+    /**
+     * Construct empty (i.e. unspecified data type) Nicira vendor data.
+     */
+    public OFNiciraVendorData() {
+    }
+    
+    /**
+     * Contruct Nicira vendor data with the specified data type
+     * @param dataType the data type value at the beginning of the vendor data.
+     */
+    public OFNiciraVendorData(int dataType) {
+        this.dataType = dataType;
+    }
+    
+    /**
+     * Get the data type value at the beginning of the vendor data
+     * @return the integer data type value
+     */
+    public int getDataType() {
+        return dataType;
+    }
+    
+    /**
+     * Set the data type value
+     * @param dataType the integer data type value at the beginning of the
+     *     vendor data.
+     */
+    public void setDataType(int dataType) {
+        this.dataType = dataType;
+    }
+    
+    /**
+     * Get the length of the vendor data. This implementation will normally
+     * be the superclass for another class that will override this to return
+     * the overall vendor data length. This implementation just returns the 
+     * length of the part that includes the 4-byte integer data type value
+     * at the beginning of the vendor data.
+     */
+    @Override
+    public int getLength() {
+        return 4;
+    }
+
+    /**
+     * Read the vendor data from the ChannelBuffer
+     * @param data the channel buffer from which we're deserializing
+     * @param length the length to the end of the enclosing message
+     */
+    @Override
+    public void readFrom(ChannelBuffer data, int length) {
+        dataType = data.readInt();
+    }
+
+    /**
+     * Write the vendor data to the ChannelBuffer
+     * @param data the channel buffer to which we're serializing
+     */
+    @Override
+    public void writeTo(ChannelBuffer data) {
+        data.writeInt(dataType);
+    }
+}
diff --git a/src/main/java/org/openflow/vendor/nicira/OFRoleReplyVendorData.java b/src/main/java/org/openflow/vendor/nicira/OFRoleReplyVendorData.java
new file mode 100644
index 000000000..302c911e4
--- /dev/null
+++ b/src/main/java/org/openflow/vendor/nicira/OFRoleReplyVendorData.java
@@ -0,0 +1,66 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson & Rob Sherwood, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package org.openflow.vendor.nicira;
+
+import org.openflow.protocol.Instantiable;
+import org.openflow.protocol.vendor.OFVendorData;
+
+/**
+ * Subclass of OFVendorData representing the vendor data associated with
+ * a role reply vendor extension.
+ * 
+ * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com)
+ */
+public class OFRoleReplyVendorData extends OFRoleVendorData {
+
+    protected static Instantiable<OFVendorData> instantiable =
+            new Instantiable<OFVendorData>() {
+                public OFVendorData instantiate() {
+                    return new OFRoleReplyVendorData();
+                }
+            };
+
+    /**
+     * @return a subclass of Instantiable<OFVendorData> that instantiates
+     *         an instance of OFRoleReplyVendorData.
+     */
+    public static Instantiable<OFVendorData> getInstantiable() {
+        return instantiable;
+    }
+
+    /**
+     * The data type value for a role reply
+     */
+    public static final int NXT_ROLE_REPLY = 10;
+
+    /**
+     * Construct a role reply vendor data with an unspecified role value.
+     */
+    public OFRoleReplyVendorData() {
+        super(NXT_ROLE_REPLY);
+    }
+    
+    /**
+     * Construct a role reply vendor data with the specified role value.
+     * @param role the role value for the role reply. Should be one of
+     *      NX_ROLE_OTHER, NX_ROLE_MASTER or NX_ROLE_SLAVE.
+     */
+    public OFRoleReplyVendorData(int role) {
+        super(NXT_ROLE_REPLY, role);
+    }
+}
diff --git a/src/main/java/org/openflow/vendor/nicira/OFRoleRequestVendorData.java b/src/main/java/org/openflow/vendor/nicira/OFRoleRequestVendorData.java
new file mode 100644
index 000000000..88eb58124
--- /dev/null
+++ b/src/main/java/org/openflow/vendor/nicira/OFRoleRequestVendorData.java
@@ -0,0 +1,66 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson & Rob Sherwood, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package org.openflow.vendor.nicira;
+
+import org.openflow.protocol.Instantiable;
+import org.openflow.protocol.vendor.OFVendorData;
+
+/**
+ * Subclass of OFVendorData representing the vendor data associated with
+ * a role request vendor extension.
+ * 
+ * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com)
+ */
+public class OFRoleRequestVendorData extends OFRoleVendorData {
+
+    protected static Instantiable<OFVendorData> instantiable =
+            new Instantiable<OFVendorData>() {
+                public OFVendorData instantiate() {
+                    return new OFRoleRequestVendorData();
+                }
+            };
+
+    /**
+     * @return a subclass of Instantiable<OFVendorData> that instantiates
+     *         an instance of OFRoleRequestVendorData.
+     */
+    public static Instantiable<OFVendorData> getInstantiable() {
+        return instantiable;
+    }
+
+    /**
+     * The data type value for a role request
+     */
+    public static final int NXT_ROLE_REQUEST = 9;
+
+    /**
+     * Construct a role request vendor data with an unspecified role value.
+     */
+    public OFRoleRequestVendorData() {
+        super(NXT_ROLE_REQUEST);
+    }
+    
+    /**
+     * Construct a role request vendor data with the specified role value.
+     * @param role the role value for the role request. Should be one of
+     *      NX_ROLE_OTHER, NX_ROLE_MASTER or NX_ROLE_SLAVE.
+     */
+    public OFRoleRequestVendorData(int role) {
+        super(NXT_ROLE_REQUEST, role);
+    }
+}
diff --git a/src/main/java/org/openflow/vendor/nicira/OFRoleVendorData.java b/src/main/java/org/openflow/vendor/nicira/OFRoleVendorData.java
new file mode 100644
index 000000000..e7c8bf213
--- /dev/null
+++ b/src/main/java/org/openflow/vendor/nicira/OFRoleVendorData.java
@@ -0,0 +1,113 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson & Rob Sherwood, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package org.openflow.vendor.nicira;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+
+/**
+ * Class that represents the vendor data in the role request
+ * extension implemented by Open vSwitch to support high availability.
+ * 
+ * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com)
+ */
+public class OFRoleVendorData extends OFNiciraVendorData {
+    
+    /**
+     * Role value indicating that the controller is in the OTHER role.
+     */
+    public static final int NX_ROLE_OTHER = 0;
+    
+    /**
+     * Role value indicating that the controller is in the MASTER role.
+     */
+    public static final int NX_ROLE_MASTER = 1;
+    
+    /**
+     * Role value indicating that the controller is in the SLAVE role.
+     */
+    public static final int NX_ROLE_SLAVE = 2;
+
+    protected int role;
+
+    /** 
+     * Construct an uninitialized OFRoleVendorData
+     */
+    public OFRoleVendorData() {
+        super();
+    }
+    
+    /**
+     * Construct an OFRoleVendorData with the specified data type
+     * (i.e. either request or reply) and an unspecified role.
+     * @param dataType
+     */
+    public OFRoleVendorData(int dataType) {
+        super(dataType);
+    }
+    
+    /**
+     * Construct an OFRoleVendorData with the specified data type
+     * (i.e. either request or reply) and role (i.e. one of of
+     * master, slave, or other).
+     * @param dataType either role request or role reply data type
+     */
+    public OFRoleVendorData(int dataType, int role) {
+        super(dataType);
+        this.role = role;
+    }
+    /**
+     * @return the role value of the role vendor data
+     */
+    public int getRole() {
+        return role;
+    }
+    
+    /**
+     * @param role the role value of the role vendor data
+     */
+    public void setRole(int role) {
+        this.role = role;
+    }
+
+    /**
+     * @return the total length of the role vendor data
+     */
+    @Override
+    public int getLength() {
+        return super.getLength() + 4;
+    }
+    
+    /**
+     * Read the role vendor data from the ChannelBuffer
+     * @param data the channel buffer from which we're deserializing
+     * @param length the length to the end of the enclosing message
+     */
+    public void readFrom(ChannelBuffer data, int length) {
+        super.readFrom(data, length);
+        role = data.readInt();
+    }
+
+    /**
+     * Write the role vendor data to the ChannelBuffer
+     * @param data the channel buffer to which we're serializing
+     */
+    public void writeTo(ChannelBuffer data) {
+        super.writeTo(data);
+        data.writeInt(role);
+    }
+}
diff --git a/src/test/java/org/openflow/protocol/OFErrorTest.java b/src/test/java/org/openflow/protocol/OFErrorTest.java
index c05f583b7..85c544d98 100644
--- a/src/test/java/org/openflow/protocol/OFErrorTest.java
+++ b/src/test/java/org/openflow/protocol/OFErrorTest.java
@@ -32,14 +32,14 @@ public class OFErrorTest extends OFTestCase {
     public void testWriteRead() throws Exception {
         OFError msg = (OFError) messageFactory.getMessage(OFType.ERROR);
         msg.setMessageFactory(messageFactory);
-        msg.setErrorType((short) OFErrorType.OFPET_HELLO_FAILED.ordinal());
+        msg.setErrorType((short) OFErrorType.OFPET_HELLO_FAILED.getValue());
         msg.setErrorCode((short) OFHelloFailedCode.OFPHFC_INCOMPATIBLE
                 .ordinal());
         ChannelBuffer bb = ChannelBuffers.dynamicBuffer();
         bb.clear();
         msg.writeTo(bb);
         msg.readFrom(bb);
-        TestCase.assertEquals((short) OFErrorType.OFPET_HELLO_FAILED.ordinal(),
+        TestCase.assertEquals((short) OFErrorType.OFPET_HELLO_FAILED.getValue(),
                 msg.getErrorType());
         TestCase.assertEquals((short) OFHelloFailedCode.OFPHFC_INCOMPATIBLE
                 .ordinal(), msg.getErrorType());
@@ -49,7 +49,7 @@ public class OFErrorTest extends OFTestCase {
         bb.clear();
         msg.writeTo(bb);
         msg.readFrom(bb);
-        TestCase.assertEquals((short) OFErrorType.OFPET_HELLO_FAILED.ordinal(),
+        TestCase.assertEquals((short) OFErrorType.OFPET_HELLO_FAILED.getValue(),
                 msg.getErrorType());
         TestCase.assertEquals((short) OFHelloFailedCode.OFPHFC_INCOMPATIBLE
                 .ordinal(), msg.getErrorType());
diff --git a/src/test/java/org/openflow/protocol/OFVendorTest.java b/src/test/java/org/openflow/protocol/OFVendorTest.java
index 50b641b5c..b85a915a9 100644
--- a/src/test/java/org/openflow/protocol/OFVendorTest.java
+++ b/src/test/java/org/openflow/protocol/OFVendorTest.java
@@ -17,20 +17,199 @@
 
 package org.openflow.protocol;
 
+import java.util.Arrays;
+
 import junit.framework.TestCase;
 
 import org.jboss.netty.buffer.ChannelBuffer;
 import org.jboss.netty.buffer.ChannelBuffers;
+import org.openflow.protocol.factory.BasicFactory;
+import org.openflow.protocol.vendor.OFByteArrayVendorData;
+import org.openflow.protocol.vendor.OFBasicVendorDataType;
+import org.openflow.protocol.vendor.OFBasicVendorId;
+import org.openflow.protocol.vendor.OFVendorData;
+import org.openflow.protocol.vendor.OFVendorId;
 import org.openflow.util.OFTestCase;
 
 public class OFVendorTest extends OFTestCase {
-    public void testWriteRead() throws Exception {
+
+    public static int ACME_VENDOR_ID = 0x00112233;
+    
+    static class AcmeVendorData implements OFVendorData {
+        protected int dataType;
+        
+        public int getLength() {
+            return 4;
+        }
+        
+        public void readFrom(ChannelBuffer data, int length) {
+            dataType = data.readInt();
+        }
+        
+        public void writeTo(ChannelBuffer data) {
+            data.writeInt(dataType);
+        }
+    }
+    
+    static class AcmeVendorData1 extends AcmeVendorData {
+        public short flags;
+        public short value;
+        
+        public static int DATA_TYPE = 1;
+        
+        public AcmeVendorData1() {
+        }
+        
+        public AcmeVendorData1(short flags, short value) {
+            this.dataType = DATA_TYPE;
+            this.flags = flags;
+            this.value = value;
+        }
+        
+        public short getFlags() {
+            return flags;
+        }
+        
+        public short getValue() {
+            return value;
+        }
+        
+        public int getLength() {
+            return 8;
+        }
+        
+        public void readFrom(ChannelBuffer data, int length) {
+            super.readFrom(data, length);
+            flags = data.readShort();
+            value = data.readShort();
+
+        }
+        public void writeTo(ChannelBuffer data) {
+            super.writeTo(data);
+            data.writeShort(flags);
+            data.writeShort(value);
+        }
+        
+        public static Instantiable<OFVendorData> getInstantiable() {
+            return new Instantiable<OFVendorData>() {
+                public OFVendorData instantiate() {
+                    return new AcmeVendorData1();
+                }
+            };
+        }
+    }
+    
+    static class AcmeVendorData2 extends AcmeVendorData {
+        public int type;
+        public int subtype;
+
+        public static int DATA_TYPE = 2;
+
+        public AcmeVendorData2() {
+        }
+        
+        public AcmeVendorData2(int type, int subtype) {
+            this.dataType = DATA_TYPE;
+            this.type = type;
+            this.subtype = subtype;
+        }
+        
+        public int getType() {
+            return type;
+        }
+        
+        public int getSubtype() {
+            return subtype;
+        }
+        
+        public int getLength() {
+            return 12;
+        }
+        
+        public void readFrom(ChannelBuffer data, int length) {
+            super.readFrom(data, length);
+            type = data.readShort();
+            subtype = data.readShort();
+
+        }
+        public void writeTo(ChannelBuffer data) {
+            super.writeTo(data);
+            data.writeShort(type);
+            data.writeShort(subtype);
+        }
+        
+        public static Instantiable<OFVendorData> getInstantiable() {
+            return new Instantiable<OFVendorData>() {
+                public OFVendorData instantiate() {
+                    return new AcmeVendorData2();
+                }
+            };
+        }
+    }
+    
+    {
+        OFBasicVendorId acmeVendorId = new OFBasicVendorId(ACME_VENDOR_ID, 4);
+        OFVendorId.registerVendorId(acmeVendorId);
+        OFBasicVendorDataType acmeVendorData1 = new OFBasicVendorDataType(
+            AcmeVendorData1.DATA_TYPE, AcmeVendorData1.getInstantiable());
+        acmeVendorId.registerVendorDataType(acmeVendorData1);
+        OFBasicVendorDataType acmeVendorData2 = new OFBasicVendorDataType(
+            AcmeVendorData2.DATA_TYPE, AcmeVendorData2.getInstantiable());
+        acmeVendorId.registerVendorDataType(acmeVendorData2);
+    }
+    
+    private OFVendor makeVendorMessage(int vendor) {
         OFVendor msg = (OFVendor) messageFactory.getMessage(OFType.VENDOR);
-        msg.setVendor(1);
+        msg.setVendorDataFactory(new BasicFactory());
+        msg.setVendor(vendor);
+        return msg;
+    }
+    
+    public void testWriteRead() throws Exception {
+        OFVendor msg = makeVendorMessage(1);
         ChannelBuffer bb = ChannelBuffers.dynamicBuffer();
         bb.clear();
         msg.writeTo(bb);
         msg.readFrom(bb);
         TestCase.assertEquals(1, msg.getVendor());
     }
+    
+    public void testVendorData() throws Exception {
+        OFVendor msg = makeVendorMessage(ACME_VENDOR_ID);
+        OFVendorData vendorData = new AcmeVendorData1((short)11, (short)22);
+        msg.setVendorData(vendorData);
+        msg.setLengthU(OFVendor.MINIMUM_LENGTH + vendorData.getLength());
+        ChannelBuffer bb = ChannelBuffers.dynamicBuffer();
+        bb.clear();
+        msg.writeTo(bb);
+        msg.readFrom(bb);
+        assertEquals(ACME_VENDOR_ID, msg.getVendor());
+        AcmeVendorData1 vendorData1 = (AcmeVendorData1) msg.getVendorData();
+        assertEquals(11, vendorData1.getFlags());
+        assertEquals(22, vendorData1.getValue());
+        
+        vendorData = new AcmeVendorData2(33, 44);
+        msg.setVendorData(vendorData);
+        msg.setLengthU(OFVendor.MINIMUM_LENGTH + vendorData.getLength());
+        bb.clear();
+        msg.writeTo(bb);
+        msg.readFrom(bb);
+        assertEquals(ACME_VENDOR_ID, msg.getVendor());
+        AcmeVendorData2 vendorData2 = (AcmeVendorData2) msg.getVendorData();
+        assertEquals(33, vendorData2.getType());
+        assertEquals(44, vendorData2.getSubtype());
+        
+        final int DUMMY_VENDOR_ID = 55;
+        msg.setVendor(DUMMY_VENDOR_ID);
+        byte[] genericVendorDataBytes = new byte[] {0x55, 0x66};
+        vendorData = new OFByteArrayVendorData(genericVendorDataBytes);
+        msg.setVendorData(vendorData);
+        msg.setLengthU(OFVendor.MINIMUM_LENGTH + vendorData.getLength());
+        bb.clear();
+        msg.writeTo(bb);
+        msg.readFrom(bb);
+        assertEquals(DUMMY_VENDOR_ID, msg.getVendor());
+        OFByteArrayVendorData genericVendorData = (OFByteArrayVendorData) msg.getVendorData();
+        assertTrue(Arrays.equals(genericVendorDataBytes, genericVendorData.getBytes()));
+    }
 }
-- 
GitLab