Merge remote-tracking branch 'upstream/master'
diff --git a/Makefile b/Makefile
index dbaf08b..fe5f19e 100644
--- a/Makefile
+++ b/Makefile
@@ -92,7 +92,7 @@
 
 clean:
 	rm -rf loxi_output # only delete generated files in the default directory
-	rm -f loxigen.log loxigen-test.log .loxi_ts.c .loxi_ts.python .loxi_ts.java
+	rm -f loxigen.log loxigen-test.log .loxi_ts.*
 
 debug:
 	@echo "LOXI_OUTPUT_DIR=\"${LOXI_OUTPUT_DIR}\""
diff --git a/c_gen/c_test_gen.py b/c_gen/c_test_gen.py
index d237cfe..e1b6913 100644
--- a/c_gen/c_test_gen.py
+++ b/c_gen/c_test_gen.py
@@ -128,6 +128,11 @@
             m_name == "experimenter" or
             m_name == "subtype")):
         return True
+
+    if (cls in ["of_bsn_lacp_stats_request", "of_bsn_lacp_stats_reply"] and (
+            m_name == "experimenter" or
+            m_name == "subtype")):
+        return True
     return loxi_utils.skip_member_name(m_name) or m_type not in scalar_types
 
 def gen_fill_string(out):
diff --git a/c_gen/c_type_maps.py b/c_gen/c_type_maps.py
index 8883193..4fc1652 100644
--- a/c_gen/c_type_maps.py
+++ b/c_gen/c_type_maps.py
@@ -503,6 +503,7 @@
     uint16_t stats_type;
     uint16_t err_type;
     uint8_t flow_mod_cmd;
+    uint32_t experimenter, subtype;
 
     if (length < OF_MESSAGE_MIN_LENGTH) {
         return OF_OBJECT_INVALID;
@@ -541,10 +542,23 @@
             return OF_OBJECT_INVALID;
         }
         stats_type = of_message_stats_type_get(msg);
-        if (obj_id == OF_STATS_REQUEST) {
-            obj_id = of_stats_request_to_object_id(stats_type, ver);
+        if (stats_type == OF_STATS_TYPE_EXPERIMENTER) {
+            if (length < OF_MESSAGE_STATS_EXPERIMENTER_MIN_LENGTH) {
+                return OF_OBJECT_INVALID;
+            }
+            experimenter = of_message_stats_experimenter_id_get(msg);
+            subtype = of_message_stats_experimenter_subtype_get(msg);
+            if (obj_id == OF_STATS_REQUEST) {
+                obj_id = of_experimenter_stats_request_to_object_id(experimenter, subtype, ver);
+            } else {
+                obj_id = of_experimenter_stats_reply_to_object_id(experimenter, subtype, ver);
+            }
         } else {
-            obj_id = of_stats_reply_to_object_id(stats_type, ver);
+            if (obj_id == OF_STATS_REQUEST) {
+                obj_id = of_stats_request_to_object_id(stats_type, ver);
+            } else {
+                obj_id = of_stats_reply_to_object_id(stats_type, ver);
+            }
         }
     }
 
@@ -945,6 +959,9 @@
  * top level message: Action, instruction, error, stats, queue_props, oxm
  */
 #define OF_EXPERIMENTER_TYPE 0xffff
+
+int of_experimenter_stats_request_to_object_id(uint32_t experimenter, uint32_t subtype, int ver);
+int of_experimenter_stats_reply_to_object_id(uint32_t experimenter, uint32_t subtype, int ver);
 """)
     gen_type_to_obj_map_functions(out)
     gen_obj_to_type_map_functions(out)
@@ -1047,6 +1064,17 @@
     if ((type = of_object_to_stats_type(id, ver)) >= 0) {
         /* It's a stats obj */
         of_message_stats_type_set(msg, type);
+        if (type == OF_STATS_TYPE_EXPERIMENTER) {
+            switch (id) {
+            case OF_BSN_LACP_STATS_REQUEST:
+            case OF_BSN_LACP_STATS_REPLY:
+                of_message_stats_experimenter_id_set(msg, OF_EXPERIMENTER_ID_BSN);
+                of_message_stats_experimenter_subtype_set(msg, 1);
+                break;
+            default:
+                break;
+            }
+        }
     }
     if ((type = of_object_to_error_type(id, ver)) >= 0) {
         /* It's an error obj */
diff --git a/c_gen/templates/loci_show.h b/c_gen/templates/loci_show.h
index f4d3f92..04d863f 100644
--- a/c_gen/templates/loci_show.h
+++ b/c_gen/templates/loci_show.h
@@ -351,5 +351,16 @@
 #define LOCI_SHOW_u32_bsn_l3_interface_class_id(writer, cookie, val) LOCI_SHOW_u32(writer, cookie, val)
 #define LOCI_SHOW_u32_bsn_l3_src_class_id(writer, cookie, val) LOCI_SHOW_u32(writer, cookie, val)
 #define LOCI_SHOW_u32_bsn_l3_dst_class_id(writer, cookie, val) LOCI_SHOW_u32(writer, cookie, val)
+#define LOCI_SHOW_u8_convergence_status(writer, cookie, val) LOCI_SHOW_u8(writer, cookie, val)
+#define LOCI_SHOW_u16_actor_sys_priority(writer, cookie, val) LOCI_SHOW_u16(writer, cookie, val)
+#define LOCI_SHOW_mac_actor_sys_mac(writer, cookie, val) LOCI_SHOW_mac(writer, cookie, val)
+#define LOCI_SHOW_u16_actor_port_priority(writer, cookie, val) LOCI_SHOW_u16(writer, cookie, val)
+#define LOCI_SHOW_u16_actor_port_num(writer, cookie, val) LOCI_SHOW_u16(writer, cookie, val)
+#define LOCI_SHOW_u16_actor_key(writer, cookie, val) LOCI_SHOW_u16(writer, cookie, val)
+#define LOCI_SHOW_u16_partner_sys_priority(writer, cookie, val) LOCI_SHOW_u16(writer, cookie, val)
+#define LOCI_SHOW_mac_partner_sys_mac(writer, cookie, val) LOCI_SHOW_mac(writer, cookie, val)
+#define LOCI_SHOW_u16_partner_port_priority(writer, cookie, val) LOCI_SHOW_u16(writer, cookie, val)
+#define LOCI_SHOW_u16_partner_port_num(writer, cookie, val) LOCI_SHOW_u16(writer, cookie, val)
+#define LOCI_SHOW_u16_partner_key(writer, cookie, val) LOCI_SHOW_u16(writer, cookie, val)
 
 #endif /* _LOCI_SHOW_H_ */
diff --git a/c_gen/templates/of_message.h b/c_gen/templates/of_message.h
index 165fe51..c1b6785 100644
--- a/c_gen/templates/of_message.h
+++ b/c_gen/templates/of_message.h
@@ -58,6 +58,10 @@
 #define OF_MESSAGE_EXPERIMENTER_SUBTYPE_OFFSET 12
 #define OF_MESSAGE_EXPERIMENTER_MIN_LENGTH 16
 
+#define OF_MESSAGE_STATS_EXPERIMENTER_ID_OFFSET 16
+#define OF_MESSAGE_STATS_EXPERIMENTER_SUBTYPE_OFFSET 20
+#define OF_MESSAGE_STATS_EXPERIMENTER_MIN_LENGTH 24
+
 /**
  * The "default" free message function; NULL means use nominal malloc/free
  */
@@ -270,4 +274,42 @@
     }
 }
 
+/**
+ * @brief Get/set stats request/reply experimenter ID of a message
+ * @param msg Pointer to the message buffer of sufficient length
+ * @param experimenter_id Data for set operation
+ * @returns get returns experimenter id in host order
+ */
+
+static inline uint32_t
+of_message_stats_experimenter_id_get(of_message_t msg) {
+    uint32_t val;
+    buf_u32_get(msg + OF_MESSAGE_STATS_EXPERIMENTER_ID_OFFSET, &val);
+    return val;
+}
+
+static inline void
+of_message_stats_experimenter_id_set(of_message_t msg, uint32_t experimenter_id) {
+    buf_u32_set(msg + OF_MESSAGE_STATS_EXPERIMENTER_ID_OFFSET, experimenter_id);
+}
+
+/**
+ * @brief Get/set stats request/reply experimenter subtype of a message
+ * @param msg Pointer to the message buffer of sufficient length
+ * @param subtype Data for set operation
+ * @returns get returns experimenter subtype in host order
+ */
+
+static inline uint32_t
+of_message_stats_experimenter_subtype_get(of_message_t msg) {
+    uint32_t val;
+    buf_u32_get(msg + OF_MESSAGE_STATS_EXPERIMENTER_SUBTYPE_OFFSET, &val);
+    return val;
+}
+
+static inline void
+of_message_stats_experimenter_subtype_set(of_message_t msg, uint32_t subtype) {
+    buf_u32_set(msg + OF_MESSAGE_STATS_EXPERIMENTER_SUBTYPE_OFFSET, subtype);
+}
+
 #endif /* _OF_MESSAGE_H_ */
diff --git a/c_gen/templates/of_type_maps.c b/c_gen/templates/of_type_maps.c
index fabd25f..30c73ac 100644
--- a/c_gen/templates/of_type_maps.c
+++ b/c_gen/templates/of_type_maps.c
@@ -840,3 +840,27 @@
 
     return OF_ERROR_NONE;
 }
+
+int
+of_experimenter_stats_request_to_object_id(uint32_t experimenter, uint32_t subtype, int ver)
+{
+    switch (experimenter) {
+    case OF_EXPERIMENTER_ID_BSN:
+        switch (subtype) {
+        case 1: return OF_BSN_LACP_STATS_REQUEST;
+        }
+    }
+    return OF_OBJECT_INVALID;
+}
+
+int
+of_experimenter_stats_reply_to_object_id(uint32_t experimenter, uint32_t subtype, int ver)
+{
+    switch (experimenter) {
+    case OF_EXPERIMENTER_ID_BSN:
+        switch (subtype) {
+        case 1: return OF_BSN_LACP_STATS_REPLY;
+        }
+    }
+    return OF_OBJECT_INVALID;
+}
diff --git a/java_gen/codegen.py b/java_gen/codegen.py
index 4369b30..f349f9e 100644
--- a/java_gen/codegen.py
+++ b/java_gen/codegen.py
@@ -37,35 +37,35 @@
 from loxi_ir import *
 import lang_java
 import test_data
+from collections import namedtuple
 from import_cleaner import ImportCleaner
 
 import loxi_utils.loxi_utils as loxi_utils
 
 import java_gen.java_model as java_model
 
-def gen_all_java(out, name):
-    # close the virtual file - we don't need it
-    out.close()
+def gen_all_java():
     basedir= '%s/openflowj' % of_g.options.install_dir
     print "Outputting to %s" % basedir
     if os.path.exists(basedir):
         shutil.rmtree(basedir)
     os.makedirs(basedir)
     copy_prewrite_tree(basedir)
-    gen = JavaGenerator(basedir)
+    gen = JavaGenerator(basedir, JavaGeneratorOptions(instrument=True))
     gen.create_of_interfaces()
     gen.create_of_classes()
     gen.create_of_const_enums()
     gen.create_of_factories()
 
-
+JavaGeneratorOptions = namedtuple("JavaGeneratorOptions", ("instrument",))
 
 class JavaGenerator(object):
     templates_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'templates')
 
-    def __init__(self, basedir):
+    def __init__(self, basedir, gen_opts):
         self.basedir = basedir
         self.java_model = java_model.model
+        self.gen_opts = gen_opts
 
     def render_class(self, clazz, template, src_dir=None, **context):
         if not src_dir:
@@ -74,6 +74,7 @@
         context['class_name'] = clazz.name
         context['package'] = clazz.package
         context['template_dir'] = self.templates_dir
+        context['genopts']= self.gen_opts
 
         filename = os.path.join(self.basedir, src_dir, "%s/%s.java" % (clazz.package.replace(".", "/"), clazz.name))
         dirname = os.path.dirname(filename)
@@ -83,14 +84,13 @@
         print "filename: %s" % filename
         with open(filename, "w") as f:
             loxi_utils.render_template(f, template, [self.templates_dir], context, prefix=prefix)
-        
+
         try:
             cleaner = ImportCleaner(filename)
             cleaner.find_used_imports()
             cleaner.rewrite_file(filename)
         except:
             print 'Cannot clean imports from file %s' % filename
-        
 
     def create_of_const_enums(self):
         for enum in self.java_model.enums:
diff --git a/java_gen/java_model.py b/java_gen/java_model.py
index 85a6c0d..a463d69 100644
--- a/java_gen/java_model.py
+++ b/java_gen/java_model.py
@@ -60,7 +60,11 @@
     interface_blacklist = set( ("OFUint8", "OFUint32",))
     # registry of interface properties that should not be generated
     # map: $java_type -> set(java_name_property)
-    read_blacklist = defaultdict(lambda: set(), OFExperimenter=set(('data','subtype')), OFActionExperimenter=set(('data',)))
+    read_blacklist = defaultdict(lambda: set(),
+        OFExperimenter=set(('data','subtype')),
+        OFActionExperimenter=set(('data',)),
+        OFExperimenterStatsRequest=set(('data','subtype')),
+        OFExperimenterStatsReply=set(('data','subtype')))
     # map: $java_type -> set(java_name_property)
     write_blacklist = defaultdict(lambda: set(), OFOxm=set(('typeLen',)), OFAction=set(('type',)), OFInstruction=set(('type',)), OFFlowMod=set(('command', )), OFExperimenter=set(('data','subtype')), OFActionExperimenter=set(('data',)))
     # interfaces that are virtual
@@ -79,8 +83,8 @@
                 "OFOxmEthSrcMasked":        OxmMapEntry("MacAddress", "ETH_SRC", True),
                 "OFOxmEthType":             OxmMapEntry("EthType", "ETH_TYPE", False),
                 "OFOxmEthTypeMasked":       OxmMapEntry("EthType", "ETH_TYPE", True),
-                "OFOxmVlanVid":             OxmMapEntry("VlanVid", "VLAN_VID", False),
-                "OFOxmVlanVidMasked":       OxmMapEntry("VlanVid", "VLAN_VID", True),
+                "OFOxmVlanVid":             OxmMapEntry("OFVlanVidMatch", "VLAN_VID", False),
+                "OFOxmVlanVidMasked":       OxmMapEntry("OFVlanVidMatch", "VLAN_VID", True),
                 "OFOxmVlanPcp":             OxmMapEntry("VlanPcp", "VLAN_PCP", False),
                 "OFOxmVlanPcpMasked":       OxmMapEntry("VlanPcp", "VLAN_PCP", True),
                 "OFOxmIpDscp":              OxmMapEntry("IpDscp", "IP_DSCP", False),
@@ -510,6 +514,14 @@
         # inheritance information from the versioned lox_ir classes.
         if re.match(r'OFStatsRequest$', self.name):
             return ("", "OFMessage", "T extends OFStatsReply")
+        elif re.match(r'OFBsnStatsRequest$', self.name):
+            return ("", "OFExperimenterStatsRequest", None)
+        elif re.match(r'OFBsnStatsReply$', self.name):
+            return ("", "OFExperimenterStatsReply", None)
+        elif re.match(r'OFBsn.+StatsRequest$', self.name):
+            return ("", "OFBsnStatsRequest", None)
+        elif re.match(r'OFBsn.+StatsReply$', self.name):
+            return ("", "OFBsnStatsReply", None)
         elif re.match(r'OF.+StatsRequest$', self.name):
             return ("", "OFStatsRequest<{}>".format(re.sub(r'Request$', 'Reply', self.name)), None)
         elif re.match(r'OF.+StatsReply$', self.name):
@@ -602,6 +614,7 @@
                     JavaVirtualMember(self, "mask", java_type.generic_t),
                     JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
                     JavaVirtualMember(self, "masked", java_type.boolean),
+                    JavaVirtualMember(self, "canonical", java_type.make_oxm_jtype("T"))
                    ]
         elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
             field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
@@ -611,6 +624,8 @@
             virtual_members += [
                     JavaVirtualMember(self, "matchField", field_type),
                     JavaVirtualMember(self, "masked", java_type.boolean),
+                    JavaVirtualMember(self, "canonical", java_type.make_oxm_jtype(model.oxm_map[self.name].type_name),
+                            custom_template=lambda builder: "OFOxm{}_getCanonical.java".format(".Builder" if builder else "")),
                    ]
             if not find(lambda x: x.name == "mask", self.ir_model_members):
                 virtual_members.append(JavaVirtualMember(self, "mask", find(lambda x: x.name == "value", self.ir_model_members).java_type))
@@ -681,7 +696,7 @@
 
     @property
     def variable_name(self):
-        return self.name[3:]
+        return self.name[2].lower() + self.name[3:]
 
     @property
     def length(self):
@@ -745,18 +760,21 @@
                 virtual_members += [
                     JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
                     JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
-                   ]
+                    ]
             else:
                 virtual_members += [
                     JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
                     JavaVirtualMember(self, "masked", java_type.boolean, "false"),
-                   ]
-
+                 ]
         if not find(lambda m: m.name == "version", self.ir_model_members):
             virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version, "OFVersion.%s" % self.version.constant_version))
 
         return tuple(virtual_members)
 
+    @memoize
+    def member_by_name(self, name):
+        return find(lambda m: m.name == name, self.members)
+
     def all_versions(self):
         return [ JavaOFVersion(int_version)
                  for int_version in of_g.unified[self.c_name]
@@ -983,9 +1001,10 @@
 
 class JavaVirtualMember(JavaMember):
     """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
-    def __init__(self, msg, name, java_type, value=None):
+    def __init__(self, msg, name, java_type, value=None, custom_template=None):
         JavaMember.__init__(self, msg, name, java_type, member=None)
         self._value = value
+        self.custom_template = custom_template
 
     @property
     def is_fixed_value(self):
diff --git a/java_gen/java_type.py b/java_gen/java_type.py
index 2772a86..333efd8 100644
--- a/java_gen/java_type.py
+++ b/java_gen/java_type.py
@@ -358,9 +358,11 @@
             write="$name.write2Bytes(bb)",
             default="EthType.NONE")
 vlan_vid = JType("VlanVid")\
-        .op(version=1, read="VlanVid.read2BytesOF10(bb)", write="$name.write2BytesOF10(bb)", default="VlanVid.NONE") \
-        .op(version=2, read="VlanVid.read2BytesOF10(bb)", write="$name.write2BytesOF10(bb)", default="VlanVid.NONE") \
-        .op(version=ANY, read="VlanVid.read2Bytes(bb)", write="$name.write2Bytes(bb)", default="VlanVid.NONE")
+        .op(version=ANY, read="VlanVid.read2Bytes(bb)", write="$name.write2Bytes(bb)", default="VlanVid.ZERO")
+vlan_vid_match = JType("OFVlanVidMatch")\
+        .op(version=1, read="OFVlanVidMatch.read2BytesOF10(bb)", write="$name.write2BytesOF10(bb)", default="OFVlanVidMatch.NONE") \
+        .op(version=2, read="OFVlanVidMatch.read2BytesOF10(bb)", write="$name.write2BytesOF10(bb)", default="OFVlanVidMatch.NONE") \
+        .op(version=ANY, read="OFVlanVidMatch.read2Bytes(bb)", write="$name.write2Bytes(bb)", default="OFVlanVidMatch.NONE")
 vlan_pcp = JType("VlanPcp")\
         .op(read="VlanPcp.readByte(bb)",
             write="$name.writeByte(bb)",
@@ -523,8 +525,8 @@
         'of_oxm_sctp_dst_masked' : { 'value' : transport_port, 'value_mask' : transport_port },
         'of_oxm_eth_type' : { 'value' : eth_type },
         'of_oxm_eth_type_masked' : { 'value' : eth_type, 'value_mask' : eth_type },
-        'of_oxm_vlan_vid' : { 'value' : vlan_vid },
-        'of_oxm_vlan_vid_masked' : { 'value' : vlan_vid, 'value_mask' : vlan_vid },
+        'of_oxm_vlan_vid' : { 'value' : vlan_vid_match },
+        'of_oxm_vlan_vid_masked' : { 'value' : vlan_vid_match, 'value_mask' : vlan_vid_match },
         'of_oxm_vlan_pcp' : { 'value' : vlan_pcp },
         'of_oxm_vlan_pcp_masked' : { 'value' : vlan_pcp, 'value_mask' : vlan_pcp },
         'of_oxm_ip_dscp' : { 'value' : ip_dscp },
@@ -579,7 +581,7 @@
         'of_oxm_bsn_l3_dst_class_id_masked' : { 'value' : class_id, 'value_mask' : class_id },
 
         'of_table_stats_entry': { 'wildcards': table_stats_wildcards },
-        'of_match_v1': { 'vlan_vid' : vlan_vid, 'vlan_pcp': vlan_pcp,
+        'of_match_v1': { 'vlan_vid' : vlan_vid_match, 'vlan_pcp': vlan_pcp,
                 'eth_type': eth_type, 'ip_dscp': ip_dscp, 'ip_proto': ip_proto,
                 'tcp_src': transport_port, 'tcp_dst': transport_port,
                 'in_port': of_port_match_v1
@@ -588,6 +590,19 @@
         'of_bsn_set_l2_table_reply': { 'l2_table_enable': boolean },
         'of_bsn_set_pktin_suppression_request': { 'enabled': boolean },
         'of_flow_stats_request': { 'out_group': of_group_default_any },
+
+        'of_action_bsn_mirror': { 'dest_port': of_port },
+        'of_action_push_mpls': { 'ethertype': eth_type },
+        'of_action_push_pbb': { 'ethertype': eth_type },
+        'of_action_push_vlan': { 'ethertype': eth_type },
+        'of_action_set_nw_dst': { 'nw_addr': ipv4 },
+        'of_action_set_nw_ecn': { 'nw_ecn': ip_ecn },
+        'of_action_set_nw_src': { 'nw_addr': ipv4 },
+        'of_action_set_nw_dst': { 'tp_port': transport_port },
+        'of_action_set_tp_dst': { 'tp_port': transport_port },
+        'of_action_set_tp_src': { 'tp_port': transport_port },
+        'of_action_set_vlan_pcp': { 'vlan_pcp': vlan_pcp },
+        'of_action_set_vlan_vid': { 'vlan_vid': vlan_vid },
 }
 
 
@@ -605,6 +620,8 @@
 def make_match_field_jtype(sub_type_name="?"):
     return JType("MatchField<{}>".format(sub_type_name))
 
+def make_oxm_jtype(sub_type_name="?"):
+    return JType("OFOxm<{}>".format(sub_type_name))
 
 def list_cname_to_java_name(c_type):
     m = re.match(r'list\(of_([a-zA-Z_]+)_t\)', c_type)
@@ -613,7 +630,6 @@
     base_name = m.group(1)
     return "OF" + name_c_to_caps_camel(base_name)
 
-
 #### main entry point for conversion of LOXI types (c_types) Java types.
 # FIXME: This badly needs a refactoring
 
diff --git a/java_gen/pre-written/pom.xml b/java_gen/pre-written/pom.xml
index 5715704..e516b6e 100644
--- a/java_gen/pre-written/pom.xml
+++ b/java_gen/pre-written/pom.xml
@@ -10,7 +10,7 @@
 
     <groupId>org.projectfloodlight</groupId>
     <artifactId>openflowj</artifactId>
-    <version>0.3.0-SNAPSHOT</version>
+    <version>0.3.1-SNAPSHOT</version>
     <packaging>jar</packaging>
 
     <name>OpenFlowJ-Loxi</name>
@@ -55,6 +55,11 @@
             <artifactId>guava</artifactId>
             <version>15.0</version>
         </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.7.5</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/OFOxmList.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/OFOxmList.java
index c6ba116..7f66110 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/OFOxmList.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/OFOxmList.java
@@ -2,7 +2,6 @@
 
 import java.util.EnumMap;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
 import org.jboss.netty.buffer.ChannelBuffer;
@@ -13,11 +12,16 @@
 import org.projectfloodlight.openflow.types.OFValueType;
 import org.projectfloodlight.openflow.types.PrimitiveSinkable;
 import org.projectfloodlight.openflow.util.ChannelUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.hash.PrimitiveSink;
 
 public class OFOxmList implements Iterable<OFOxm<?>>, Writeable, PrimitiveSinkable {
+    private static final Logger logger = LoggerFactory.getLogger(OFOxmList.class);
+
     private final Map<MatchFields, OFOxm<?>> oxmMap;
 
     public final static OFOxmList EMPTY = new OFOxmList(ImmutableMap.<MatchFields, OFOxm<?>>of());
@@ -51,7 +55,7 @@
         }
 
         public OFOxmList build() {
-            return new OFOxmList(oxmMap);
+            return OFOxmList.ofList(oxmMap.values());
         }
     }
 
@@ -60,12 +64,19 @@
         return oxmMap.values().iterator();
     }
 
-    public static OFOxmList ofList(List<OFOxm<?>> oxmList) {
+    public static OFOxmList ofList(Iterable<OFOxm<?>> oxmList) {
         Map<MatchFields, OFOxm<?>> map = new EnumMap<MatchFields, OFOxm<?>>(
                 MatchFields.class);
         for (OFOxm<?> o : oxmList) {
-            // TODO: if fully masked, ignore oxm.
-            map.put(o.getMatchField().id, o);
+            OFOxm<?> canonical = o.getCanonical();
+
+            if(logger.isDebugEnabled() && !Objects.equal(o, canonical)) {
+                logger.debug("OFOxmList: normalized non-canonical OXM {} to {}", o, canonical);
+            }
+
+            if(canonical != null)
+                map.put(canonical.getMatchField().id, canonical);
+
         }
         return new OFOxmList(map);
     }
@@ -74,8 +85,14 @@
         Map<MatchFields, OFOxm<?>> map = new EnumMap<MatchFields, OFOxm<?>>(
                 MatchFields.class);
         for (OFOxm<?> o : oxms) {
-            // TODO: if fully masked, ignore oxm.
-            map.put(o.getMatchField().id, o);
+            OFOxm<?> canonical = o.getCanonical();
+
+            if(logger.isDebugEnabled() && !Objects.equal(o, canonical)) {
+                logger.debug("OFOxmList: normalized non-canonical OXM {} to {}", o, canonical);
+            }
+
+            if(canonical != null)
+                map.put(canonical.getMatchField().id, canonical);
         }
         return new OFOxmList(map);
     }
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/Match.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/Match.java
index 5e68de5..0efdcbb 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/Match.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/Match.java
@@ -26,7 +26,7 @@
  * them in part. For example, OF1.0 supports exact match and (full) wildcarding for all fields, but it
  * does only supports partial masking for IP source/destination fields, and this partial masking must be
  * in the CIDR prefix format. Thus, OF1.0 implementation may throw <code>UnsupportedOperationException</code> if given
- * in <code>setMaksed</code> an IP mask of, for example, 255.0.255.0, or if <code>setMasked</code> is called for any field
+ * in <code>setMasked</code> an IP mask of, for example, 255.0.255.0, or if <code>setMasked</code> is called for any field
  * which is not IP source/destination address.
  * <br><br>
  * On prerequisites:<br>
@@ -37,7 +37,7 @@
  * be ignored unless the Ethertype is specified as MPLS. Likewise, the IP header and
  * transport header fields will be ignored unless the Ethertype is specified as either
  * IPv4 or ARP. The tp_src and tp_dst fields will be ignored unless the network protocol
- * specified is as TCP, UDP or SCTP. Fields that are ignored don�t need to be wildcarded
+ * specified is as TCP, UDP or SCTP. Fields that are ignored don't need to be wildcarded
  * and should be set to 0."
  * <br><br>
  * This interface uses generics to assure type safety in users code. However, implementing classes may have to suppress
@@ -124,6 +124,15 @@
     public boolean isPartiallyMasked(MatchField<?> field) throws UnsupportedOperationException;
 
     /**
+     * Get an Iterable over the match fields that have been specified for the
+     * match. This includes the match fields that are exact or masked match
+     * (but not fully wildcarded).
+     *
+     * @return
+     */
+    public Iterable<MatchField<?>> getMatchFields();
+
+    /**
      * Returns a builder to build new instances of this type of match object.
      * @return Match builder
      */
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/MatchField.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/MatchField.java
index 1831626..92d4878 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/MatchField.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/MatchField.java
@@ -18,12 +18,12 @@
 import org.projectfloodlight.openflow.types.OFMetadata;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.OFValueType;
+import org.projectfloodlight.openflow.types.OFVlanVidMatch;
 import org.projectfloodlight.openflow.types.TransportPort;
 import org.projectfloodlight.openflow.types.U32;
 import org.projectfloodlight.openflow.types.U8;
-import org.projectfloodlight.openflow.types.VlanPcp;
-import org.projectfloodlight.openflow.types.VlanVid;
 import org.projectfloodlight.openflow.types.VRF;
+import org.projectfloodlight.openflow.types.VlanPcp;
 
 @SuppressWarnings("unchecked")
 public class MatchField<F extends OFValueType<F>> {
@@ -56,12 +56,12 @@
     public final static MatchField<EthType> ETH_TYPE =
             new MatchField<EthType>("eth_type", MatchFields.ETH_TYPE);
 
-    public final static MatchField<VlanVid> VLAN_VID =
-            new MatchField<VlanVid>("vlan_vid", MatchFields.VLAN_VID);
+    public final static MatchField<OFVlanVidMatch> VLAN_VID =
+            new MatchField<OFVlanVidMatch>("vlan_vid", MatchFields.VLAN_VID);
 
     public final static MatchField<VlanPcp> VLAN_PCP =
             new MatchField<VlanPcp>("vlan_pcp", MatchFields.VLAN_PCP,
-                    new Prerequisite<VlanVid>(MatchField.VLAN_VID));
+                    new Prerequisite<OFVlanVidMatch>(MatchField.VLAN_VID));
 
     public final static MatchField<IpDscp> IP_DSCP =
             new MatchField<IpDscp>("ip_dscp", MatchFields.IP_DSCP,
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/ClassId.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/ClassId.java
index b082f8b..7d7c38e 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/ClassId.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/ClassId.java
@@ -14,6 +14,10 @@
     private final static int NONE_VAL = 0;
     public final static ClassId NONE = new ClassId(NONE_VAL);
 
+    private final static int NO_MASK_VAL = 0xFFFFFFFF;
+    public final static ClassId NO_MASK = new ClassId(NO_MASK_VAL);
+    public final static ClassId FULL_MASK = NONE;
+
     private final int rawValue;
 
     private ClassId(final int rawValue) {
@@ -23,7 +27,8 @@
     public static ClassId of(final int raw) {
         if(raw == NONE_VAL)
             return NONE;
-
+        else if(raw == NO_MASK_VAL)
+            return NO_MASK;
         return new ClassId(raw);
     }
 
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4AddressWithMask.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4AddressWithMask.java
index 67bbce4..f30fcbb 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4AddressWithMask.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4AddressWithMask.java
@@ -46,7 +46,7 @@
     public static IPv4AddressWithMask of(final String string) {
         int slashPos;
         String ip = string;
-        int maskBits = 0;
+        int maskBits = 32;
         IPv4Address maskAddress = null;
 
         // Read mask suffix
@@ -77,9 +77,12 @@
         if (maskAddress != null) {
             // Full address mask
             return IPv4AddressWithMask.of(ipv4, maskAddress);
-        } else if (maskBits == 0) {
+        } else if (maskBits == 32) {
             // No mask
             return IPv4AddressWithMask.of(ipv4, IPv4Address.NO_MASK);
+        } else if (maskBits == 0) {
+            // No mask
+            return IPv4AddressWithMask.of(ipv4, IPv4Address.FULL_MASK);
         } else {
             // With mask
             int mask = (-1) << (32 - maskBits);
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6AddressWithMask.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6AddressWithMask.java
index 727daf6..6faf0b8 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6AddressWithMask.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6AddressWithMask.java
@@ -40,7 +40,7 @@
     public static IPv6AddressWithMask of(final String string) {
         int slashPos;
         String ip = string;
-        int maskBits = 0;
+        int maskBits = 128;
         IPv6Address maskAddress = null;
 
         // Read mask suffix
@@ -71,10 +71,13 @@
         if (maskAddress != null) {
             // Full address mask
             return IPv6AddressWithMask.of(ipv6, maskAddress);
-        } else if (maskBits == 0) {
+        } else if (maskBits == 128) {
             // No mask
             return IPv6AddressWithMask.of(ipv6, IPv6Address.NO_MASK);
-        } else {
+        } else if (maskBits == 0) {
+            // Entirely masked out
+            return IPv6AddressWithMask.of(ipv6, IPv6Address.FULL_MASK);
+        }else {
             // With mask
             BigInteger mask = BigInteger.ONE.negate().shiftLeft(128 - maskBits);
             byte[] maskBytesTemp = mask.toByteArray();
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/LagId.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/LagId.java
index 6d9421a..51364e1 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/LagId.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/LagId.java
@@ -15,11 +15,19 @@
     private final static int NONE_VAL = 0;
     public final static LagId NONE = new LagId(NONE_VAL);
 
+    private final static int NO_MASK_VAL = 0xFFFFFFFF;
+    public final static LagId NO_MASK = new LagId(NO_MASK_VAL);
+    public final static LagId FULL_MASK = NONE;
+
     private LagId(final int rawValue) {
         this.rawValue = rawValue;
     }
 
     public static LagId of(final int raw) {
+        if(raw == NONE_VAL)
+            return NONE;
+        else if (raw == NO_MASK_VAL)
+            return NO_MASK;
         return new LagId(raw);
     }
 
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBitMask128.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBitMask128.java
index 5cec233..051d573 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBitMask128.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBitMask128.java
@@ -14,6 +14,9 @@
     public static final OFBitMask128 ALL = new OFBitMask128(-1, -1);
     public static final OFBitMask128 NONE = new OFBitMask128(0, 0);
 
+    public static final OFBitMask128 NO_MASK = ALL;
+    public static final OFBitMask128 FULL_MASK = NONE;
+
     private OFBitMask128(long raw1, long raw2) {
         this.raw1 = raw1;
         this.raw2 = raw2;
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBooleanValue.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBooleanValue.java
index b386e54..e276092 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBooleanValue.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBooleanValue.java
@@ -28,6 +28,9 @@
     public final static OFBooleanValue TRUE = new OFBooleanValue(true);
     public final static OFBooleanValue FALSE = new OFBooleanValue(false);
 
+    public final static OFBooleanValue NO_MASK = TRUE;
+    public final static OFBooleanValue FULL_MASK = FALSE;
+
     private final boolean value;
 
     private OFBooleanValue(boolean value) {
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFMetadata.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFMetadata.java
index d1a23df..fcabdcd 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFMetadata.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFMetadata.java
@@ -27,6 +27,10 @@
         return new OFMetadata(U64.ofRaw(raw));
     }
 
+    public U64 getValue() {
+        return u64;
+    }
+
     public static OFMetadata read8Bytes(ChannelBuffer cb) {
         return OFMetadata.ofRaw(cb.readLong());
     }
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFPortBitMap.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFPortBitMap.java
index 49c8c4e..b9c2b75 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFPortBitMap.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFPortBitMap.java
@@ -1,5 +1,7 @@
 package org.projectfloodlight.openflow.types;
 
+import javax.annotation.concurrent.Immutable;
+
 
 /** User-facing object representing a bitmap of ports that can be matched on.
  *  This is implemented by the custom BSN OXM type of_oxm_bsn_in_ports_182.
@@ -20,8 +22,22 @@
  *  The second term cannot be represented in OXM, the second can.
  *
  *  That said, all that craziness is hidden from the user of this object.
+ *
+ *  <h2>Usage</h2>
+ *  OFPortBitmap is meant to be used with MatchField <tt>BSN_IN_PORTS_128</tt> in place
+ *  of the raw type Masked&lt;OFBitMask128&gt;.
+ *
+ *  <h3>Example:</h3>:
+ *  <pre>
+ *  OFPortBitMap portBitMap;
+ *  Match.Builder matchBuilder;
+ *  // initialize
+ *  matchBuilder.setMasked(MatchField.BSN_IN_PORTS_128, portBitmap);
+ *  </pre>
+ *
  * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
  */
+@Immutable
 public class OFPortBitMap extends Masked<OFBitMask128> {
 
     private OFPortBitMap(OFBitMask128 mask) {
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFVlanVidMatch.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFVlanVidMatch.java
new file mode 100644
index 0000000..29d6e02
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFVlanVidMatch.java
@@ -0,0 +1,183 @@
+package org.projectfloodlight.openflow.types;
+
+import javax.annotation.Nullable;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.projectfloodlight.openflow.exceptions.OFParseError;
+
+import com.google.common.hash.PrimitiveSink;
+import com.google.common.primitives.Shorts;
+
+/** Represents an OpenFlow Vlan VID for use in Matches, as specified by the OpenFlow 1.3 spec.
+ *
+ *  <b> Note: this is not just the 12-bit vlan tag. OpenFlow defines
+ *      the additional mask bits 0x1000 to represent the presence of a vlan
+ *      tag. This additional bit will be stripped when writing a OF1.0 value
+ *      tag.
+ *  </b>
+ *
+ *
+ * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
+ *
+ */
+public class OFVlanVidMatch implements OFValueType<OFVlanVidMatch> {
+
+    private static final short VALIDATION_MASK = 0x1FFF;
+    private static final short PRESENT_VAL = 0x1000;
+    private static final short VLAN_MASK = 0x0FFF;
+    private static final short NONE_VAL = 0x0000;
+    private static final short UNTAGGED_VAL_OF13 = (short) 0x0000;
+    private static final short UNTAGGED_VAL_OF10 = (short) 0xFFFF;
+    final static int LENGTH = 2;
+
+    /** presence of a VLAN tag is indicated by the presence of bit 0x1000 */
+    public static final OFVlanVidMatch PRESENT = new OFVlanVidMatch(PRESENT_VAL);
+
+    /** this value means 'not set' in OF1.0 (e.g., in a match). not used elsewhere */
+    public static final OFVlanVidMatch NONE = new OFVlanVidMatch(NONE_VAL);
+
+    /** for use with masking operations */
+    public static final OFVlanVidMatch NO_MASK = new OFVlanVidMatch((short)0xFFFF);
+    public static final OFVlanVidMatch FULL_MASK = NONE;
+
+    /** an untagged packet is specified as 0000 in OF 1.0, but 0xFFFF in OF1.0. Special case that. */
+    public static final OFVlanVidMatch UNTAGGED = new OFVlanVidMatch(NONE_VAL) {
+        @Override
+        public void write2BytesOF10(ChannelBuffer c) {
+            c.writeShort(UNTAGGED_VAL_OF10);
+        }
+    };
+
+    private final short vid;
+
+    private OFVlanVidMatch(short vid) {
+        this.vid = vid;
+    }
+
+    public static OFVlanVidMatch ofRawVid(short vid) {
+        if(vid == UNTAGGED_VAL_OF13)
+            return UNTAGGED;
+        else if(vid == PRESENT_VAL)
+            return PRESENT;
+        else if ((vid & VALIDATION_MASK) != vid)
+            throw new IllegalArgumentException(String.format("Illegal VLAN value: %x", vid));
+        return new OFVlanVidMatch(vid);
+    }
+
+    public static OFVlanVidMatch ofVlanVid(VlanVid vid) {
+        return ofVlan(vid.getVlan());
+    }
+
+
+    public static OFVlanVidMatch ofVlan(int vlan) {
+        if( (vlan & VLAN_MASK) != vlan)
+            throw new IllegalArgumentException(String.format("Illegal VLAN value: %x", vlan));
+        return ofRawVid( (short) (vlan | PRESENT_VAL));
+    }
+
+    public static OFVlanVidMatch ofVlanOF10(short of10vlan) {
+        if(of10vlan == NONE_VAL) {
+            return NONE;
+        } else if(of10vlan == UNTAGGED_VAL_OF10) {
+            return UNTAGGED;
+        } else {
+            return ofVlan(of10vlan);
+        }
+    }
+
+    /** @return whether or not this VlanId has the present (0x1000) bit set */
+    public boolean isPresentBitSet() {
+       return (vid & PRESENT_VAL) != 0;
+    }
+
+    /** @return the actual VLAN tag this vid identifies */
+    public short getVlan() {
+        return (short) (vid & VLAN_MASK);
+    }
+
+    /** @return the actual vlan tag this vid identifies as a VlanVid object, if this
+     *  VlanVidMatch has the present bit set (i.e., identifies a tagged VLAN).
+     *  Else, returns null.
+     */
+    @Nullable
+    public VlanVid getVlanVid() {
+        return isPresentBitSet() ? VlanVid.ofVlan((short) (vid & VLAN_MASK)) : null;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof OFVlanVidMatch))
+            return false;
+        OFVlanVidMatch other = (OFVlanVidMatch)obj;
+        if (other.vid != this.vid)
+            return false;
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int prime = 13873;
+        return this.vid * prime;
+    }
+
+    @Override
+    public String toString() {
+        return "0x" + Integer.toHexString(vid);
+    }
+
+    public short getRawVid() {
+        return vid;
+    }
+
+
+    @Override
+    public int getLength() {
+        return LENGTH;
+    }
+
+
+    volatile byte[] bytesCache = null;
+
+    public byte[] getBytes() {
+        if (bytesCache == null) {
+            synchronized (this) {
+                if (bytesCache == null) {
+                    bytesCache =
+                            new byte[] { (byte) ((vid >>> 8) & 0xFF),
+                                         (byte) ((vid >>> 0) & 0xFF) };
+                }
+            }
+        }
+        return bytesCache;
+    }
+
+    public void write2Bytes(ChannelBuffer c) {
+        c.writeShort(this.vid);
+    }
+
+    public void write2BytesOF10(ChannelBuffer c) {
+        c.writeShort(this.getVlan());
+    }
+
+    public static OFVlanVidMatch read2Bytes(ChannelBuffer c) throws OFParseError {
+        return OFVlanVidMatch.ofRawVid(c.readShort());
+    }
+
+    public static OFVlanVidMatch read2BytesOF10(ChannelBuffer c) throws OFParseError {
+        return OFVlanVidMatch.ofVlanOF10(c.readShort());
+    }
+
+    @Override
+    public OFVlanVidMatch applyMask(OFVlanVidMatch mask) {
+        return OFVlanVidMatch.ofRawVid((short)(this.vid & mask.vid));
+    }
+
+    @Override
+    public int compareTo(OFVlanVidMatch o) {
+        return Shorts.compare(vid, o.vid);
+    }
+    @Override
+    public void putTo(PrimitiveSink sink) {
+        sink.putShort(vid);
+    }
+}
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFVlanVidMatchWithMask.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFVlanVidMatchWithMask.java
new file mode 100644
index 0000000..c91c28c
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFVlanVidMatchWithMask.java
@@ -0,0 +1,10 @@
+package org.projectfloodlight.openflow.types;
+
+public class OFVlanVidMatchWithMask extends Masked<OFVlanVidMatch> {
+    private OFVlanVidMatchWithMask(OFVlanVidMatch value, OFVlanVidMatch mask) {
+        super(value, mask);
+    }
+
+    /* a combination of Vlan Vid and mask that matches any tagged packet */
+    public final static OFVlanVidMatchWithMask ANY_TAGGED = new OFVlanVidMatchWithMask(OFVlanVidMatch.PRESENT, OFVlanVidMatch.PRESENT);
+}
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U32.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U32.java
index 9fafc30..7f53374 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U32.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U32.java
@@ -29,6 +29,10 @@
     private final static int ZERO_VAL = 0;
     public final static U32 ZERO = new U32(ZERO_VAL);
 
+    private static final int NO_MASK_VAL = 0xFFffFFff;
+    public final static U32 NO_MASK = new U32(NO_MASK_VAL);
+    public static final U32 FULL_MASK = ZERO;
+
     private final int raw;
 
     private U32(int raw) {
@@ -42,6 +46,8 @@
     public static U32 ofRaw(int raw) {
         if(raw == ZERO_VAL)
             return ZERO;
+        if(raw == NO_MASK_VAL)
+            return NO_MASK;
         return new U32(raw);
     }
 
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U8.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U8.java
index 078a846..c644599 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U8.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U8.java
@@ -29,6 +29,10 @@
     private final static byte ZERO_VAL = 0;
     public final static U8 ZERO = new U8(ZERO_VAL);
 
+    private static final byte NO_MASK_VAL = (byte) 0xFF;
+    public static final U8 NO_MASK = new U8(NO_MASK_VAL);
+    public static final U8 FULL_MASK = ZERO;
+
     private final byte raw;
 
     private U8(byte raw) {
@@ -38,6 +42,8 @@
     public static final U8 of(short value) {
         if(value == ZERO_VAL)
             return ZERO;
+        if(value == NO_MASK_VAL)
+            return NO_MASK;
 
         return new U8(t(value));
     }
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/VlanVid.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/VlanVid.java
index d370711..8337eb6 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/VlanVid.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/VlanVid.java
@@ -6,45 +6,23 @@
 import com.google.common.hash.PrimitiveSink;
 import com.google.common.primitives.Shorts;
 
-/** Represents an OpenFlow Vlan VID, as specified by the OpenFlow 1.3 spec.
- *
- *  <b> Note: this is not just the 12-bit vlan tag. OpenFlow defines
- *      the additional mask bits 0x1000 to represent the presence of a vlan
- *      tag. This additional bit will be stripped when writing a OF1.0 value
- *      tag.
- *  </b>
- *
+/** Represents an 802.1Q Vlan VID (12 bits).
  *
  * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
  *
  */
 public class VlanVid implements OFValueType<VlanVid> {
 
-    private static final short VALIDATION_MASK = 0x1FFF;
-    private static final short PRESENT_VAL = 0x1000;
-    private static final short VLAN_MASK = 0x0FFF;
-    private static final short NONE_VAL = 0x0000;
-    private static final short UNTAGGED_VAL_OF13 = (short) 0x0000;
-    private static final short UNTAGGED_VAL_OF10 = (short) 0xFFFF;
+    private static final short VALIDATION_MASK = 0x0FFF;
+    private static final short ZERO_VAL = 0x0000;
     final static int LENGTH = 2;
 
-    /** presence of a VLAN tag is idicated by the presence of bit 0x1000 */
-    public static final VlanVid PRESENT = new VlanVid(PRESENT_VAL);
-
     /** this value means 'not set' in OF1.0 (e.g., in a match). not used elsewhere */
-    public static final VlanVid NONE = new VlanVid(NONE_VAL);
+    public static final VlanVid ZERO = new VlanVid(ZERO_VAL);
 
     /** for use with masking operations */
     public static final VlanVid NO_MASK = new VlanVid((short)0xFFFF);
-    public static final VlanVid FULL_MASK = NONE;
-
-    /** an untagged packet is specified as 0000 in OF 1.0, but 0xFFFF in OF1.0. Special case that. */
-    public static final VlanVid UNTAGGED = new VlanVid(NONE_VAL) {
-        @Override
-        public void write2BytesOF10(ChannelBuffer c) {
-            c.writeShort(UNTAGGED_VAL_OF10);
-        }
-    };
+    public static final VlanVid FULL_MASK = ZERO;
 
     private final short vid;
 
@@ -52,40 +30,15 @@
         this.vid = vid;
     }
 
-    public static VlanVid ofRawVid(short vid) {
-        if(vid == UNTAGGED_VAL_OF13)
-            return UNTAGGED;
-        else if(vid == PRESENT_VAL)
-            return PRESENT;
-        else if ((vid & VALIDATION_MASK) != vid)
+    public static VlanVid ofVlan(int vid) {
+        if ((vid & VALIDATION_MASK) != vid)
             throw new IllegalArgumentException(String.format("Illegal VLAN value: %x", vid));
-        return new VlanVid(vid);
-    }
-
-    public static VlanVid ofVlan(int vlan) {
-        if( (vlan & VLAN_MASK) != vlan)
-            throw new IllegalArgumentException(String.format("Illegal VLAN value: %x", vlan));
-        return ofRawVid( (short) (vlan | PRESENT_VAL));
-    }
-
-    public static VlanVid ofVlanOF10(short of10vlan) {
-        if(of10vlan == NONE_VAL) {
-            return NONE;
-        } else if(of10vlan == UNTAGGED_VAL_OF10) {
-            return UNTAGGED;
-        } else {
-            return ofVlan(of10vlan);
-        }
-    }
-
-    /** @return whether or not this VlanId has the present (0x1000) bit set */
-    public boolean isPresentBitSet() {
-       return (vid & PRESENT_VAL) != 0;
+        return new VlanVid((short) vid);
     }
 
     /** @return the actual VLAN tag this vid identifies */
     public short getVlan() {
-        return (short) (vid & VLAN_MASK);
+        return vid;
     }
 
     @Override
@@ -109,17 +62,11 @@
         return "0x" + Integer.toHexString(vid);
     }
 
-    public short getRawVid() {
-        return vid;
-    }
-
-
     @Override
     public int getLength() {
         return LENGTH;
     }
 
-
     volatile byte[] bytesCache = null;
 
     public byte[] getBytes() {
@@ -144,22 +91,19 @@
     }
 
     public static VlanVid read2Bytes(ChannelBuffer c) throws OFParseError {
-        return VlanVid.ofRawVid(c.readShort());
-    }
-
-    public static VlanVid read2BytesOF10(ChannelBuffer c) throws OFParseError {
-        return VlanVid.ofVlanOF10(c.readShort());
+        return VlanVid.ofVlan(c.readShort());
     }
 
     @Override
     public VlanVid applyMask(VlanVid mask) {
-        return VlanVid.ofRawVid((short)(this.vid & mask.vid));
+        return VlanVid.ofVlan((short)(this.vid & mask.vid));
     }
 
     @Override
     public int compareTo(VlanVid o) {
         return Shorts.compare(vid, o.vid);
     }
+
     @Override
     public void putTo(PrimitiveSink sink) {
         sink.putShort(vid);
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/VlanVidWithMask.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/VlanVidWithMask.java
deleted file mode 100644
index cb81d31..0000000
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/VlanVidWithMask.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.projectfloodlight.openflow.types;
-
-public class VlanVidWithMask extends Masked<VlanVid> {
-    private VlanVidWithMask(VlanVid value, VlanVid mask) {
-        super(value, mask);
-    }
-
-    /* a combination of Vlan Vid and mask that matches any tagged packet */
-    public final static VlanVidWithMask ANY_TAGGED = new VlanVidWithMask(VlanVid.PRESENT, VlanVid.PRESENT);
-
-}
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/ChannelUtils.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/ChannelUtils.java
index 13cfdc7..1a1ac6a 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/ChannelUtils.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/ChannelUtils.java
@@ -6,6 +6,8 @@
 import org.projectfloodlight.openflow.exceptions.OFParseError;
 import org.projectfloodlight.openflow.protocol.OFMessageReader;
 import org.projectfloodlight.openflow.protocol.Writeable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Charsets;
 import com.google.common.collect.ImmutableList;
@@ -18,6 +20,7 @@
  */
 
 public class ChannelUtils {
+    private static final Logger logger = LoggerFactory.getLogger(ChannelUtils.class);
     public static String readFixedLengthString(ChannelBuffer bb, int length) {
         byte[] dst = new byte[length];
         bb.readBytes(dst, 0, length);
@@ -56,8 +59,13 @@
     public static <T> List<T> readList(ChannelBuffer bb, int length, OFMessageReader<T> reader) throws OFParseError {
         int end = bb.readerIndex() + length;
         Builder<T> builder = ImmutableList.<T>builder();
+        if(logger.isTraceEnabled())
+            logger.trace("readList(length={}, reader={})", length, reader.getClass());
         while(bb.readerIndex() < end) {
-            builder.add(reader.readFrom(bb));
+            T read = reader.readFrom(bb);
+            if(logger.isTraceEnabled())
+                logger.trace("readList: read={}, left={}", read, end - bb.readerIndex());
+            builder.add(read);
         }
         if(bb.readerIndex() != end) {
             throw new IllegalStateException("Overread length: length="+length + " overread by "+ (bb.readerIndex() - end) + " reader: "+reader);
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv4AddressTest.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv4AddressTest.java
index 4f3f968..38d60b3 100644
--- a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv4AddressTest.java
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv4AddressTest.java
@@ -2,8 +2,10 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
+import org.hamcrest.CoreMatchers;
 import org.jboss.netty.buffer.ChannelBuffers;
 import org.junit.Test;
 import org.projectfloodlight.openflow.exceptions.OFParseError;
@@ -47,20 +49,23 @@
                             "192.168.130.140/255.255.192.0",
                             "127.0.0.1/8",
                             "8.8.8.8",
+                            "0.0.0.0/0"
     };
 
     boolean[] hasMask = {
                          true,
                          true,
                          true,
-                         false
+                         false,
+                         true
     };
 
     byte[][][] ipsWithMaskValues = {
                              new byte[][] { new byte[] { (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04 }, new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00 } },
                              new byte[][] { new byte[] { (byte)0xC0, (byte)0xA8, (byte)0x82, (byte)0x8C }, new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xC0, (byte)0x00 } },
                              new byte[][] { new byte[] { (byte)0x7F, (byte)0x00, (byte)0x00, (byte)0x01 }, new byte[] { (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00 } },
-                             new byte[][] { new byte[] { (byte)0x08, (byte)0x08, (byte)0x08, (byte)0x08 }, null }
+                             new byte[][] { new byte[] { (byte)0x08, (byte)0x08, (byte)0x08, (byte)0x08 }, null },
+                             new byte[][] { new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 }, new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 } }
     };
 
 
@@ -123,7 +128,8 @@
                 }
 
                 assertArrayEquals(ipBytes, value.getValue().getBytes());
-                assertArrayEquals(ipsWithMaskValues[i][1], value.getMask().getBytes());
+                assertThat(String.format("Byte comparison for mask of %s (%s)", ipsWithMask[i], value),
+                        value.getMask().getBytes(), CoreMatchers.equalTo(ipsWithMaskValues[i][1]));
             }
         }
     }
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv6AddressTest.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv6AddressTest.java
index 3fcfe23..6eb5743 100644
--- a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv6AddressTest.java
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv6AddressTest.java
@@ -2,16 +2,20 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 
+import org.hamcrest.CoreMatchers;
 import org.jboss.netty.buffer.ChannelBuffers;
 import org.junit.Test;
 import org.projectfloodlight.openflow.exceptions.OFParseError;
 
+import com.google.common.io.BaseEncoding;
+
 public class IPv6AddressTest {
 
     String[] testStrings = {
@@ -21,61 +25,63 @@
             "1:2:3:4:5:6:7:8"
     };
 
-    String[] ipsWithMask = {
-                            "1::1/80",
-                            "1:2:3:4::/ffff:ffff:ffff:ff00::",
-                            "ffff:ffee:1::/ff00:ff00:ff00:ff00::",
-                            "8:8:8:8:8:8:8:8",
-    };
 
-    byte[][] masks = {
-                    new byte[] { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff,
-                                 (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff,
-                                 (byte)0xff, (byte)0xff, (byte)0x00, (byte)0x00,
-                                 (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 },
-                    new byte[] { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff,
-                                 (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x00,
-                                 (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
-                                 (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 },
-                    new byte[] { (byte)0xff, (byte)0x00, (byte)0xff, (byte)0x00,
-                                 (byte)0xff, (byte)0x00, (byte)0xff, (byte)0x00,
-                                 (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
-                                 (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 },
-                    new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
-                                 (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
-                                 (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
-                                 (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 }
-    };
+    private final BaseEncoding hex = BaseEncoding.base16().omitPadding().lowerCase();
 
-    boolean[] hasMask = {
-                         true,
-                         true,
-                         true,
-                         false
+    private class WithMaskTaskCase {
+        final String input;
+        boolean hasMask;
+        byte[] expectedMask = hex.decode("ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff".replaceAll(" ", ""));
+
+        public WithMaskTaskCase(String input) {
+            super();
+            this.input = input;
+        }
+
+        public WithMaskTaskCase maskHex(String string) {
+            string = string.replaceAll(" ", "");
+            this.hasMask = true;
+            expectedMask = hex.decode(string);
+            return this;
+        }
+
+    }
+
+    WithMaskTaskCase[] withMasks = new WithMaskTaskCase[] {
+            new WithMaskTaskCase("1::1/80")
+                .maskHex("ff ff ff ff ff ff ff ff ff ff 00 00 00 00 00 00"),
+
+            new WithMaskTaskCase("ffff:ffee:1::/ff00:ff00:ff00:ff00::")
+                .maskHex("ff 00 ff 00 ff 00 ff 00 00 00 00 00 00 00 00 00"),
+            new WithMaskTaskCase("8:8:8:8:8:8:8:8"),
+            new WithMaskTaskCase("8:8:8:8:8:8:8:8"),
+            new WithMaskTaskCase("1:2:3:4:5:6:7:8/128"),
+            new WithMaskTaskCase("::/0")
+                .maskHex("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00")
     };
 
     @Test
     public void testMasked() throws UnknownHostException {
-        for(int i=0; i < ipsWithMask.length; i++ ) {
-            IPv6AddressWithMask value = IPv6AddressWithMask.of(ipsWithMask[i]);
-            if (!hasMask[i]) {
+        for(WithMaskTaskCase w: withMasks) {
+            IPv6AddressWithMask value = IPv6AddressWithMask.of(w.input);
+            if (!w.hasMask) {
                 IPv6Address ip = value.getValue();
-                InetAddress inetAddress = InetAddress.getByName(ipsWithMask[i]);
+                InetAddress inetAddress = InetAddress.getByName(w.input.split("/")[0]);
 
                 assertArrayEquals(ip.getBytes(), inetAddress.getAddress());
-                assertEquals(ipsWithMask[i], ip.toString());
-            } else if (value instanceof IPv6AddressWithMask && hasMask[i]) {
-                InetAddress inetAddress = InetAddress.getByName(ipsWithMask[i].substring(0, ipsWithMask[i].indexOf('/')));
+                assertEquals(w.input.split("/")[0], ip.toString());
+            } else {
+                InetAddress inetAddress = InetAddress.getByName(w.input.substring(0, w.input.indexOf('/')));
 
                 byte[] address = inetAddress.getAddress();
                 assertEquals(address.length, value.getValue().getBytes().length);
 
                 for (int j = 0; j < address.length; j++) {
-                    address[j] &= masks[i][j];
+                    address[j] &= w.expectedMask[j];
                 }
 
-                assertArrayEquals(value.getValue().getBytes(), address);
-                assertArrayEquals(masks[i], value.getMask().getBytes());
+                assertThat("Address bytes for input " + w.input + ", value=" + value, value.getValue().getBytes(), CoreMatchers.equalTo(address));
+                assertThat("mask check for input " + w.input + ", value=" + value, value.getMask().getBytes(), CoreMatchers.equalTo(w.expectedMask));
             }
         }
     }
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/OFOxmListTest.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/OFOxmListTest.java
new file mode 100644
index 0000000..39e8c0c
--- /dev/null
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/OFOxmListTest.java
@@ -0,0 +1,41 @@
+package org.projectfloodlight.protocol;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFOxmList;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6DstMasked;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6SrcMasked;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxms;
+import org.projectfloodlight.openflow.types.IPv6AddressWithMask;
+
+public class OFOxmListTest {
+    private OFOxms oxms;
+
+    @Before
+    public void setup() {
+        oxms = OFFactories.getFactory(OFVersion.OF_13).oxms();
+    }
+
+    @Test
+    public void testCanonicalize() {
+        OFOxmList.Builder builder = new OFOxmList.Builder();
+        IPv6AddressWithMask fullMasked = IPv6AddressWithMask.of("::/0");
+        OFOxmIpv6DstMasked  fullMaskedOxm = oxms.ipv6DstMasked(fullMasked.getValue(), fullMasked.getMask());
+        builder.set(fullMaskedOxm);
+
+        IPv6AddressWithMask address= IPv6AddressWithMask.of("1:2:3:4:5:6::8");
+        OFOxmIpv6SrcMasked  addressSrcOxm = oxms.ipv6SrcMasked(address.getValue(), address.getMask());
+        builder.set(addressSrcOxm);
+
+        OFOxmList list = builder.build();
+        assertThat(list.get(MatchField.IPV6_DST), CoreMatchers.nullValue());
+        assertFalse(list.get(MatchField.IPV6_SRC).isMasked());
+    }
+}
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/OFOxmTest.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/OFOxmTest.java
new file mode 100644
index 0000000..8482886
--- /dev/null
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/OFOxmTest.java
@@ -0,0 +1,62 @@
+package org.projectfloodlight.protocol;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4Src;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4SrcMasked;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxms;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv4AddressWithMask;
+
+public class OFOxmTest {
+    private OFOxms oxms;
+
+    @Before
+    public void setup() {
+        oxms = OFFactories.getFactory(OFVersion.OF_13).oxms();
+    }
+
+    @Test
+    public void testGetCanonicalFullMask() {
+        IPv4AddressWithMask empty = IPv4AddressWithMask.of("0.0.0.0/0");
+        assertEquals(IPv4Address.FULL_MASK, empty.getMask());
+        OFOxmIpv4SrcMasked ipv4SrcMasked = oxms.ipv4SrcMasked(empty.getValue(), empty.getMask());
+        // canonicalize should remove /0
+        assertNull(ipv4SrcMasked.getCanonical());
+    }
+
+    @Test
+    public void testGetCanonicalNoMask() {
+        IPv4AddressWithMask fullIp = IPv4AddressWithMask.of("1.2.3.4/32");
+        assertEquals(IPv4Address.NO_MASK, fullIp.getMask());
+        OFOxmIpv4SrcMasked ipv4SrcMasked = oxms.ipv4SrcMasked(fullIp.getValue(), fullIp.getMask());
+        assertTrue(ipv4SrcMasked.isMasked());
+        assertEquals(IPv4Address.NO_MASK, ipv4SrcMasked.getMask());
+
+        // canonicalize should convert the masked oxm to the non-masked one
+        OFOxm<IPv4Address> canonical = ipv4SrcMasked.getCanonical();
+        assertThat(canonical, CoreMatchers.instanceOf(OFOxmIpv4Src.class));
+        assertFalse(canonical.isMasked());
+    }
+
+    @Test
+    public void testGetCanonicalNormalMask() {
+        IPv4AddressWithMask ip = IPv4AddressWithMask.of("1.2.3.0/24");
+        OFOxmIpv4SrcMasked ipv4SrcMasked = oxms.ipv4SrcMasked(ip.getValue(), ip.getMask());
+        assertTrue(ipv4SrcMasked.isMasked());
+
+        // canonicalize should convert the masked oxm to the non-masked one
+        OFOxm<IPv4Address> canonical = ipv4SrcMasked.getCanonical();
+        assertEquals(ipv4SrcMasked, canonical);
+    }
+}
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration10Test.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration10Test.java
new file mode 100644
index 0000000..c6f4471
--- /dev/null
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration10Test.java
@@ -0,0 +1,10 @@
+package org.projectfloodlight.protocol.match;
+
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+
+public class MatchFieldIteration10Test extends MatchFieldIterationBase {
+    public MatchFieldIteration10Test() {
+        super(OFFactories.getFactory(OFVersion.OF_10));
+    }
+}
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration13Test.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration13Test.java
new file mode 100644
index 0000000..b654a53
--- /dev/null
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration13Test.java
@@ -0,0 +1,10 @@
+package org.projectfloodlight.protocol.match;
+
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+
+public class MatchFieldIteration13Test extends MatchFieldIterationBase {
+    public MatchFieldIteration13Test() {
+        super(OFFactories.getFactory(OFVersion.OF_13));
+    }
+}
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIterationBase.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIterationBase.java
new file mode 100644
index 0000000..9c72e37
--- /dev/null
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIterationBase.java
@@ -0,0 +1,249 @@
+package org.projectfloodlight.protocol.match;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.util.Iterator;
+
+import org.junit.Test;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
+import org.projectfloodlight.openflow.protocol.match.MatchFields;
+import org.projectfloodlight.openflow.types.ArpOpcode;
+import org.projectfloodlight.openflow.types.EthType;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IpProtocol;
+import org.projectfloodlight.openflow.types.MacAddress;
+import org.projectfloodlight.openflow.types.Masked;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.TransportPort;
+
+import com.google.common.collect.Iterables;
+
+public class MatchFieldIterationBase {
+
+    private OFFactory factory;
+
+    protected MatchFieldIterationBase(OFFactory factory) {
+        this.factory = factory;
+    }
+    
+    @Test
+    public void iterateEmptyMatch() {
+        Match match = factory.buildMatch().build();
+        Iterator<MatchField<?>> iter = match.getMatchFields().iterator();
+        assertThat(iter.hasNext(), is(false));
+    }
+    
+    @Test
+    public void iterateSingleExactMatchField() {
+        OFPort port5 = OFPort.of(5);
+        Match match = factory.buildMatch()
+                .setExact(MatchField.IN_PORT, port5)
+                .build();
+        Iterator<MatchField<?>> iter = match.getMatchFields().iterator();
+        assertThat(iter.hasNext(), is(true));
+        MatchField<?> matchField = iter.next();
+        assertThat(matchField.id, is(MatchFields.IN_PORT));
+        assertThat(match.isExact(matchField), is(true));
+        @SuppressWarnings("unchecked")
+        MatchField<OFPort> portMatchField = (MatchField<OFPort>) matchField;
+        OFPort port = match.get(portMatchField);
+        assertThat(port, is(port5));
+        assertThat(iter.hasNext(), is(false));
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void iterateExactMatchFields() {
+        OFPort port5 = OFPort.of(5);
+        MacAddress macSrc = MacAddress.of("00:01:02:03:04:05");
+        MacAddress macDst = MacAddress.of("01:01:02:02:03:03");
+        IPv4Address ipSrc = IPv4Address.of("10.192.20.1");
+        IPv4Address ipDst = IPv4Address.of("10.192.20.2");
+        TransportPort tcpSrc = TransportPort.of(100);
+        TransportPort tcpDst = TransportPort.of(200);
+        Match match = factory.buildMatch()
+                .setExact(MatchField.IN_PORT, port5)
+                .setExact(MatchField.ETH_TYPE, EthType.IPv4)
+                .setExact(MatchField.ETH_SRC, macSrc)
+                .setExact(MatchField.ETH_DST, macDst)
+                .setExact(MatchField.IP_PROTO, IpProtocol.TCP)
+                .setExact(MatchField.IPV4_SRC, ipSrc)
+                .setExact(MatchField.IPV4_DST, ipDst)
+                .setExact(MatchField.TCP_SRC, tcpSrc)
+                .setExact(MatchField.TCP_DST, tcpDst)
+                .build();
+        assertThat(Iterables.size(match.getMatchFields()), is(9));
+        for (MatchField<?> matchField: match.getMatchFields()) {
+            switch (matchField.id) {
+            case IN_PORT:
+                OFPort port = match.get((MatchField<OFPort>) matchField);
+                assertThat(port, is(port5));
+                break;
+            case ETH_TYPE:
+                EthType ethType = match.get((MatchField<EthType>) matchField);
+                assertThat(ethType, is(EthType.IPv4));
+                break;
+            case ETH_SRC:
+                MacAddress mac = match.get((MatchField<MacAddress>) matchField);
+                assertThat(mac, is(macSrc));
+                break;
+            case ETH_DST:
+                mac = match.get((MatchField<MacAddress>) matchField);
+                assertThat(mac, is(macDst));
+                break;
+            case IP_PROTO:
+                IpProtocol ipProtocol = match.get((MatchField<IpProtocol>) matchField);
+                assertThat(ipProtocol, is(IpProtocol.TCP));
+                break;
+            case IPV4_SRC:
+                IPv4Address ip = match.get((MatchField<IPv4Address>) matchField);
+                assertThat(ip, is(ipSrc));
+                break;
+            case IPV4_DST:
+                ip = match.get((MatchField<IPv4Address>) matchField);
+                assertThat(ip, is(ipDst));
+                break;
+            case TCP_SRC:
+                TransportPort tcp = match.get((MatchField<TransportPort>) matchField);
+                assertThat(tcp, is(tcpSrc));
+                break;
+            case TCP_DST:
+                tcp = match.get((MatchField<TransportPort>) matchField);
+                assertThat(tcp, is(tcpDst));
+                break;
+            default:
+                fail("Unexpected match field returned from iterator");
+            }
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void iterateArpFields() {
+        MacAddress macSrc = MacAddress.of("00:01:02:03:04:05");
+        MacAddress macDst = MacAddress.of("01:01:02:02:03:03");
+        IPv4Address ipSrc = IPv4Address.of("10.192.20.1");
+        IPv4Address ipDst = IPv4Address.of("10.192.20.2");
+        OFVersion version = factory.getVersion();
+        boolean supportsArpHardwareAddress = (version != OFVersion.OF_10) &&
+                (version != OFVersion.OF_11) && (version != OFVersion.OF_12);
+        int matchFieldCount = 4;
+        Match.Builder builder = factory.buildMatch();
+        builder.setExact(MatchField.ETH_TYPE, EthType.ARP)
+                .setExact(MatchField.ARP_OP, ArpOpcode.REPLY)
+                .setExact(MatchField.ARP_SPA, ipSrc)
+                .setExact(MatchField.ARP_TPA, ipDst);
+        if (supportsArpHardwareAddress) {
+            builder.setExact(MatchField.ARP_SHA, macSrc);
+            builder.setExact(MatchField.ARP_THA, macDst);
+            matchFieldCount += 2;
+        }
+        Match match = builder.build();
+        assertThat(Iterables.size(match.getMatchFields()), is(matchFieldCount));
+        for (MatchField<?> matchField: match.getMatchFields()) {
+            switch (matchField.id) {
+            case ETH_TYPE:
+                EthType ethType = match.get((MatchField<EthType>) matchField);
+                assertThat(ethType, is(EthType.ARP));
+                break;
+            case ARP_OP:
+                ArpOpcode opcode = match.get((MatchField<ArpOpcode>) matchField);
+                assertThat(opcode, is(ArpOpcode.REPLY));
+                break;
+            case ARP_SHA:
+                MacAddress mac = match.get((MatchField<MacAddress>) matchField);
+                assertThat(mac, is(macSrc));
+                break;
+            case ARP_THA:
+                mac = match.get((MatchField<MacAddress>) matchField);
+                assertThat(mac, is(macDst));
+                break;
+            case ARP_SPA:
+                IPv4Address ip = match.get((MatchField<IPv4Address>) matchField);
+                assertThat(ip, is(ipSrc));
+                break;
+            case ARP_TPA:
+                ip = match.get((MatchField<IPv4Address>) matchField);
+                assertThat(ip, is(ipDst));
+                break;
+            default:
+                fail("Unexpected match field returned from iterator");
+            }
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void iterateMaskedFields() {
+        MacAddress macSrc = MacAddress.of("01:02:03:04:00:00");
+        MacAddress macSrcMask = MacAddress.of("FF:FF:FF:FF:00:00");
+        MacAddress macDst = MacAddress.of("11:22:33:00:00:00");
+        MacAddress macDstMask = MacAddress.of("FF:FF:FF:00:00:00");
+        IPv4Address ipSrc = IPv4Address.of("10.192.20.0");
+        IPv4Address ipSrcMask = IPv4Address.of("255.255.255.0");
+        IPv4Address ipDst = IPv4Address.of("10.192.20.0");
+        IPv4Address ipDstMask = IPv4Address.of("255.255.255.128");
+        TransportPort tcpSrcMask = TransportPort.of(0x01F0);
+        OFVersion version = factory.getVersion();
+        boolean supportsAllMasks = (version != OFVersion.OF_10) &&
+                (version != OFVersion.OF_11) && (version != OFVersion.OF_12);
+        int matchFieldCount = 4;
+        Match.Builder builder = factory.buildMatch()
+                .setExact(MatchField.ETH_TYPE, EthType.IPv4)
+                .setMasked(MatchField.IPV4_SRC, ipSrc, ipSrcMask)
+                .setMasked(MatchField.IPV4_DST, ipDst, ipDstMask)
+                .setExact(MatchField.IP_PROTO, IpProtocol.TCP);
+        if (supportsAllMasks) {
+            builder.setMasked(MatchField.ETH_SRC, macSrc, macSrcMask);
+            builder.setMasked(MatchField.ETH_DST, macDst, macDstMask);
+            builder.setMasked(MatchField.TCP_SRC, tcpSrcMask, tcpSrcMask);
+            matchFieldCount += 3;
+        }
+        Match match = builder.build();
+        assertThat(Iterables.size(match.getMatchFields()), is(matchFieldCount));
+        for (MatchField<?> matchField: match.getMatchFields()) {
+            switch (matchField.id) {
+            case ETH_TYPE:
+                EthType ethType = match.get((MatchField<EthType>) matchField);
+                assertThat(ethType, is(EthType.IPv4));
+                break;
+            case ETH_SRC:
+                Masked<MacAddress> mac = match.getMasked((MatchField<MacAddress>) matchField);
+                assertThat(mac.getValue(), is(macSrc));
+                assertThat(mac.getMask(), is(macSrcMask));
+                break;
+            case ETH_DST:
+                mac = match.getMasked((MatchField<MacAddress>) matchField);
+                assertThat(mac.getValue(), is(macDst));
+                assertThat(mac.getMask(), is(macDstMask));
+                break;
+            case IP_PROTO:
+                IpProtocol ipProtocol = match.get((MatchField<IpProtocol>) matchField);
+                assertThat(ipProtocol, is(IpProtocol.TCP));
+                break;
+            case IPV4_SRC:
+                Masked<IPv4Address> ip = match.getMasked((MatchField<IPv4Address>) matchField);
+                assertThat(ip.getValue(), is(ipSrc));
+                assertThat(ip.getMask(), is(ipSrcMask));
+                break;
+            case IPV4_DST:
+                ip = match.getMasked((MatchField<IPv4Address>) matchField);
+                assertThat(ip.getValue(), is(ipDst));
+                assertThat(ip.getMask(), is(ipDstMask));
+                break;
+            case TCP_SRC:
+                Masked<TransportPort> tcp = match.getMasked((MatchField<TransportPort>) matchField);
+                assertThat(tcp.getValue(), is(tcpSrcMask));
+                assertThat(tcp.getMask(), is(tcpSrcMask));
+                break;
+            default:
+                fail("Unexpected match field returned from iterator");
+            }
+        }
+    }
+}
diff --git a/java_gen/templates/_field_accessors.java b/java_gen/templates/_field_accessors.java
index c7aa3a0..030388c 100644
--- a/java_gen/templates/_field_accessors.java
+++ b/java_gen/templates/_field_accessors.java
@@ -1,10 +1,13 @@
 //:: import os
 //:: for prop in msg.interface.members:
-//:: getter_template_file_name = "%s/custom/%s_%s.java" % (template_dir, msg.name if not builder else msg.name + '.Builder', prop.getter_name)
-//:: if os.path.exists(getter_template_file_name):
-//:: include(getter_template_file_name, msg=msg, builder=builder, has_parent=has_parent)
-
-//:: else:
+//::    if hasattr(prop, "custom_template") and prop.custom_template != None:
+//::        getter_template_file_name = "%s/custom/%s" % (template_dir, prop.custom_template(builder=builder))
+//::    else:
+//::        getter_template_file_name = "%s/custom/%s_%s.java" % (template_dir, msg.name if not builder else msg.name + '.Builder', prop.getter_name)
+//::    #endif
+//::    if os.path.exists(getter_template_file_name):
+//::        include(getter_template_file_name, msg=msg, builder=builder, has_parent=has_parent, prop=prop)
+//::    else:
     @Override
     public ${prop.java_type.public_type} ${prop.getter_name}()${ "" if prop in msg.members else "throws UnsupportedOperationException"} {
 //:: if prop in msg.members:
diff --git a/java_gen/templates/_imports.java b/java_gen/templates/_imports.java
index 55e9a2d..cf7334d 100644
--- a/java_gen/templates/_imports.java
+++ b/java_gen/templates/_imports.java
@@ -1,6 +1,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 import java.util.Map;
@@ -14,10 +15,13 @@
 import org.projectfloodlight.openflow.types.*;
 import org.projectfloodlight.openflow.util.*;
 import org.projectfloodlight.openflow.exceptions.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.jboss.netty.buffer.ChannelBuffer;
 import org.jboss.netty.buffer.ChannelBuffers;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
+import com.google.common.collect.UnmodifiableIterator;
 import com.google.common.hash.Funnel;
 import com.google.common.hash.PrimitiveSink;
diff --git a/java_gen/templates/custom/OFMatchV1Ver10.Builder.java b/java_gen/templates/custom/OFMatchV1Ver10.Builder.java
index 239828e..396e3a0 100644
--- a/java_gen/templates/custom/OFMatchV1Ver10.Builder.java
+++ b/java_gen/templates/custom/OFMatchV1Ver10.Builder.java
@@ -381,7 +381,7 @@
                     wildcards &= ~OFPFW_DL_VLAN_PCP;
                     break;
                 case VLAN_VID:
-                    setVlanVid((VlanVid) value);
+                    setVlanVid((OFVlanVidMatch) value);
                     wildcards &= ~OFPFW_DL_VLAN;
                     break;
                 default:
@@ -492,7 +492,7 @@
                     wildcards |= OFPFW_DL_VLAN_PCP;
                     break;
                 case VLAN_VID:
-                    setVlanVid(VlanVid.NONE);
+                    setVlanVid(OFVlanVidMatch.NONE);
                     wildcards |= OFPFW_DL_VLAN;
                     break;
                 default:
diff --git a/java_gen/templates/custom/OFMatchV1Ver10.java b/java_gen/templates/custom/OFMatchV1Ver10.java
index 82ff10f..7817f10 100644
--- a/java_gen/templates/custom/OFMatchV1Ver10.java
+++ b/java_gen/templates/custom/OFMatchV1Ver10.java
@@ -83,7 +83,7 @@
                 result = ipv4Dst;
                 break;
             case TCP_SRC:
-                result = ipv4Src;
+                result = tcpSrc;
                 break;
             case TCP_DST:
                 result = tcpDst;
@@ -334,3 +334,77 @@
                 throw new UnsupportedOperationException("OFMatch does not support masked matching on field " + field.getName());
         }
     }
+
+    @Override
+    public Iterable<MatchField<?>> getMatchFields() {
+        ImmutableList.Builder<MatchField<?>> builder = ImmutableList.builder();
+        if ((wildcards & OFPFW_IN_PORT) == 0)
+            builder.add(MatchField.IN_PORT);
+        if ((wildcards & OFPFW_DL_VLAN) == 0)
+            builder.add(MatchField.VLAN_VID);
+        if ((wildcards & OFPFW_DL_SRC) == 0)
+            builder.add(MatchField.ETH_SRC);
+        if ((wildcards & OFPFW_DL_DST) == 0)
+            builder.add(MatchField.ETH_DST);
+        if ((wildcards & OFPFW_DL_TYPE) == 0)
+            builder.add(MatchField.ETH_TYPE);
+        if ((wildcards & OFPFW_NW_PROTO) == 0) {
+            if (ethType == EthType.ARP) {
+                builder.add(MatchField.ARP_OP);
+            } else if (ethType == EthType.IPv4) {
+                builder.add(MatchField.IP_PROTO);
+            } else {
+                throw new UnsupportedOperationException(
+                        "Unsupported Ethertype for matching on network protocol " + ethType);
+            }
+        }
+        if ((wildcards & OFPFW_TP_SRC) == 0) {
+            if (ipProto == IpProtocol.UDP) {
+                builder.add(MatchField.UDP_SRC);
+            } else if (ipProto == IpProtocol.TCP) {
+                builder.add(MatchField.TCP_SRC);
+            } else if (ipProto == IpProtocol.SCTP) {
+                builder.add(MatchField.SCTP_SRC);
+            } else {
+                throw new UnsupportedOperationException(
+                        "Unsupported IP protocol for matching on source port " + ipProto);
+            }
+        }
+        if ((wildcards & OFPFW_TP_DST) == 0) {
+            if (ipProto == IpProtocol.UDP) {
+                builder.add(MatchField.UDP_DST);
+            } else if (ipProto == IpProtocol.TCP) {
+                builder.add(MatchField.TCP_DST);
+            } else if (ipProto == IpProtocol.SCTP) {
+                builder.add(MatchField.SCTP_DST);
+            } else {
+                throw new UnsupportedOperationException(
+                        "Unsupported IP protocol for matching on destination port " + ipProto);
+            }
+        }
+        if (((wildcards & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT) < 32) {
+            if (ethType == EthType.ARP) {
+                builder.add(MatchField.ARP_SPA);
+            } else if (ethType == EthType.IPv4) {
+                builder.add(MatchField.IPV4_SRC);
+            } else {
+                throw new UnsupportedOperationException(
+                        "Unsupported Ethertype for matching on source IP " + ethType);
+            }
+        }
+        if (((wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT) < 32) {
+            if (ethType == EthType.ARP) {
+                builder.add(MatchField.ARP_TPA);
+            } else if (ethType == EthType.IPv4) {
+                builder.add(MatchField.IPV4_DST);
+            } else {
+                throw new UnsupportedOperationException(
+                        "Unsupported Ethertype for matching on destination IP " + ethType);
+            }
+        }
+        if ((wildcards & OFPFW_DL_VLAN_PCP) == 0)
+            builder.add(MatchField.VLAN_PCP);
+        if ((wildcards & OFPFW_NW_TOS) == 0)
+            builder.add(MatchField.IP_DSCP);
+        return builder.build();
+    }
diff --git a/java_gen/templates/custom/OFMatchV2Ver11.java b/java_gen/templates/custom/OFMatchV2Ver11.java
index ec7bfcc..ef79ffb 100644
--- a/java_gen/templates/custom/OFMatchV2Ver11.java
+++ b/java_gen/templates/custom/OFMatchV2Ver11.java
@@ -42,3 +42,8 @@
         // FIXME yotam - please replace with real implementation
         return false;
     }
+
+    @Override
+    public Iterable<MatchField<?>> getMatchFields() {
+        throw new UnsupportedOperationException();
+    }
diff --git a/java_gen/templates/custom/OFMatchV3Ver12.java b/java_gen/templates/custom/OFMatchV3Ver12.java
index a4cc51c..81092c1 100644
--- a/java_gen/templates/custom/OFMatchV3Ver12.java
+++ b/java_gen/templates/custom/OFMatchV3Ver12.java
@@ -106,4 +106,9 @@
         OFOxm<?> oxm = this.oxmList.get(field);
 
         return oxm != null && oxm.isMasked();
-    }
\ No newline at end of file
+    }
+
+    @Override
+    public Iterable<MatchField<?>> getMatchFields() {
+        throw new UnsupportedOperationException();
+    }
diff --git a/java_gen/templates/custom/OFMatchV3Ver13.Builder.java b/java_gen/templates/custom/OFMatchV3Ver13.Builder.java
index 48f8b70..79cbdbc 100644
--- a/java_gen/templates/custom/OFMatchV3Ver13.Builder.java
+++ b/java_gen/templates/custom/OFMatchV3Ver13.Builder.java
@@ -1,13 +1,13 @@
 
     private OFOxmList.Builder oxmListBuilder;
 
-    private synchronized void initBuilder() {
+    private void initBuilder() {
         if (oxmListBuilder != null)
             return;
         oxmListBuilder = new OFOxmList.Builder();
     }
 
-    private synchronized void updateOxmList() {
+    private void updateOxmList() {
         this.oxmList = this.oxmListBuilder.build();
         this.oxmListSet = true;
     }
@@ -21,7 +21,7 @@
     }
 
     @Override
-    public synchronized <F extends OFValueType<F>> F get(MatchField<F> field)
+    public <F extends OFValueType<F>> F get(MatchField<F> field)
             throws UnsupportedOperationException {
         OFOxm<F> value = getOxm(field);
         if (value == null)
@@ -30,7 +30,7 @@
     }
 
     @Override
-    public synchronized <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
+    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
             throws UnsupportedOperationException {
         OFOxm<F> value = getOxm(field);
         if (value == null || !value.isMasked())
@@ -50,25 +50,25 @@
     }
 
     @Override
-    public synchronized boolean isExact(MatchField<?> field) {
+    public boolean isExact(MatchField<?> field) {
         OFOxm<?> value = getOxm(field);
         return (value != null && !value.isMasked());
     }
 
     @Override
-    public synchronized boolean isFullyWildcarded(MatchField<?> field) {
+    public boolean isFullyWildcarded(MatchField<?> field) {
         OFOxm<?> value = getOxm(field);
         return (value == null);
     }
 
     @Override
-    public synchronized boolean isPartiallyMasked(MatchField<?> field) {
+    public boolean isPartiallyMasked(MatchField<?> field) {
         OFOxm<?> value = getOxm(field);
         return (value != null && value.isMasked());
     }
 
     @Override
-    public synchronized <F extends OFValueType<F>> Match.Builder setExact(
+    public <F extends OFValueType<F>> Match.Builder setExact(
             MatchField<F> field, F value) {
         initBuilder();
         OFOxm<F> oxm = OFFactories.getFactory(OFVersion.OF_13).oxms().fromValue(value, field);
@@ -78,7 +78,7 @@
     }
 
     @Override
-    public synchronized <F extends OFValueType<F>> Match.Builder setMasked(
+    public <F extends OFValueType<F>> Match.Builder setMasked(
             MatchField<F> field, F value, F mask) {
         initBuilder();
         OFOxm<F> oxm = OFFactories.getFactory(OFVersion.OF_13).oxms().fromValueAndMask(value, mask, field);
@@ -88,7 +88,7 @@
     }
 
     @Override
-    public synchronized <F extends OFValueType<F>> Match.Builder setMasked(
+    public <F extends OFValueType<F>> Match.Builder setMasked(
             MatchField<F> field, Masked<F> valueWithMask) {
         initBuilder();
         OFOxm<F> oxm = OFFactories.getFactory(OFVersion.OF_13).oxms().fromMasked(valueWithMask, field);
@@ -98,7 +98,7 @@
     }
 
     @Override
-    public synchronized <F extends OFValueType<F>> Match.Builder wildcard(MatchField<F> field) {
+    public <F extends OFValueType<F>> Match.Builder wildcard(MatchField<F> field) {
         initBuilder();
         this.oxmListBuilder.unset(field);
         updateOxmList();
diff --git a/java_gen/templates/custom/OFMatchV3Ver13.java b/java_gen/templates/custom/OFMatchV3Ver13.java
index a4cc51c..9bfb234 100644
--- a/java_gen/templates/custom/OFMatchV3Ver13.java
+++ b/java_gen/templates/custom/OFMatchV3Ver13.java
@@ -62,6 +62,7 @@
             case IPV6_SRC:
             case IPV6_DST:
             case IPV6_FLABEL:
+            case BSN_IN_PORTS_128:
                 return true;
             default:
                 return false;
@@ -106,4 +107,32 @@
         OFOxm<?> oxm = this.oxmList.get(field);
 
         return oxm != null && oxm.isMasked();
-    }
\ No newline at end of file
+    }
+
+    private class MatchFieldIterator extends UnmodifiableIterator<MatchField<?>> {
+        private Iterator<OFOxm<?>> oxmIterator;
+
+        MatchFieldIterator() {
+            oxmIterator = oxmList.iterator();
+        }
+
+        @Override
+        public boolean hasNext() {
+            return oxmIterator.hasNext();
+        }
+
+        @Override
+        public MatchField<?> next() {
+            OFOxm<?> next = oxmIterator.next();
+            return next.getMatchField();
+        }
+    }
+
+    @Override
+    public Iterable<MatchField<?>> getMatchFields() {
+        return new Iterable<MatchField<?>>() {
+            public Iterator<MatchField<?>> iterator() {
+                return new MatchFieldIterator();
+            }
+        };
+    }
diff --git a/java_gen/templates/custom/OFOxm_getCanonical.java b/java_gen/templates/custom/OFOxm_getCanonical.java
new file mode 100644
index 0000000..6681870
--- /dev/null
+++ b/java_gen/templates/custom/OFOxm_getCanonical.java
@@ -0,0 +1,17 @@
+//:: import re
+    public ${prop.java_type.public_type} getCanonical() {
+        //:: if not msg.member_by_name("masked").value == "true":
+        // exact match OXM is always canonical
+        return this;
+        //:: else:
+        //:: mask_type = msg.member_by_name("mask").java_type.public_type
+        if (${mask_type}.NO_MASK.equals(mask)) {
+            //:: unmasked = re.sub(r'(.*)Masked(Ver.*)', r'\1\2', msg.name)
+            return new ${unmasked}(value);
+        } else if(${mask_type}.FULL_MASK.equals(mask)) {
+            return null;
+        } else {
+            return this;
+        }
+        //:: #endif
+    }
diff --git a/java_gen/templates/of_class.java b/java_gen/templates/of_class.java
index 7b3b762..36d407c 100644
--- a/java_gen/templates/of_class.java
+++ b/java_gen/templates/of_class.java
@@ -38,6 +38,9 @@
 //:: include("_imports.java", msg=msg)
 
 class ${impl_class} implements ${msg.interface.inherited_declaration()} {
+//:: if genopts.instrument:
+    private static final Logger logger = LoggerFactory.getLogger(${impl_class}.class);
+//:: #endif
     // version: ${version}
     final static byte WIRE_VERSION = ${version.int_version};
 //:: if msg.is_fixed_length:
@@ -216,6 +219,10 @@
                 bb.readerIndex(start);
                 return null;
             }
+            //:: if genopts.instrument:
+            if(logger.isTraceEnabled())
+                logger.trace("readFrom - length={}", ${prop.name});
+            //:: #endif
 //:: elif prop.is_fixed_value:
             // fixed value property ${prop.name} == ${prop.value}
             ${prop.java_type.priv_type} ${prop.name} = ${prop.java_type.read_op(version, pub_type=False)};
@@ -242,11 +249,20 @@
             //:: if os.path.exists("%s/custom/%s.Reader_normalize_stanza.java" % (template_dir, msg.name)):
             //:: include("custom/%s.Reader_normalize_stanza.java" % msg.name, msg=msg, has_parent=False)
             //:: #endif
-             return new ${impl_class}(
+            ${impl_class} ${msg.variable_name} = new ${impl_class}(
                     ${",\n                      ".join(
                          [ prop.name for prop in msg.data_members])}
                     );
+            //:: if genopts.instrument:
+            if(logger.isTraceEnabled())
+                logger.trace("readFrom - read={}", ${msg.variable_name});
+            //:: #endif
+            return ${msg.variable_name};
             //:: else:
+            //:: if genopts.instrument:
+            if(logger.isTraceEnabled())
+                logger.trace("readFrom - returning shared instance={}", INSTANCE);
+            //:: #endif
             return INSTANCE;
             //:: #endif
         }
diff --git a/lang_c.py b/lang_c.py
index fc016f2..9cfc2fa 100644
--- a/lang_c.py
+++ b/lang_c.py
@@ -39,6 +39,7 @@
 import c_gen.c_show_gen as c_show_gen
 import c_gen.c_validator_gen as c_validator_gen
 import c_gen.util
+import loxi_utils.loxi_utils as loxi_utils
 
 def static(out, name):
     c_gen.util.render_template(out, os.path.basename(name))
@@ -110,3 +111,8 @@
     'locitest/src/main.c': static,
     'locitest/Makefile': static,
 }
+
+def generate():
+    for (name, fn) in targets.items():
+        with loxi_utils.open_output(name) as outfile:
+            fn(outfile, os.path.basename(name))
diff --git a/lang_java.py b/lang_java.py
index be94e82..3891076 100644
--- a/lang_java.py
+++ b/lang_java.py
@@ -27,24 +27,9 @@
 
 """
 @brief Java language specific LOXI generating configuration
-
-This language specific file defines:
-
-target_files:  List of internal references to files to generate
-file_gen_map: The map from above file references to generator functions
-file_to_name_map: The map from internal references to external file names
-file_to_subdir_map: The map from internal references to external subdirectories
-
-HOWEVER, since java files are all a function of their class name, we don't
-know in advance what the names of the files/classes will be, so we just
-define a single directory and generate everything in there.
-    @fixme talk to DanT to see if there isn't something that makes more sense
-
 """
 
 import java_gen.codegen as java_codegen
 
-
-targets = {
-    'openflowj/README': java_codegen.gen_all_java
-}
+def generate():
+    java_codegen.gen_all_java()
diff --git a/lang_python.py b/lang_python.py
index 639cf1a..9c9d13e 100644
--- a/lang_python.py
+++ b/lang_python.py
@@ -61,6 +61,8 @@
 "ofp.OFPP_NONE".
 """
 
+import os
+import loxi_utils.loxi_utils as loxi_utils
 import py_gen
 import py_gen.util
 import py_gen.codegen
@@ -99,3 +101,9 @@
     for module in modules[version]:
         filename = '%s/%s/%s.py' % (prefix, subdir, module)
         targets[filename] = make_gen(module, version)
+
+def generate():
+    py_gen.codegen.init()
+    for (name, fn) in targets.items():
+        with loxi_utils.open_output(name) as outfile:
+            fn(outfile, os.path.basename(name))
diff --git a/lang_wireshark.py b/lang_wireshark.py
index 9f73543..3e862d2 100644
--- a/lang_wireshark.py
+++ b/lang_wireshark.py
@@ -38,6 +38,5 @@
 
 import wireshark_gen
 
-targets = {
-    'wireshark/openflow.lua' : wireshark_gen.generate
-}
+def generate():
+    wireshark_gen.generate()
diff --git a/loxi_front_end/frontend.py b/loxi_front_end/frontend.py
index f4dece5..35c1e44 100644
--- a/loxi_front_end/frontend.py
+++ b/loxi_front_end/frontend.py
@@ -62,7 +62,7 @@
     else:
         raise InputError("Dont know how to create member: %s" % m_ast[0])
 
-def create_ofinput(ast):
+def create_ofinput(name, ast):
 
     """
     Create an OFInput from an AST
@@ -72,7 +72,7 @@
     @returns An OFInput object
     """
     ctx = FrontendCtx(set())
-    ofinput = OFInput(wire_versions=set(), classes=[], enums=[])
+    ofinput = OFInput(name, wire_versions=set(), classes=[], enums=[])
 
     for decl_ast in ast:
         if decl_ast[0] == 'struct':
@@ -115,8 +115,4 @@
     if not ofinput.wire_versions:
         raise InputError("Missing #version metadata")
 
-    for used_enum in ctx.used_enums:
-        if not find(lambda e: e.name == used_enum, ofinput.enums):
-            raise Exception("Undeclared enum used in OFInput: {}".format(used_enum))
-
     return ofinput
diff --git a/loxi_front_end/type_maps.py b/loxi_front_end/type_maps.py
index a3394b2..2f02e93 100644
--- a/loxi_front_end/type_maps.py
+++ b/loxi_front_end/type_maps.py
@@ -155,7 +155,7 @@
     if loxi_utils.class_is_list(cls):
         return True
     # TODO get this from the input file when we have virtual class syntax
-    if cls in ["of_flow_mod", "of_stats_request", "of_stats_reply", "of_error_msg", "of_bsn_header", "of_nicira_header", "of_action_bsn", "of_action_nicira", "of_action_id_bsn", "of_action_id_nicira"]:
+    if cls in ["of_flow_mod", "of_stats_request", "of_stats_reply", "of_error_msg", "of_bsn_header", "of_nicira_header", "of_action_bsn", "of_action_nicira", "of_action_id_bsn", "of_action_id_nicira", "of_bsn_stats_request", "of_bsn_stats_reply", "of_experimenter_stats_request", "of_experimenter_stats_reply"]:
         return True
     return False
 
@@ -266,7 +266,8 @@
         meter_features = 11,
         table_features = 12,
         port_desc = 13,
-        experimenter = 0xffff
+        experimenter = 0xffff,
+        bsn_lacp = 0xffff
         )
     }
 
@@ -451,7 +452,9 @@
     "of_port_desc_stats_reply",
     "of_queue_stats_reply",
     "of_table_stats_reply",
-    "of_table_features_stats_reply"
+    "of_table_features_stats_reply",
+    "of_bsn_stats_reply",
+    "of_bsn_lacp_stats_reply",
 ]
 
 stats_request_list = [
@@ -469,7 +472,9 @@
     "of_port_desc_stats_request",
     "of_queue_stats_request",
     "of_table_stats_request",
-    "of_table_features_stats_request"
+    "of_table_features_stats_request",
+    "of_bsn_stats_request",
+    "of_bsn_lacp_stats_request",
 ]
 
 flow_mod_list = [
diff --git a/loxi_ir.py b/loxi_ir.py
index a70f041..0fe5497 100644
--- a/loxi_ir.py
+++ b/loxi_ir.py
@@ -47,11 +47,12 @@
 """
 One input file
 
+@param name Name of the input file
 @param wire_versions Set of integer wire versions this file applies to
 @param classes List of OFClass objects in the same order as in the file
 @param enums List of Enum objects in the same order as in the file
 """
-OFInput = namedtuple('OFInput', ['wire_versions', 'classes', 'enums'])
+OFInput = namedtuple('OFInput', ['name', 'wire_versions', 'classes', 'enums'])
 
 """
 One version of the OpenFlow protocol
diff --git a/loxi_utils/loxi_utils.py b/loxi_utils/loxi_utils.py
index 601a65d..059c363 100644
--- a/loxi_utils/loxi_utils.py
+++ b/loxi_utils/loxi_utils.py
@@ -34,6 +34,7 @@
 """
 
 import sys
+import os
 import of_g
 import tenjin
 from generic_utils import find, memoize
@@ -543,3 +544,16 @@
         context.update(kwargs)
         template = self.get_template(template_name, context, globals)
         return template.render(context, globals, _buf=locals["_buf"])
+
+def open_output(name):
+    """
+    Open an output file for writing
+
+    'name' may include slashes. Subdirectories will be automatically created.
+    """
+    print "Writing %s" % name
+    path = os.path.join(of_g.options.install_dir, name)
+    dirpath = os.path.dirname(path)
+    if not os.path.exists(dirpath):
+        os.makedirs(dirpath)
+    return open(path, "w")
diff --git a/loxigen.py b/loxigen.py
index 05dd6b5..43ff2a8 100755
--- a/loxigen.py
+++ b/loxigen.py
@@ -332,7 +332,7 @@
 
     # Create the OFInput from the AST
     try:
-        ofinput = frontend.create_ofinput(ast)
+        ofinput = frontend.create_ofinput(os.path.basename(filename), ast)
     except frontend.InputError as e:
         print "Error in %s: %s" % (os.path.basename(filename), str(e))
         sys.exit(1)
@@ -417,13 +417,35 @@
     # Ignore emacs backup files
     filenames = [x for x in filenames if not x.endswith('~')]
 
+    # Read input files
+    all_ofinputs = []
     for filename in filenames:
         log("Processing struct file: " + filename)
         ofinput = process_input_file(filename)
-
-        # Populate global state
+        all_ofinputs.append(ofinput)
         for wire_version in ofinput.wire_versions:
             ofinputs_by_version[wire_version].append(ofinput)
+
+    # Merge input files into per-version IR
+    for wire_version, ofinputs in ofinputs_by_version.items():
+        ofprotocol = OFProtocol(wire_version=wire_version, classes=[], enums=[])
+        for ofinput in ofinputs:
+            ofprotocol.classes.extend(ofinput.classes)
+            ofprotocol.enums.extend(ofinput.enums)
+        ofprotocol.classes.sort(key=lambda ofclass: ofclass.name)
+        of_g.ir[wire_version] = ofprotocol
+
+    # Extract enums
+    # An input file can refer to an enum in another file
+    enums_by_version = { ver: {} for ver in ofinputs_by_version }
+    for ofinput in all_ofinputs:
+        for wire_version in ofinput.wire_versions:
+            for enum in ofinput.enums:
+                enums_by_version[wire_version][enum.name] = enum
+
+    # Populate legacy maps
+    for ofinput in all_ofinputs:
+        for wire_version in ofinput.wire_versions:
             version_name = of_g.of_version_wire2name[wire_version]
 
             for ofclass in ofinput.classes:
@@ -442,7 +464,7 @@
                         if m.oftype == 'of_oxm_t':
                             m_type = 'of_octets_t'
                         else:
-                            enum = find(lambda e: e.name == m.oftype, ofinput.enums)
+                            enum = enums_by_version[wire_version].get(m.oftype)
                             if enum and "wire_type" in enum.params:
                                 m_type = enum.params["wire_type"]
                             else:
@@ -457,14 +479,6 @@
                         entry.name, enum.name, entry.value, wire_version,
                         of_g.identifiers, of_g.identifiers_by_group)
 
-        for wire_version, ofinputs in ofinputs_by_version.items():
-            ofprotocol = OFProtocol(wire_version=wire_version, classes=[], enums=[])
-            for ofinput in ofinputs:
-                ofprotocol.classes.extend(ofinput.classes)
-                ofprotocol.enums.extend(ofinput.enums)
-            ofprotocol.classes.sort(key=lambda ofclass: ofclass.name)
-            of_g.ir[wire_version] = ofprotocol
-
 def populate_type_maps():
     """
     Use the type members in the IR to fill out the legacy type_maps.
@@ -535,7 +549,7 @@
 
             # Extensions
             experimenter = find_experimenter('of', cls)
-            if experimenter:
+            if experimenter and ofclass.superclass in ['of_bsn_header', 'of_nicira_header']:
                 val = find_type_value(ofclass, 'subtype')
                 type_maps.extension_message_subtype[wire_version][experimenter][cls] = val
 
@@ -630,12 +644,7 @@
     """
     Create the files for the language target
     """
-    for (name, fn) in lang_module.targets.items():
-        path = of_g.options.install_dir + '/' + name
-        os.system("mkdir -p %s" % os.path.dirname(path))
-        with open(path, "w") as outfile:
-            fn(outfile, os.path.basename(name))
-        print("Wrote contents for " + name)
+    lang_module.generate()
 
 if __name__ == '__main__':
     of_g.loxigen_log_file = open("loxigen.log", "w")
diff --git a/openflow_input/bsn b/openflow_input/bsn
index 0aa674b..dfbf981 100644
--- a/openflow_input/bsn
+++ b/openflow_input/bsn
@@ -46,3 +46,26 @@
     pad(4);
 };
 
+struct of_bsn_stats_request : of_experimenter_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 0xffff;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == ?;
+};
+
+struct of_bsn_stats_reply : of_experimenter_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 0xffff;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == ?;
+};
diff --git a/openflow_input/bsn_lacp b/openflow_input/bsn_lacp
new file mode 100644
index 0000000..d2e540a
--- /dev/null
+++ b/openflow_input/bsn_lacp
@@ -0,0 +1,132 @@
+// Copyright 2013, Big Switch Networks, Inc.
+//
+// LoxiGen is licensed under the Eclipse Public License,
+// version 1.0 (EPL), with the following special exception:
+//
+// LOXI Exception
+//
+// As a special exception to the terms of the EPL, you may
+// distribute libraries generated by LoxiGen (LoxiGen Libraries)
+// under the terms of your choice, provided that copyright and
+// licensing notices generated by LoxiGen are not altered or removed
+// from the LoxiGen Libraries and the notice provided below is (i)
+// included in the LoxiGen Libraries, if distributed in source code
+// form and (ii) included in any documentation for the LoxiGen
+// Libraries, if distributed in binary form.
+//
+// Notice: "Copyright 2013, Big Switch Networks, Inc.
+// This library was generated by the LoxiGen Compiler."
+//
+// You may not use this file except in compliance with the EPL or
+// LOXI Exception. You may obtain a copy of the EPL at:
+//
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// 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 EPL for the specific language
+// governing permissions and limitations under the EPL.
+
+#version 4
+
+// LACP Convergence Status set in of_bsn_lacp_convergence_notif message
+enum of_bsn_lacp_convergence_status_t(wire_type=uint8_t, complete=False) {
+    LACP_SUCCESS = 0,
+    LACP_TIMEDOUT = 1,
+    LACP_OUT_OF_SYNC = 2,
+};
+
+struct of_bsn_set_lacp_request : of_bsn_header {
+    uint8_t version;
+    uint8_t type == 4;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 41;
+    uint8_t enabled;
+    pad(3);
+    of_port_no_t port_no;
+    uint16_t actor_sys_priority;
+    of_mac_addr_t actor_sys_mac;
+    uint16_t actor_port_priority;
+    uint16_t actor_port_num;
+    uint16_t actor_key;
+};
+
+struct of_bsn_set_lacp_reply : of_bsn_header {
+    uint8_t version;
+    uint8_t type == 4;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 42;
+    uint32_t status;
+    of_port_no_t port_no;
+};
+
+struct of_bsn_lacp_convergence_notif : of_bsn_header {
+    uint8_t version;
+    uint8_t type == 4;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 43;
+    uint8_t convergence_status;
+    pad(3);
+    of_port_no_t port_no;
+
+    uint16_t actor_sys_priority;
+    of_mac_addr_t actor_sys_mac;
+    uint16_t actor_port_priority;
+    uint16_t actor_port_num;
+    uint16_t actor_key;
+
+    uint16_t partner_sys_priority;
+    of_mac_addr_t partner_sys_mac;
+    uint16_t partner_port_priority;
+    uint16_t partner_port_num;
+    uint16_t partner_key;
+};
+
+struct of_bsn_lacp_stats_request : of_bsn_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 0xffff;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 1;
+};
+
+struct of_bsn_lacp_stats_entry {
+    of_port_no_t port_no;
+    uint16_t actor_sys_priority;
+    of_mac_addr_t actor_sys_mac;
+    uint16_t actor_port_priority;
+    uint16_t actor_port_num;
+    uint16_t actor_key;
+    uint8_t convergence_status;
+    pad(1);
+    uint16_t partner_sys_priority;
+    of_mac_addr_t partner_sys_mac;
+    uint16_t partner_port_priority;
+    uint16_t partner_port_num;
+    uint16_t partner_key;
+    pad(2);
+};
+
+struct of_bsn_lacp_stats_reply : of_bsn_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 0xffff;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 1;
+    list(of_bsn_lacp_stats_entry_t) entries;
+};
diff --git a/openflow_input/bsn_pdu b/openflow_input/bsn_pdu
index 790604f..66465f3 100644
--- a/openflow_input/bsn_pdu
+++ b/openflow_input/bsn_pdu
@@ -58,6 +58,8 @@
     uint32_t experimenter == 0x5c16c7;
     uint32_t subtype == 32;
     uint32_t status; // 0 means success
+    of_port_no_t port_no;
+    uint8_t slot_num;
 };
 
 struct of_bsn_pdu_rx_request : of_bsn_header {
@@ -82,6 +84,8 @@
     uint32_t experimenter == 0x5c16c7;
     uint32_t subtype == 34;
     uint32_t status; // 0 means success
+    of_port_no_t port_no;
+    uint8_t slot_num;
 };
 
 struct of_bsn_pdu_rx_timeout : of_bsn_header {
diff --git a/openflow_input/standard-1.3 b/openflow_input/standard-1.3
index db7ff0d..b4c4bee 100644
--- a/openflow_input/standard-1.3
+++ b/openflow_input/standard-1.3
@@ -1527,6 +1527,30 @@
     list(of_table_stats_entry_t) entries;
 };
 
+struct of_experimenter_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 0xffff;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+    uint32_t experimenter == ?;
+    uint32_t subtype;
+};
+
+struct of_experimenter_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 0xffff;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    uint32_t experimenter == ?;
+    uint32_t subtype;
+};
+
 // FIXME: These are padded to 8 byte align beyond the length indicated
 
 struct of_table_feature_prop {
diff --git a/py_gen/codegen.py b/py_gen/codegen.py
index 540d212..70053f7 100644
--- a/py_gen/codegen.py
+++ b/py_gen/codegen.py
@@ -34,6 +34,8 @@
 import oftype
 from loxi_ir import *
 
+ofclasses_by_version = {}
+
 PyOFClass = namedtuple('PyOFClass', ['name', 'pyname', 'members', 'type_members',
                                      'min_length', 'is_fixed_length',
                                      'has_internal_alignment', 'has_external_alignment'])
@@ -99,17 +101,17 @@
     util.render_template(out, 'init.py', version=version)
 
 def generate_action(out, name, version):
-    ofclasses = [x for x in build_ofclasses(version)
+    ofclasses = [x for x in ofclasses_by_version[version]
                  if utils.class_is_action(x.name)]
     util.render_template(out, 'action.py', ofclasses=ofclasses, version=version)
 
 def generate_oxm(out, name, version):
-    ofclasses = [x for x in build_ofclasses(version)
+    ofclasses = [x for x in ofclasses_by_version[version]
                  if utils.class_is_oxm(x.name)]
     util.render_template(out, 'oxm.py', ofclasses=ofclasses, version=version)
 
 def generate_common(out, name, version):
-    ofclasses = [x for x in build_ofclasses(version)
+    ofclasses = [x for x in ofclasses_by_version[version]
                  if not utils.class_is_message(x.name)
                     and not utils.class_is_action(x.name)
                     and not utils.class_is_instruction(x.name)
@@ -123,17 +125,17 @@
                          enums=of_g.ir[version].enums)
 
 def generate_instruction(out, name, version):
-    ofclasses = [x for x in build_ofclasses(version)
+    ofclasses = [x for x in ofclasses_by_version[version]
                  if utils.class_is_instruction(x.name)]
     util.render_template(out, 'instruction.py', ofclasses=ofclasses, version=version)
 
 def generate_message(out, name, version):
-    ofclasses = [x for x in build_ofclasses(version)
+    ofclasses = [x for x in ofclasses_by_version[version]
                  if utils.class_is_message(x.name)]
     util.render_template(out, 'message.py', ofclasses=ofclasses, version=version)
 
 def generate_meter_band(out, name, version):
-    ofclasses = [x for x in build_ofclasses(version)
+    ofclasses = [x for x in ofclasses_by_version[version]
                  if utils.class_is_meter_band(x.name)]
     util.render_template(out, 'meter_band.py', ofclasses=ofclasses, version=version)
 
@@ -142,3 +144,7 @@
 
 def generate_util(out, name, version):
     util.render_template(out, 'util.py', version=version)
+
+def init():
+    for version in of_g.supported_wire_protos:
+        ofclasses_by_version[version] = build_ofclasses(version)
diff --git a/py_gen/oftype.py b/py_gen/oftype.py
index c378cea..b8f7de1 100644
--- a/py_gen/oftype.py
+++ b/py_gen/oftype.py
@@ -204,6 +204,7 @@
     'list(of_table_stats_entry_t)': 'common.table_stats_entry.unpack',
     'list(of_uint32_t)': 'common.uint32.unpack',
     'list(of_uint8_t)': 'common.uint8.unpack',
+    'list(of_bsn_lacp_stats_entry_t)': 'common.bsn_lacp_stats_entry.unpack',
 }
 
 for (cls, element_deserializer) in fixed_elem_len_lists.items():
diff --git a/py_gen/templates/message.py b/py_gen/templates/message.py
index c8e31c9..e4c2c7b 100644
--- a/py_gen/templates/message.py
+++ b/py_gen/templates/message.py
@@ -164,6 +164,30 @@
     else:
         raise loxi.ProtocolError("unexpected stats type %u" % stats_type)
 
+def parse_experimenter_stats_request(buf):
+    if len(buf) < 24:
+        raise loxi.ProtocolError("experimenter stats request message too short")
+
+    experimenter, exp_type = struct.unpack_from("!LL", buf, 16)
+
+    if experimenter in experimenter_stats_request_parsers and \
+            exp_type in experimenter_stats_request_parsers[experimenter]:
+        return experimenter_stats_request_parsers[experimenter][exp_type](buf)
+    else:
+        raise loxi.ProtocolError("unexpected stats request experimenter %#x exp_type %#x" % (experimenter, exp_type))
+
+def parse_experimenter_stats_reply(buf):
+    if len(buf) < 24:
+        raise loxi.ProtocolError("experimenter stats reply message too short")
+
+    experimenter, exp_type = struct.unpack_from("!LL", buf, 16)
+
+    if experimenter in experimenter_stats_reply_parsers and \
+            exp_type in experimenter_stats_reply_parsers[experimenter]:
+        return experimenter_stats_reply_parsers[experimenter][exp_type](buf)
+    else:
+        raise loxi.ProtocolError("unexpected stats reply experimenter %#x exp_type %#x" % (experimenter, exp_type))
+
 def parse_experimenter(buf):
     if len(buf) < 16:
         raise loxi.ProtocolError("experimenter message too short")
@@ -234,6 +258,7 @@
     const.OFPST_TABLE : table_stats_reply.unpack,
     const.OFPST_PORT : port_stats_reply.unpack,
     const.OFPST_QUEUE : queue_stats_reply.unpack,
+    const.OFPST_EXPERIMENTER : parse_experimenter_stats_reply,
 :: if version >= of_g.VERSION_1_1:
     const.OFPST_GROUP : group_stats_reply.unpack,
     const.OFPST_GROUP_DESC : group_desc_stats_reply.unpack,
@@ -257,6 +282,7 @@
     const.OFPST_TABLE : table_stats_request.unpack,
     const.OFPST_PORT : port_stats_request.unpack,
     const.OFPST_QUEUE : queue_stats_request.unpack,
+    const.OFPST_EXPERIMENTER : parse_experimenter_stats_request,
 :: if version >= of_g.VERSION_1_1:
     const.OFPST_GROUP : group_stats_request.unpack,
     const.OFPST_GROUP_DESC : group_desc_stats_request.unpack,
@@ -286,3 +312,19 @@
     },
 :: #endfor
 }
+
+experimenter_stats_request_parsers = {
+    0x005c16c7: {
+:: if version >= of_g.VERSION_1_3:
+        1: bsn_lacp_stats_request.unpack,
+:: #endif
+    },
+}
+
+experimenter_stats_reply_parsers = {
+    0x005c16c7: {
+:: if version >= of_g.VERSION_1_3:
+        1: bsn_lacp_stats_reply.unpack,
+:: #endif
+    },
+}
diff --git a/test_data/of13/bsn_lacp_stats_reply.data b/test_data/of13/bsn_lacp_stats_reply.data
new file mode 100644
index 0000000..3e12237
--- /dev/null
+++ b/test_data/of13/bsn_lacp_stats_reply.data
@@ -0,0 +1,72 @@
+-- binary
+04 13 # version, type
+00 3c # length
+12 34 56 78 # xid
+ff ff # stats_type
+00 00 # flags
+00 00 00 00 # pad
+00 5c 16 c7 # experimenter
+00 00 00 1 # subtype
+00 00 f1 11 # entries[0].port_no
+f2 22 # entries[0].actor_sys_priority
+01 02 03 04 05 06 # entries[0].actor_sys_mac
+f3 33 # entries[0].actor_port_priority
+f4 44 # entries[0].actor_port_num
+f5 55 # entries[0].actor_port_key
+02 # entries[0].convergence_status
+00 # pad
+f6 66 # entries[0].partner_sys_priority
+0a 0b 0c 0d 0e 0f # entries[0].partner_sys_mac
+f7 77 # entries[0].partner_port_priority
+f8 88 # entries[0].partner_port_num
+f9 99 # entries[0].partner_port_key
+00 00 # pad
+-- python
+ofp.message.bsn_lacp_stats_reply(
+    xid=0x12345678,
+    flags=0,
+    entries=[
+        ofp.bsn_lacp_stats_entry(
+            port_no=0xf111,
+            actor_sys_priority=0xf222,
+            actor_sys_mac=[1, 2, 3, 4, 5, 6],
+            actor_port_priority=0xf333,
+            actor_port_num=0xf444,
+            actor_key=0xf555,
+            convergence_status=ofp.LACP_OUT_OF_SYNC,
+            partner_sys_priority=0xf666,
+            partner_sys_mac=[0xa, 0xb, 0xc, 0xd, 0xe, 0xf],
+            partner_port_priority=0xf777,
+            partner_port_num=0xf888,
+            partner_key=0xf999)])
+-- c
+obj = of_bsn_lacp_stats_reply_new(OF_VERSION_1_3);
+of_bsn_lacp_stats_reply_xid_set(obj, 0x12345678);
+{
+    of_object_t *entries = of_list_bsn_lacp_stats_entry_new(OF_VERSION_1_3);
+    {
+        of_object_t *elem = of_bsn_lacp_stats_entry_new(OF_VERSION_1_3);
+        of_bsn_lacp_stats_entry_port_no_set(elem, 0xf111);
+        of_bsn_lacp_stats_entry_actor_sys_priority_set(elem, 0xf222);
+        {
+            of_mac_addr_t mac = { { 1, 2, 3, 4, 5, 6 } };
+            of_bsn_lacp_stats_entry_actor_sys_mac_set(elem, mac);
+        }
+        of_bsn_lacp_stats_entry_actor_port_priority_set(elem, 0xf333);
+        of_bsn_lacp_stats_entry_actor_port_num_set(elem, 0xf444);
+        of_bsn_lacp_stats_entry_actor_key_set(elem, 0xf555);
+        of_bsn_lacp_stats_entry_partner_sys_priority_set(elem, 0xf666);
+        of_bsn_lacp_stats_entry_convergence_status_set(elem, LACP_OUT_OF_SYNC);
+        {
+            of_mac_addr_t mac = { { 0xa, 0xb, 0xc, 0xd, 0xe, 0xf } };
+            of_bsn_lacp_stats_entry_partner_sys_mac_set(elem, mac);
+        }
+        of_bsn_lacp_stats_entry_partner_port_priority_set(elem, 0xf777);
+        of_bsn_lacp_stats_entry_partner_port_num_set(elem, 0xf888);
+        of_bsn_lacp_stats_entry_partner_key_set(elem, 0xf999);
+        of_list_append(entries, elem);
+        of_object_delete(elem);
+    }
+    of_bsn_lacp_stats_reply_entries_set(obj, entries);
+    of_object_delete(entries);
+}
diff --git a/test_data/of13/bsn_lacp_stats_request.data b/test_data/of13/bsn_lacp_stats_request.data
new file mode 100644
index 0000000..34aaf94
--- /dev/null
+++ b/test_data/of13/bsn_lacp_stats_request.data
@@ -0,0 +1,18 @@
+-- binary
+04 12 # version, type
+00 18 # length
+12 34 56 78 # xid
+ff ff # stats_type
+00 00 # flags
+00 00 00 00 # pad
+00 5c 16 c7 # experimenter
+00 00 00 1 # subtype
+-- python
+ofp.message.bsn_lacp_stats_request(
+    xid=0x12345678,
+    flags=0)
+-- java
+builder.setXid(0x12345678)
+-- c
+obj = of_bsn_lacp_stats_request_new(OF_VERSION_1_3);
+of_bsn_lacp_stats_request_xid_set(obj, 0x12345678);
diff --git a/utest/test_frontend.py b/utest/test_frontend.py
index 8febfde..19cde4e 100755
--- a/utest/test_frontend.py
+++ b/utest/test_frontend.py
@@ -109,7 +109,7 @@
         ]
         self.assertEquals(expected_ast, ast)
 
-        ofinput = frontend.create_ofinput(ast)
+        ofinput = frontend.create_ofinput("test", ast)
         self.assertEquals(set([1, 2]), ofinput.wire_versions)
         expected_classes = [
             OFClass(name='of_echo_reply', superclass=None, members=[
@@ -180,7 +180,7 @@
         ]
         self.assertEquals(expected_ast, ast)
 
-        ofinput = frontend.create_ofinput(ast)
+        ofinput = frontend.create_ofinput("test", ast)
         expected_classes = [
             OFClass(name='of_queue_prop', superclass=None, members=[
                 OFDiscriminatorMember('type', 'uint16_t'),
diff --git a/wireshark_gen/__init__.py b/wireshark_gen/__init__.py
index c4ff7f4..1225131 100644
--- a/wireshark_gen/__init__.py
+++ b/wireshark_gen/__init__.py
@@ -111,8 +111,10 @@
 
     return r
 
-def generate(out, name):
+def generate():
     context = {
         'fields': create_fields(),
     }
-    utils.render_template(out, "openflow.lua", [templates_dir], context)
+
+    with utils.open_output('openflow.lua') as out:
+        utils.render_template(out, "openflow.lua", [templates_dir], context)
diff --git a/wireshark_gen/field_info.py b/wireshark_gen/field_info.py
index 4a80a8e..ec81665 100644
--- a/wireshark_gen/field_info.py
+++ b/wireshark_gen/field_info.py
@@ -113,9 +113,52 @@
     ('of_instruction_write_actions', 'type'): 'ofp_instruction_type',
     ('of_group_mod', 'group_type'): 'ofp_group_type',
     ('of_group_mod', 'type'): 'ofp_type',
-    ('of_group_mod', 'command'): 'ofp_group_mod_commane',
+    ('of_group_mod', 'command'): 'ofp_group_mod_command',
     ('of_group_mod', 'group_id'): 'ofp_group',
-
+    ('of_packet_out', 'type'): 'ofp_type',
+    ('of_packet_in', 'type'): 'ofp_type',
+    ('of_packet_in', 'reason'): 'ofp_packet_in_reason',
+    ('of_flow_stats_request', 'type'): 'ofp_type',
+    ('of_flow_stats_request', 'stats_type'): 'ofp_stats_type',
+    ('of_desc_stats_request', 'type'): 'ofp_type',
+    ('of_desc_stats_request', 'stats_type'): 'ofp_stats_type',
+    ('of_queue_stats_request', 'type'): 'ofp_type',
+    ('of_queue_stats_request', 'stats_type'): 'ofp_stats_type',
+    ('of_port_stats_request', 'type'): 'ofp_type',
+    ('of_port_stats_request', 'stats_type'): 'ofp_stats_type',
+    ('of_table_stats_request', 'type'): 'ofp_type',
+    ('of_table_stats_request', 'stats_type'): 'ofp_stats_type',
+    ('of_port_desc_stats_request', 'type'): 'ofp_type',
+    ('of_port_desc_stats_request', 'stats_type'): 'ofp_stats_type',
+    ('of_meter_stats_request', 'type'): 'ofp_type',
+    ('of_meter_stats_request', 'stats_type'): 'ofp_stats_type',
+    ('of_meter_features_stats_request', 'type'): 'ofp_type',
+    ('of_meter_features_stats_request', 'stats_type'): 'ofp_stats_type',
+    ('of_meter_config_stats_request', 'type'): 'ofp_type',
+    ('of_meter_config_stats_request', 'stats_type'): 'ofp_stats_type',
+    ('of_group_stats_request', 'type'): 'ofp_type',
+    ('of_group_stats_request', 'stats_type'): 'ofp_stats_type',
+    ('of_group_features_stats_request', 'type'): 'ofp_type',
+    ('of_group_features_stats_request', 'stats_type'): 'ofp_stats_type',
+    ('of_group_desc_stats_request', 'type'): 'ofp_type',
+    ('of_group_desc_stats_request', 'stats_type'): 'ofp_stats_type',
+    ('of_aggregate_stats_request', 'type'): 'ofp_type',
+    ('of_aggregate_stats_request', 'stats_type'): 'ofp_stats_type',
+    ('of_async_get_request', 'type'): 'ofp_type',
+    ('of_flow_stats_reply', 'type'): 'ofp_type',
+    ('of_flow_stats_reply', 'stats_type'): 'ofp_stats_type',
+    ('of_desc_stats_reply', 'type'): 'ofp_type',
+    ('of_desc_stats_reply', 'stats_type'): 'ofp_stats_type',
+    ('of_queue_stats_reply', 'type'): 'ofp_type',
+    ('of_queue_stats_reply', 'stats_type'): 'ofp_stats_type',
+    ('of_port_stats_reply', 'type'): 'ofp_type',
+    ('of_port_stats_reply', 'stats_type'): 'ofp_stats_type',
+    ('of_table_stats_reply', 'type'): 'ofp_type',
+    ('of_table_stats_reply', 'stats_type'): 'ofp_stats_type',
+    ('of_port_desc_stats_reply', 'type'): 'ofp_type',
+    ('of_port_desc_stats_reply', 'stats_type'): 'ofp_stats_type',
+    ('of_aggregate_stats_reply', 'type'): 'ofp_type',
+    ('of_aggregate_stats_reply', 'stats_type'): 'ofp_stats_type',
 }
 
 # Override oftype_to_base for certain field names
diff --git a/wireshark_gen/templates/_oftype_readers.lua b/wireshark_gen/templates/_oftype_readers.lua
index 4f5d7f9..9a5e4fa 100644
--- a/wireshark_gen/templates/_oftype_readers.lua
+++ b/wireshark_gen/templates/_oftype_readers.lua
@@ -81,6 +81,10 @@
     end
 end
 
+function read_of_port_name_t(reader, version, subtree, field_name)
+    read_scalar(reader, subtree, field_name, 16)
+end
+
 function read_of_mac_addr_t(reader, version, subtree, field_name)
     read_scalar(reader, subtree, field_name, 6)
 end
@@ -106,10 +110,30 @@
         return
     end
 
-    local list = subtree:add(fields[field_name], reader.peek_all(0))
-    while not reader.is_empty() do
-        local action_len = reader.peek(2, 2):uint()
-        local child_reader = reader.slice(action_len)
+    local list_len = 0
+
+    if string.find(field_name,'packet_out') then
+        if version == 1 then
+            list_len = reader.peek(-2,2):uint()
+        else
+            list_len = reader.peek(-8,2):uint()
+        end
+    end
+
+    local list = nil
+    local reader2 = nil
+
+    if list_len == 0 then
+        list = subtree:add(fields[field_name], reader.peek_all(0))
+        reader2 = reader
+    else
+        list = subtree:add(fields[field_name], reader.peek(0, list_len))
+        reader2 = reader.slice(list_len)
+    end
+
+    while not reader2.is_empty() do
+        local action_len = reader2.peek(2, 2):uint()
+        local child_reader = reader2.slice(action_len)
         local child_subtree = list:add(fields[field_name], child_reader.peek_all(0))
         local info = dissect_of_action(child_reader, child_subtree, version)
         child_subtree:set_text(info)
@@ -118,11 +142,91 @@
 end
 
 function read_list_of_port_desc_t(reader, version, subtree, field_name)
-    -- TODO
+    if reader.is_empty() then
+        return
+    end
+    local list = subtree:add(fields[field_name], reader.peek_all(0))
+    list:set_text("List of port descriptions")
+    while not reader.is_empty() do
+        local port_desc_len = 64
+        local child_reader = reader.slice(port_desc_len)
+        local child_subtree = list:add(fields[field_name], child_reader.peek_all(0))
+        local info = dissect_of_port_desc(child_reader, child_subtree, version)
+        child_subtree:set_text(info)
+    end
+end
+
+function read_list_of_flow_stats_entry_t(reader, version, subtree, field_name)
+    if reader.is_empty() then
+        return
+    end
+    local list = subtree:add(fields[field_name], reader.peek_all(0))
+    list:set_text("List of flow stats entries")
+    while not reader.is_empty() do
+        local stats_len = reader.peek(0,2):uint()
+        local child_reader = reader.slice(stats_len)
+        local child_subtree = list:add(fields[field_name], child_reader.peek_all(0))
+        local info = dissect_of_flow_stats_entry(child_reader, child_subtree, version)
+        child_subtree:set_text(info)
+    end
+end
+
+function read_list_of_port_stats_entry_t(reader, version, subtree, field_name)
+    if reader.is_empty() then
+        return
+    end
+    local list = subtree:add(fields[field_name], reader.peek_all(0))
+    list:set_text("List of port stats entries")
+    while not reader.is_empty() do
+        local stats_len = 112
+        local child_reader = reader.slice(stats_len)
+        local child_subtree = list:add(fields[field_name], child_reader.peek_all(0))
+        local info = dissect_of_port_stats_entry(child_reader, child_subtree, version)
+        child_subtree:set_text(info)
+    end
+end
+
+function read_list_of_table_stats_entry_t(reader, version, subtree, field_name)
+    if reader.is_empty() then
+        return
+    end
+    local list = subtree:add(fields[field_name], reader.peek_all(0))
+    list:set_text("List of table stats entries")
+    while not reader.is_empty() do
+        local stats_len = 24
+        local child_reader = reader.slice(stats_len)
+        local child_subtree = list:add(fields[field_name], child_reader.peek_all(0))
+        local info = dissect_of_table_stats_entry(child_reader, child_subtree, version)
+        child_subtree:set_text(info)
+    end
+end
+
+function read_list_of_queue_stats_entry_t(reader, version, subtree, field_name)
+    if reader.is_empty() then
+        return
+    end
+    local list = subtree:add(fields[field_name], reader.peek_all(0))
+    list:set_text("List of flow stats entries")
+    while not reader.is_empty() do
+        local stats_len = 40
+        local child_reader = reader.slice(stats_len)
+        local child_subtree = list:add(fields[field_name], child_reader.peek_all(0))
+        local info = dissect_of_queue_stats_entry(child_reader, child_subtree, version)
+        child_subtree:set_text(info)
+    end
 end
 
 function read_list_of_packet_queue_t(reader, version, subtree, field_name)
     -- TODO
+    read_of_octets_t()
+end
+
+function read_of_desc_str_t(reader, version, subtree, field_name)
+    read_scalar(reader, subtree, field_name, 256)
+end
+
+function read_of_serial_num_t(reader, version, subtree, field_name)
+    read_scalar(reader, subtree, field_name, 32)
 end
 
 function read_list_of_oxm_t(reader, version, subtree, field_name)
diff --git a/wireshark_gen/templates/openflow.lua b/wireshark_gen/templates/openflow.lua
index f2791d1..841e502 100644
--- a/wireshark_gen/templates/openflow.lua
+++ b/wireshark_gen/templates/openflow.lua
@@ -125,6 +125,48 @@
 :: #endfor
 }
 
+local of_port_desc_dissectors = {
+:: for version in ir:
+    [${version}] = dissect_of_port_desc_v${version},
+:: #endfor
+}
+
+local of_stats_reply_dissectors = {
+:: for version in ir:
+    [${version}] = of_stats_reply_v${version}_dissectors,
+:: #endfor
+}
+
+local of_stats_request_dissectors = {
+:: for version in ir:
+    [${version}] = of_stats_request_v${version}_dissectors,
+:: #endfor
+}
+
+local of_flow_stats_entry_dissectors = {
+:: for version in ir:
+    [${version}] = dissect_of_flow_stats_entry_v${version},
+:: #endfor
+}
+
+local of_port_stats_entry_dissectors = {
+:: for version in ir:
+    [${version}] = dissect_of_port_stats_entry_v${version},
+:: #endfor
+}
+
+local of_table_stats_entry_dissectors = {
+:: for version in ir:
+    [${version}] = dissect_of_table_stats_entry_v${version},
+:: #endfor
+}
+
+local of_queue_stats_entry_dissectors = {
+:: for version in ir:
+    [${version}] = dissect_of_queue_stats_entry_v${version},
+:: #endfor
+}
+
 function dissect_of_message(buf, root)
     local reader = OFReader.new(buf)
     local subtree = root:add(p_of, buf(0))
@@ -137,7 +179,13 @@
     end
 
     local info = "unknown"
-    if of_message_dissectors[version_val] and of_message_dissectors[version_val][type_val] then
+    if type_val == 19 then
+        local stats_type = buf(8,2):uint()
+        info = of_stats_reply_dissectors[version_val][stats_type](reader,subtree)
+    elseif type_val == 18 then
+        local stats_type = buf(8,2):uint()
+        info = of_stats_request_dissectors[version_val][stats_type](reader,subtree)
+    elseif of_message_dissectors[version_val] and of_message_dissectors[version_val][type_val] then
         info = of_message_dissectors[version_val][type_val](reader, subtree)
     end
 
@@ -183,6 +231,51 @@
     return info
 end
 
+function dissect_of_port_desc(reader, subtree, version_val)
+    local info = "unknown"
+    if of_port_desc_dissectors[version_val] then
+        info = of_port_desc_dissectors[version_val](reader, subtree)
+    end
+
+    return info
+end
+
+function dissect_of_flow_stats_entry(reader, subtree, version_val)
+    local info = "unknown"
+    if of_flow_stats_entry_dissectors[version_val] then
+        info = of_flow_stats_entry_dissectors[version_val](reader, subtree)
+    end
+
+    return info
+end
+
+function dissect_of_port_stats_entry(reader, subtree, version_val)
+    local info = "unknown"
+    if of_port_stats_entry_dissectors[version_val] then
+        info = of_port_stats_entry_dissectors[version_val](reader, subtree)
+    end
+
+    return info
+end
+
+function dissect_of_table_stats_entry(reader, subtree, version_val)
+    local info = "unknown"
+    if of_table_stats_entry_dissectors[version_val] then
+        info = of_table_stats_entry_dissectors[version_val](reader, subtree)
+    end
+
+    return info
+end
+
+function dissect_of_queue_stats_entry(reader, subtree, version_val)
+    local info = "unknown"
+    if of_queue_stats_entry_dissectors[version_val] then
+        info = of_queue_stats_entry_dissectors[version_val](reader, subtree)
+    end
+
+    return info
+end
+
 -- of dissector function
 function p_of.dissector (buf, pkt, root)
     local offset = 0