Merge into master from pull request #327:
loci: automatically derive show emitters (https://github.com/floodlight/loxigen/pull/327)
diff --git a/c_gen/build_of_g.py b/c_gen/build_of_g.py
index 9b19af6..047b254 100755
--- a/c_gen/build_of_g.py
+++ b/c_gen/build_of_g.py
@@ -361,81 +361,6 @@
     """
     Use the type members in the IR to fill out the legacy type_maps.
     """
-
-    def split_inherited_cls(cls):
-        if cls == 'of_meter_band_stats': # HACK not a subtype of of_meter_band
-            return None, None
-        for parent in sorted(type_maps.inheritance_data.keys(), reverse=True):
-            if cls.startswith(parent):
-                return (parent, cls[len(parent)+1:])
-        return None, None
-
-    def find_experimenter(parent, cls):
-        for experimenter in sorted(of_g.experimenter_name_to_id.keys(), reverse=True):
-            prefix = parent + '_' + experimenter
-            if cls.startswith(prefix) and cls != prefix:
-                return experimenter
-        return None
-
-    def find_type_value(ofclass, m_name):
-        for m in ofclass.members:
-            if isinstance(m, OFTypeMember) and m.name == m_name:
-                return m.value
-        raise KeyError("ver=%d, cls=%s, m_name=%s" % (wire_version, cls, m_name))
-
-    # Most inheritance classes: actions, instructions, etc
-    for version, protocol in loxi_globals.ir.items():
-        wire_version = version.wire_version
-        for ofclass in protocol.classes:
-            cls = ofclass.name
-            parent, subcls = split_inherited_cls(cls)
-            if not (parent and subcls):
-                continue
-            if parent == 'of_oxm':
-                type_len = find_type_value(ofclass, 'type_len')
-                oxm_class = (type_len >> 16) & 0xffff
-                if oxm_class != 0x8000:
-                    # Do not include experimenter OXMs in the main table
-                    val = type_maps.invalid_type
-                else:
-                    val = (type_len >> 8) & 0xff
-            else:
-                val = find_type_value(ofclass, 'type')
-            type_maps.inheritance_data[parent][wire_version][subcls] = val
-
-            # Extensions (only actions for now)
-            experimenter = find_experimenter(parent, cls)
-            if parent == 'of_action' and experimenter:
-                val = find_type_value(ofclass, 'subtype')
-                type_maps.extension_action_subtype[wire_version][experimenter][cls] = val
-                if wire_version >= of_g.VERSION_1_3:
-                    cls2 = parent + "_id" + cls[len(parent):]
-                    type_maps.extension_action_id_subtype[wire_version][experimenter][cls2] = val
-            elif parent == 'of_instruction' and experimenter:
-                val = find_type_value(ofclass, 'subtype')
-                type_maps.extension_instruction_subtype[wire_version][experimenter][cls] = val
-
-    # Messages
-    for version, protocol in loxi_globals.ir.items():
-        wire_version = version.wire_version
-        for ofclass in protocol.classes:
-            cls = ofclass.name
-            # HACK (though this is what loxi_utils.class_is_message() does)
-            if not [x for x in ofclass.members if isinstance(x, OFDataMember) and x.name == 'xid']:
-                continue
-            if type_maps.class_is_virtual(cls):
-                continue
-            subcls = cls[3:]
-            val = find_type_value(ofclass, 'type')
-            if not val in type_maps.message_types[wire_version].values():
-                type_maps.message_types[wire_version][subcls] = val
-
-            # Extensions
-            experimenter = find_experimenter('of', cls)
-            if experimenter and ofclass.is_subclassof("of_experimenter"):
-                val = find_type_value(ofclass, 'subtype')
-                type_maps.extension_message_subtype[wire_version][experimenter][cls] = val
-
     type_maps.generate_maps()
 
 def analyze_input():
@@ -448,7 +373,7 @@
     for wire_version, ordered_classes in of_g.ordered_classes.items():
         classes = versions[of_g.of_version_wire2name[wire_version]]['classes']
         for cls in ordered_classes:
-            if cls in type_maps.inheritance_map:
+            if type_maps.class_is_inheritance_root(cls):
                 new_cls = cls + '_header'
                 of_g.ordered_classes[wire_version].append(new_cls)
                 classes[new_cls] = classes[cls]
diff --git a/c_gen/c_code_gen.py b/c_gen/c_code_gen.py
index 6556d3b..3229403 100644
--- a/c_gen/c_code_gen.py
+++ b/c_gen/c_code_gen.py
@@ -874,61 +874,6 @@
 #define OF_MESSAGE_OBJECT_COUNT %d
 """ % ((last + 1), msg_count))
 
-    # Generate object type range checking for inheritance classes
-
-    # @fixme These should be determined algorithmicly
-    out.write("""
-/*
- * Macros to check if an object ID is within an inheritance class range
- */
-""")
-    # Alphabetical order for 'last'
-    last_ids = dict(of_action="OF_ACTION_STRIP_VLAN",
-                    of_oxm="OF_OXM_VLAN_VID_MASKED",
-                    of_instruction="OF_INSTRUCTION_WRITE_METADATA",
-                    of_queue_prop="OF_QUEUE_PROP_MIN_RATE",
-                    of_table_feature_prop="OF_TABLE_FEATURE_PROP_WRITE_SETFIELD_MISS",
-                    # @FIXME add meter_band ?
-                    )
-    for cls, last in last_ids.items():
-        out.write("""
-#define %(enum)s_FIRST_ID      (%(enum)s + 1)
-#define %(enum)s_LAST_ID       %(last)s
-#define %(enum)s_VALID_ID(id) \\
-    ((id) >= %(enum)s_FIRST_ID && \\
-     (id) <= %(enum)s_LAST_ID)
-""" % dict(enum=enum_name(cls), last=last))
-    out.write("""
-/**
- * Function to check a wire ID
- * @param object_id The ID to check
- * @param base_object_id The inheritance parent, if applicable
- * @returns boolean: If base_object_id is an inheritance class, check if
- * object_id is valid as a subclass.  Otherwise return 1.
- *
- * Note: Could check that object_id == base_object_id in the
- * second case.
- */
-static inline int
-of_wire_id_valid(int object_id, int base_object_id) {
-    switch (base_object_id) {
-    case OF_ACTION:
-        return OF_ACTION_VALID_ID(object_id);
-    case OF_OXM:
-        return OF_OXM_VALID_ID(object_id);
-    case OF_QUEUE_PROP:
-        return OF_QUEUE_PROP_VALID_ID(object_id);
-    case OF_TABLE_FEATURE_PROP:
-        return OF_TABLE_FEATURE_PROP_VALID_ID(object_id);
-    case OF_INSTRUCTION:
-        return OF_INSTRUCTION_VALID_ID(object_id);
-    default:
-        break;
-    }
-    return 1;
-}
-""")
-
 ################################################################
 #
 # Internal Utility Functions
@@ -1055,7 +1000,8 @@
     %(cls)s_header_t header; /* Generic instance */
 """ % dict(cls=cls))
         for subcls in sorted(subclasses):
-            out.write("    %s_%s_t %s;\n" % (cls, subcls, subcls))
+            instance = loxi_utils.class_to_instance(subcls, cls)
+            out.write("    %s_%s_t %s;\n" % (cls, instance, instance))
         out.write("};\n")
 
 def gen_struct_typedefs(out):
@@ -1069,7 +1015,7 @@
         out.write("typedef union %(cls)s_u %(cls)s_t;\n" % dict(cls=cls))
     out.write("\n/* LOCI object typedefs */\n")
     for cls in of_g.standard_class_order:
-        if cls in type_maps.inheritance_map:
+        if type_maps.class_is_inheritance_root(cls):
             continue
         template = "typedef of_object_t %(cls)s_t;\n"
         out.write(template % dict(cls=cls))
@@ -1109,7 +1055,7 @@
  ****************************************************************/
 """)
     for cls in of_g.standard_class_order:
-        if cls in type_maps.inheritance_map:
+        if type_maps.class_is_inheritance_root(cls):
             continue
         out.write("\n/* Unified accessor functions for %s */\n" % cls)
         for m_name in of_g.ordered_members[cls]:
@@ -1629,7 +1575,7 @@
     @param cls The class name for the function
     @param out The file to which to write
     """
-    if cls in type_maps.inheritance_map:
+    if type_maps.class_is_inheritance_root(cls):
         param = "obj_p"
     else:
         param = "obj"
@@ -1659,7 +1605,7 @@
 """ % dict(cls=cls, param=param))
 
     # Use an extra pointer to deal with inheritance classes
-    if cls in type_maps.inheritance_map:
+    if type_maps.class_is_inheritance_root(cls):
         out.write("""\
     %s_header_t *obj;
 
@@ -1862,7 +1808,7 @@
  ****************************************************************/
 """)
     for cls in of_g.standard_class_order:
-#        if cls in type_maps.inheritance_map:
+#        if type_maps.class_is_inheritance_root(cls):
 #            continue
         out.write("""
 /**
@@ -1921,7 +1867,7 @@
     out.write("/* DOCUMENTATION ONLY */\n")
 
     for cls in of_g.standard_class_order:
-        if cls in type_maps.inheritance_map:
+        if type_maps.class_is_inheritance_root(cls):
             pass # Check this
 
         out.write("""
diff --git a/c_gen/c_dump_gen.py b/c_gen/c_dump_gen.py
index d4a016c..2f41c35 100644
--- a/c_gen/c_dump_gen.py
+++ b/c_gen/c_dump_gen.py
@@ -90,7 +90,7 @@
         for cls in of_g.standard_class_order:
             if not loxi_utils.class_in_version(cls, version):
                 continue
-            if cls in type_maps.inheritance_map:
+            if type_maps.class_is_inheritance_root(cls):
                 continue
             out.write("""\
 int %(cls)s_%(ver_name)s_dump(loci_writer_f writer, void* cookie, %(cls)s_t *obj);
@@ -129,7 +129,7 @@
         for cls in of_g.standard_class_order:
             if not loxi_utils.class_in_version(cls, version):
                 continue
-            if cls in type_maps.inheritance_map:
+            if type_maps.class_is_inheritance_root(cls):
                 continue
             out.write("""
 int
@@ -234,7 +234,7 @@
                 comma = ","
 
             if (not loxi_utils.class_in_version(cls, version) or
-                    cls in type_maps.inheritance_map):
+                    type_maps.class_is_inheritance_root(cls)):
                 out.write("    unknown_dump%s\n" % comma);
             else:
                 out.write("    %s_%s_dump%s\n" %
diff --git a/c_gen/c_show_gen.py b/c_gen/c_show_gen.py
index 8a784c7..f4d7945 100644
--- a/c_gen/c_show_gen.py
+++ b/c_gen/c_show_gen.py
@@ -163,7 +163,7 @@
         for cls in of_g.standard_class_order:
             if not loxi_utils.class_in_version(cls, version):
                 continue
-            if cls in type_maps.inheritance_map:
+            if type_maps.class_is_inheritance_root(cls):
                 continue
             out.write("""\
 int %(cls)s_%(ver_name)s_show(loci_writer_f writer, void* cookie, %(cls)s_t *obj);
@@ -202,7 +202,7 @@
         for cls in of_g.standard_class_order:
             if not loxi_utils.class_in_version(cls, version):
                 continue
-            if cls in type_maps.inheritance_map:
+            if type_maps.class_is_inheritance_root(cls):
                 continue
             out.write("""
 int
@@ -303,7 +303,7 @@
                 comma = ","
 
             if (not loxi_utils.class_in_version(cls, version) or
-                    cls in type_maps.inheritance_map):
+                    type_maps.class_is_inheritance_root(cls)):
                 out.write("    unknown_show%s\n" % comma);
             else:
                 out.write("    %s_%s_show%s\n" %
diff --git a/c_gen/c_test_gen.py b/c_gen/c_test_gen.py
index 71615e6..41fe122 100644
--- a/c_gen/c_test_gen.py
+++ b/c_gen/c_test_gen.py
@@ -124,6 +124,13 @@
 scalar_types = integer_types[:]
 scalar_types.extend(string_types)
 
+# When embedding an object inside of another object we have to pick a single
+# subclass to use, unlike lists where we use all subclasses.
+embedded_subclasses = {
+    'of_oxm_header_t': 'of_oxm_eth_type',
+    'of_bsn_vport_header_t': 'of_bsn_vport_q_in_q',
+}
+
 def ignore_member(cls, version, m_name, m_type):
     """
     Filter out names or types that either don't have accessors
@@ -372,7 +379,7 @@
         for cls in of_g.standard_class_order:
             if not loxi_utils.class_in_version(cls, version):
                 continue
-            if cls in type_maps.inheritance_map:
+            if type_maps.class_is_inheritance_root(cls):
                 continue
             out.write("""
 extern int %(cls)s_%(v_name)s_populate(
@@ -507,7 +514,7 @@
  */
 """ % v_name)
         for cls in of_g.standard_class_order:
-            if cls in type_maps.inheritance_map:
+            if type_maps.class_is_inheritance_root(cls):
                 continue
             if version in of_g.unified[cls]:
                 message_scalar_test(out, version, cls)
@@ -520,7 +527,7 @@
     for version in of_g.of_version_range:
         v_name = loxi_utils.version_to_name(version)
         for cls in of_g.standard_class_order:
-            if cls in type_maps.inheritance_map:
+            if type_maps.class_is_inheritance_root(cls):
                 continue
             if version in of_g.unified[cls]:
                 test_name = "%s_%s" % (cls, v_name)
@@ -1301,8 +1308,9 @@
     for m_type in member_types:
         if loxi_utils.type_is_scalar(m_type) or m_type in ["of_match_t", "of_octets_t"]:
             out.write("    %s %s;\n" % (m_type, var_name_map(m_type)))
-        elif m_type == "of_bsn_vport_header_t":
-            out.write("    of_bsn_vport_q_in_q_t *%s;\n" % var_name_map(m_type))
+        elif m_type in embedded_subclasses:
+            subcls = embedded_subclasses[m_type]
+            out.write("    %s_t *%s;\n" % (subcls, var_name_map(m_type)))
         else:
             out.write("    %s *%s;\n" % (m_type, var_name_map(m_type)))
     out.write("""
@@ -1313,7 +1321,7 @@
         m_name = member["name"]
         if loxi_utils.skip_member_name(m_name):
             continue
-        if m_type == "of_bsn_vport_header_t":
+        if m_type in embedded_subclasses:
             continue
         if loxi_utils.type_is_scalar(m_type) or m_type in ["of_match_t", "of_octets_t"]:
             out.write("""\
@@ -1363,8 +1371,8 @@
         FREE(octets.data);
     }
 """ % dict(var_name=var_name_map(m_type), cls=cls, m_name=m_name))
-        elif m_type == "of_bsn_vport_header_t": # test q_in_q
-            sub_cls = "of_bsn_vport_q_in_q"
+        elif m_type in embedded_subclasses:
+            sub_cls = embedded_subclasses[m_type]
             out.write("""\
     %(var_name)s = %(sub_cls)s_new(%(v_name)s);
     TEST_ASSERT(%(var_name)s != NULL);
@@ -1443,8 +1451,8 @@
     value = of_octets_check(&%(var_name)s, value);
 """ % dict(cls=cls, var_name=var_name_map(m_type), m_name=m_name,
            v_name=loxi_utils.version_to_name(version)))
-        elif m_type == "of_bsn_vport_header_t": # tests only q_in_q
-            sub_cls = "of_bsn_vport_q_in_q"
+        elif m_type in embedded_subclasses:
+            sub_cls = embedded_subclasses[m_type]
             out.write("""
     { /* Use get/delete to access on check */
         %(sub_cls)s_t *%(m_name)s_ptr;
@@ -1565,7 +1573,7 @@
         for cls in of_g.standard_class_order:
             if not loxi_utils.class_in_version(cls, version):
                 continue
-            if cls in type_maps.inheritance_map:
+            if type_maps.class_is_inheritance_root(cls):
                 continue
             elif loxi_utils.class_is_list(cls):
                 gen_list_setup_check(out, cls, version)
@@ -1588,7 +1596,7 @@
         for cls in of_g.standard_class_order:
             if not loxi_utils.class_in_version(cls, version):
                 continue
-            if cls in type_maps.inheritance_map:
+            if type_maps.class_is_inheritance_root(cls):
                 continue
             unified_accessor_test_case(out, cls, version)
 
@@ -1602,7 +1610,7 @@
         for cls in of_g.standard_class_order:
             if not loxi_utils.class_in_version(cls, version):
                 continue
-            if cls in type_maps.inheritance_map:
+            if type_maps.class_is_inheritance_root(cls):
                 continue
             test_name = "%s_%s" % (cls, v_name)
             out.write("    RUN_TEST(%s);\n" % test_name)
@@ -1682,13 +1690,13 @@
 """ % dict(cls=cls, ver_name=ver_name))
 
     # For each subclass, check if this is an instance of that subclass
-    version_classes = type_maps.inheritance_data[cls][version]
-    for sub_cls in version_classes:
-        sub_enum = (cls + "_" + sub_cls).upper()
+    sub_classes = type_maps.sub_class_map(cls, version)
+    for (_, sub_cls) in sub_classes:
+        sub_enum = sub_cls.upper()
         out.write("""
     if (src->header.object_id == %(sub_enum)s) {
-        return (%(cls)s_t *)%(cls)s_%(sub_cls)s_%(ver_name)s_dup(
-            &src->%(sub_cls)s);
+        return (%(cls)s_t *)%(sub_cls)s_%(ver_name)s_dup(
+            (of_object_t *)src);
     }
 """ % dict(sub_cls=sub_cls, ver_name=ver_name, sub_enum=sub_enum, cls=cls))
 
@@ -1728,11 +1736,12 @@
         if loxi_utils.type_is_scalar(m_type) or m_type in ["of_match_t", "of_octets_t"]:
             # Declare instance of these
             out.write("    %s %s;\n" % (m_type, var_name_map(m_type)))
-        elif m_type == "of_bsn_vport_header_t": # test q_in_q
+        elif m_type in embedded_subclasses:
+            sub_cls = embedded_subclasses[m_type]
             out.write("""
-    of_bsn_vport_q_in_q_t src_%(v_name)s;
-    of_bsn_vport_q_in_q_t *dst_%(v_name)s;
-""" % dict(v_name=var_name_map(m_type)))
+    %(sub_cls)s_t src_%(v_name)s;
+    %(sub_cls)s_t *dst_%(v_name)s;
+""" % dict(v_name=var_name_map(m_type), sub_cls=sub_cls))
         else:
             out.write("""
     %(m_type)s src_%(v_name)s;
@@ -1760,8 +1769,8 @@
     %(cls)s_%(m_name)s_get(src, &%(v_name)s);
     %(cls)s_%(m_name)s_set(dst, &%(v_name)s);
 """ % dict(cls=cls, m_name=m_name, v_name=var_name_map(m_type)))
-        elif m_type == "of_bsn_vport_header_t": # test q_in_q
-            sub_cls = "of_bsn_vport_q_in_q"
+        elif m_type in embedded_subclasses:
+            sub_cls = embedded_subclasses[m_type]
             out.write("""
     %(cls)s_%(m_name)s_bind(
         src, &src_%(v_name)s);
@@ -1814,7 +1823,7 @@
         for cls in of_g.standard_class_order:
             if not loxi_utils.class_in_version(cls, version):
                 continue
-            if cls in type_maps.inheritance_map:
+            if type_maps.class_is_inheritance_root(cls):
                 gen_dup_inheritance(out, cls, version)
             elif loxi_utils.class_is_list(cls):
                 gen_dup_list(out, cls, version)
@@ -1836,7 +1845,7 @@
         for version in of_g.of_version_range:
             if not loxi_utils.class_in_version(cls, version):
                 continue
-            hdr = "header." if cls in type_maps.inheritance_map else ""
+            hdr = "header." if type_maps.class_is_inheritance_root(cls) else ""
 
             ver_name = loxi_utils.version_to_name(version)
             out.write("""
@@ -1940,7 +1949,7 @@
         for j, cls in enumerate(of_g.all_class_order):
             if not loxi_utils.class_in_version(cls, version):
                 continue
-            if cls in type_maps.inheritance_map:
+            if type_maps.class_is_inheritance_root(cls):
                 continue
             if cls == "of_bsn_virtual_port_create_request": # test q_in_q
                 out.write("""
diff --git a/c_gen/codegen.py b/c_gen/codegen.py
index 07b0ade..c1b00ad 100644
--- a/c_gen/codegen.py
+++ b/c_gen/codegen.py
@@ -207,69 +207,29 @@
         if uclass and not uclass.virtual and uclass.has_type_members:
             wire_type_set = '%s_push_wire_types' % uclass.name
 
+        root = uclass.inheritance_root()
+        if root and root.name != 'of_header':
+            wire_type_get = root.name + '_wire_object_id_get'
+
         if uclass.is_message:
             wire_length_get = 'of_object_message_wire_length_get'
             wire_length_set = 'of_object_message_wire_length_set'
-        elif uclass.is_action:
-            wire_length_set = 'of_tlv16_wire_length_set'
-            wire_length_get = 'of_tlv16_wire_length_get'
-            wire_type_get = 'of_action_wire_object_id_get'
-        elif uclass.is_instanceof('of_bsn_vport'):
-            wire_length_set = 'of_tlv16_wire_length_set'
-            wire_length_get = 'of_tlv16_wire_length_get'
-            wire_type_get = 'of_bsn_vport_wire_object_id_get'
-        elif uclass.is_action_id:
-            wire_length_set = 'of_tlv16_wire_length_set'
-            wire_length_get = 'of_tlv16_wire_length_get'
-            wire_type_get = 'of_action_id_wire_object_id_get'
-        elif uclass.is_instruction:
-            wire_length_set = 'of_tlv16_wire_length_set'
-            wire_length_get = 'of_tlv16_wire_length_get'
-            wire_type_get = 'of_instruction_wire_object_id_get'
-        elif uclass.is_instanceof('of_instruction_id'):
-            wire_length_set = 'of_tlv16_wire_length_set'
-            wire_length_get = 'of_tlv16_wire_length_get'
-            wire_type_get = 'of_instruction_id_wire_object_id_get'
-        elif uclass.is_instanceof('of_queue_prop'):
-            wire_length_set = 'of_tlv16_wire_length_set'
-            wire_length_get = 'of_tlv16_wire_length_get'
-            wire_type_get = 'of_queue_prop_wire_object_id_get'
-        elif uclass.is_instanceof('of_table_feature_prop'):
-            wire_length_set = 'of_tlv16_wire_length_set'
-            wire_length_get = 'of_tlv16_wire_length_get'
-            wire_type_get = 'of_table_feature_prop_wire_object_id_get'
-        elif uclass.is_instanceof('of_meter_band'):
-            wire_length_set = 'of_tlv16_wire_length_set'
-            wire_length_get = 'of_tlv16_wire_length_get'
-            wire_type_get = 'of_meter_band_wire_object_id_get'
-        elif uclass.is_instanceof('of_hello_elem'):
-            wire_length_set = 'of_tlv16_wire_length_set'
-            wire_length_get = 'of_tlv16_wire_length_get'
-            wire_type_get = 'of_hello_elem_wire_object_id_get'
-        elif uclass.is_instanceof('of_bsn_tlv'):
-            wire_length_set = 'of_tlv16_wire_length_set'
-            wire_length_get = 'of_tlv16_wire_length_get'
-            wire_type_get = 'of_bsn_tlv_wire_object_id_get'
         elif uclass.is_oxm:
             wire_length_get = 'of_oxm_wire_length_get'
-            wire_type_get = 'of_oxm_wire_object_id_get'
         elif uclass.name == "of_packet_queue":
+            # u16 len, but at offset 4
             wire_length_get = 'of_packet_queue_wire_length_get'
             wire_length_set = 'of_packet_queue_wire_length_set'
         elif uclass.name == "of_meter_stats":
+            # u16 len, but at offset 4
             wire_length_get = 'of_meter_stats_wire_length_get'
             wire_length_set = 'of_meter_stats_wire_length_set'
-        elif uclass.name in ["of_group_desc_stats_entry", "of_group_stats_entry",
-                "of_flow_stats_entry", "of_bucket", "of_table_features",
-                "of_bsn_port_counter_stats_entry", "of_bsn_vlan_counter_stats_entry",
-                "of_bsn_gentable_entry_desc_stats_entry", "of_bsn_gentable_entry_stats_entry",
-                "of_bsn_gentable_desc_stats_entry", "of_bsn_vrf_counter_stats_entry"]:
-            wire_length_get = "of_u16_len_wire_length_get"
-            wire_length_set = "of_u16_len_wire_length_set"
-        elif uclass.name == 'of_match_v3':
+        elif loxi_utils_legacy.class_is_tlv16(uclass.name):
             wire_length_set = 'of_tlv16_wire_length_set'
             wire_length_get = 'of_tlv16_wire_length_get'
-            wire_type_set = 'of_match_v3_push_wire_types'
+        elif loxi_utils_legacy.class_is_u16_len(uclass.name):
+            wire_length_get = "of_u16_len_wire_length_get"
+            wire_length_set = "of_u16_len_wire_length_set"
 
         class_metadata.append(ClassMetadata(
             name=uclass.name,
@@ -278,74 +238,15 @@
             wire_type_get=wire_type_get,
             wire_type_set=wire_type_set))
 
-    class_metadata.extend([
-        ClassMetadata(
-            name="of_action_header",
-            wire_length_set='of_tlv16_wire_length_set',
-            wire_length_get='of_tlv16_wire_length_get',
-            wire_type_get='of_action_wire_object_id_get',
-            wire_type_set='NULL'),
-        ClassMetadata(
-            name="of_action_id_header",
-            wire_length_set='of_tlv16_wire_length_set',
-            wire_length_get='of_tlv16_wire_length_get',
-            wire_type_get='of_action_id_wire_object_id_get',
-            wire_type_set='NULL'),
-        ClassMetadata(
-            name="of_bsn_vport_header",
-            wire_length_set='of_tlv16_wire_length_set',
-            wire_length_get='of_tlv16_wire_length_get',
-            wire_type_get='of_bsn_vport_wire_object_id_get',
-            wire_type_set='NULL'),
-        ClassMetadata(
-            name="of_instruction_header",
-            wire_length_set='of_tlv16_wire_length_set',
-            wire_length_get='of_tlv16_wire_length_get',
-            wire_type_get='of_instruction_wire_object_id_get',
-            wire_type_set='NULL'),
-        ClassMetadata(
-            name="of_instruction_id_header",
-            wire_length_set='of_tlv16_wire_length_set',
-            wire_length_get='of_tlv16_wire_length_get',
-            wire_type_get='of_instruction_id_wire_object_id_get',
-            wire_type_set='NULL'),
-        ClassMetadata(
-            name="of_queue_prop_header",
-            wire_length_set='of_tlv16_wire_length_set',
-            wire_length_get='of_tlv16_wire_length_get',
-            wire_type_get='of_queue_prop_wire_object_id_get',
-            wire_type_set='NULL'),
-        ClassMetadata(
-            name="of_table_feature_prop_header",
-            wire_length_set='of_tlv16_wire_length_set',
-            wire_length_get='of_tlv16_wire_length_get',
-            wire_type_get='of_table_feature_prop_wire_object_id_get',
-            wire_type_set='NULL'),
-        ClassMetadata(
-            name="of_meter_band_header",
-            wire_length_set='of_tlv16_wire_length_set',
-            wire_length_get='of_tlv16_wire_length_get',
-            wire_type_get='of_meter_band_wire_object_id_get',
-            wire_type_set='NULL'),
-        ClassMetadata(
-            name="of_hello_elem_header",
-            wire_length_set='of_tlv16_wire_length_set',
-            wire_length_get='of_tlv16_wire_length_get',
-            wire_type_get='of_hello_elem_wire_object_id_get',
-            wire_type_set='NULL'),
-        ClassMetadata(
-            name="of_bsn_tlv_header",
-            wire_length_set='of_tlv16_wire_length_set',
-            wire_length_get='of_tlv16_wire_length_get',
-            wire_type_get='of_bsn_tlv_wire_object_id_get',
-            wire_type_set='NULL'),
-        ClassMetadata(
-            name="of_oxm_header",
-            wire_length_set='NULL',
-            wire_length_get='of_oxm_wire_length_get',
-            wire_type_get='of_oxm_wire_object_id_get',
-            wire_type_set='NULL'),
-    ])
+        # If this is the root of an inheritance hierachy, add metadata
+        # for the corresponding header class
+        if uclass.name in type_maps.inheritance_map:
+            class_metadata.append(ClassMetadata(
+                name=uclass.name + '_header',
+                wire_length_get=wire_length_get,
+                wire_length_set=wire_length_set,
+                wire_type_get=wire_type_get,
+                wire_type_set=wire_type_set))
 
     for metadata in class_metadata:
         class_metadata_dict[metadata.name] = metadata
diff --git a/c_gen/loxi_utils_legacy.py b/c_gen/loxi_utils_legacy.py
index 4472ffa..0399638 100644
--- a/c_gen/loxi_utils_legacy.py
+++ b/c_gen/loxi_utils_legacy.py
@@ -37,6 +37,8 @@
 import c_gen.of_g_legacy as of_g
 import tenjin
 from generic_utils import find, memoize
+import loxi_globals
+from loxi_ir import ir
 
 def class_signature(members):
     """
@@ -99,40 +101,49 @@
     """
     Return True if cls_name is an object which uses uint16 for type and length
     """
-    if cls.find("of_action") == 0: # Includes of_action_id classes
-        return True
-    if cls.find("of_instruction") == 0:
-        return True
-    if cls.find("of_queue_prop") == 0:
-        return True
-    if cls.find("of_table_feature_prop") == 0:
-        return True
-    # *sigh*
-    if cls.find("of_meter_band_stats") == 0:  # NOT A TLV
+
+    ofclass = loxi_globals.unified.class_by_name(cls)
+    if not ofclass:
         return False
-    if cls.find("of_meter_band") == 0:
-        return True
-    if cls.find("of_hello_elem") == 0:
-        return True
-    if cls == "of_match_v3":
-        return True
-    if cls == "of_match_v4":
-        return True
-    if cls.find("of_bsn_tlv") == 0:
-        return True
-    if cls.find("of_bsn_vport") == 0:
-        return True
-    return False
+
+    if len(ofclass.members) < 2:
+        return False
+
+    m1 = ofclass.members[0]
+    m2 = ofclass.members[1]
+
+    if not (isinstance(m1, ir.OFTypeMember) or isinstance(m1, ir.OFDiscriminatorMember)):
+        return False
+
+    if not isinstance(m2, ir.OFLengthMember):
+        return False
+
+    if m1.oftype != "uint16_t" or m2.oftype != "uint16_t":
+        return False
+
+    return True
 
 def class_is_u16_len(cls):
     """
     Return True if cls_name is an object which uses initial uint16 length
     """
-    return cls in ["of_group_desc_stats_entry", "of_group_stats_entry",
-                   "of_flow_stats_entry", "of_bucket", "of_table_features",
-                   "of_bsn_port_counter_stats_entry", "of_bsn_vlan_counter_stats_entry",
-                   "of_bsn_gentable_entry_desc_stats_entry", "of_bsn_gentable_entry_stats_entry",
-                   "of_bsn_gentable_desc_stats_entry", "of_bsn_vrf_counter_stats_entry"]
+
+    ofclass = loxi_globals.unified.class_by_name(cls)
+    if not ofclass:
+        return False
+
+    if len(ofclass.members) < 1:
+        return False
+
+    m = ofclass.members[0]
+
+    if not isinstance(m, ir.OFLengthMember):
+        return False
+
+    if m.oftype != "uint16_t":
+        return False
+
+    return True
 
 def class_is_list(cls):
     """
@@ -219,6 +230,10 @@
     """
     return parent + "_" + instance
 
+def class_to_instance(cls, base_cls):
+    assert cls.startswith(base_cls + '_')
+    return cls[len(base_cls)+1:]
+
 def class_is_var_len(cls, version):
     # Match is special case.  Only version 1.2 (wire version 3) is var
     if cls == "of_match":
@@ -283,7 +298,7 @@
     """
     Convert an integer version to the C macro name
     """
-    return "OF_" + of_g.version_names[version]
+    return of_g.of_version_wire2name[version]
 
 def gen_c_copy_license(out):
     """
diff --git a/c_gen/of_g_legacy.py b/c_gen/of_g_legacy.py
index b58ca08..2372b31 100644
--- a/c_gen/of_g_legacy.py
+++ b/c_gen/of_g_legacy.py
@@ -31,8 +31,7 @@
 # @fixme This needs to be refactored and brought into the 21st century.
 #
 
-import sys
-# @fixme Replace with argparse
+import loxi_globals
 
 ################################################################
 #
@@ -286,25 +285,21 @@
 # Indexed by (cls, version, member-name) and value is prev-member-name
 special_offsets = {}
 
-## Define Python variables with integer wire version values
-VERSION_1_0 = 1
-VERSION_1_1 = 2
-VERSION_1_2 = 3
-VERSION_1_3 = 4
-
-version_names = {1:"VERSION_1_0", 2:"VERSION_1_1", 3:"VERSION_1_2",
-                 4:"VERSION_1_3"}
-short_version_names = {1:"OF_1_0", 2:"OF_1_1", 3:"OF_1_2", 4:"OF_1_3"}
+# Map from wire version to OF_1_x
+short_version_names = {}
 
 # The iteration object that gives the wire versions supported
-of_version_range = [VERSION_1_0, VERSION_1_1, VERSION_1_2, VERSION_1_3]
+of_version_range = []
 
-of_version_wire2name = {
-    VERSION_1_0:"OF_VERSION_1_0",
-    VERSION_1_1:"OF_VERSION_1_1",
-    VERSION_1_2:"OF_VERSION_1_2",
-    VERSION_1_3:"OF_VERSION_1_3"
-    }
+# Map from wire version to OF_VERSION_1_x
+of_version_wire2name = {}
+
+for version in loxi_globals.OFVersions.all_supported:
+    v = version.version.replace('.', '_')
+    short_version_names[version.wire_version] = 'OF_' + v
+    of_version_range.append(version.wire_version)
+    of_version_wire2name[version.wire_version] = 'OF_VERSION_' + v
+    globals()['VERSION_' + v] = version.wire_version
 
 
 ################################################################
diff --git a/c_gen/templates/of_object.c b/c_gen/templates/of_object.c
index 5647d81..596d79a 100644
--- a/c_gen/templates/of_object.c
+++ b/c_gen/templates/of_object.c
@@ -581,9 +581,6 @@
     if (loci_class_metadata[obj->object_id].wire_type_get != NULL) {
         of_object_id_t id;
         loci_class_metadata[obj->object_id].wire_type_get(obj, &id);
-        if (!of_wire_id_valid(id, base_object_id)) {
-            return OF_ERROR_PARSE;
-        }
         obj->object_id = id;
         /* Call the init function for this object type; do not push to wire */
         of_object_init_map[id]((of_object_t *)(obj), obj->version, -1, 0);
diff --git a/c_gen/type_maps.py b/c_gen/type_maps.py
index 6cbb3a5..3544d80 100644
--- a/c_gen/type_maps.py
+++ b/c_gen/type_maps.py
@@ -25,126 +25,12 @@
 # EPL for the specific language governing permissions and limitations
 # under the EPL.
 
-#
-# Miscellaneous type information
-#
-# Define the map between sub-class types and wire values.  In each
-# case, an array indexed by wire version gives a hash from identifier
-# to wire value.
-#
-
-import c_gen.of_g_legacy as of_g
-import sys
 from generic_utils import *
-import loxi_utils.loxi_utils as loxi_utils
 import c_gen.loxi_utils_legacy as loxi_utils
 import loxi_globals
 
-invalid_type = "invalid_type"
-
-################################################################
-#
-# Define type data for inheritance classes:
-#   instructions, actions, queue properties and OXM
-#
-# Messages are not in this group; they're treated specially for now
-#
-# These are indexed by wire protocol number
-#
-################################################################
-
-instruction_types = {
-    of_g.VERSION_1_0:dict(),
-    of_g.VERSION_1_1:dict(),
-    of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:dict()
-    }
-
-instruction_id_types = {
-    of_g.VERSION_1_0:dict(),
-    of_g.VERSION_1_1:dict(),
-    of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:dict()
-    }
-
-action_types = {
-    of_g.VERSION_1_0:dict(),
-    of_g.VERSION_1_1:dict(),
-    of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:dict(),
-    }
-
-action_id_types = {
-    of_g.VERSION_1_0:dict(),
-    of_g.VERSION_1_1:dict(),
-    of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:dict(),
-    }
-
-queue_prop_types = {
-    of_g.VERSION_1_0:dict(),
-    of_g.VERSION_1_1:dict(),
-    of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:dict()
-    }
-
-bsn_vport_types = {
-    of_g.VERSION_1_0:dict(),
-    of_g.VERSION_1_1:dict(),
-    of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:dict(),
-    }
-
-oxm_types = {
-    of_g.VERSION_1_0:dict(),
-    of_g.VERSION_1_1:dict(),
-    of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:dict(),
-    }
-
-hello_elem_types = {
-    of_g.VERSION_1_0:dict(),
-    of_g.VERSION_1_1:dict(),
-    of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:dict(),
-    }
-
-table_feature_prop_types = {
-    of_g.VERSION_1_0:dict(),
-    of_g.VERSION_1_1:dict(),
-    of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:dict(),
-    }
-
-meter_band_types = {
-    of_g.VERSION_1_0:dict(),
-    of_g.VERSION_1_1:dict(),
-    of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:dict(),
-    }
-
-bsn_tlv_types = {
-    of_g.VERSION_1_0:dict(),
-    of_g.VERSION_1_1:dict(),
-    of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:dict(),
-    }
-
-# All inheritance data for non-messages
-inheritance_data = dict(
-    of_instruction = instruction_types,
-    of_instruction_id = instruction_id_types,
-    of_action = action_types,
-    of_action_id = action_id_types,
-    of_oxm = oxm_types,
-    of_queue_prop = queue_prop_types,
-    of_hello_elem = hello_elem_types,
-    of_table_feature_prop = table_feature_prop_types,
-    of_meter_band = meter_band_types,
-    # BSN specific inheritance extensions
-    of_bsn_vport = bsn_vport_types,
-    of_bsn_tlv = bsn_tlv_types,
-    )
+# map from inheritance root class name to set of subclass names
+inheritance_map = {}
 
 def class_is_virtual(cls):
     """
@@ -156,301 +42,20 @@
         return True
     return loxi_globals.unified.class_by_name(cls).virtual
 
-################################################################
-#
-# These are message types
-#
-################################################################
-
-# The hardcoded message types are for inheritance parents
-message_types = {
-    # version 1.0
-    of_g.VERSION_1_0:dict(
-        error_msg               = 1,
-        experimenter            = 4,
-        flow_mod                = 14,
-        stats_request           = 16,
-        stats_reply             = 17,
-        ),
-
-    # version 1.1
-    of_g.VERSION_1_1:dict(
-        error_msg               = 1,
-        experimenter            = 4,
-        flow_mod                = 14,
-        group_mod               = 15,
-        stats_request           = 18,
-        stats_reply             = 19,
-        ),
-
-    # version 1.2
-    of_g.VERSION_1_2:dict(
-        error_msg               = 1,
-        experimenter            = 4,
-        flow_mod                = 14,
-        group_mod               = 15,
-        stats_request           = 18,
-        stats_reply             = 19,
-        ),
-
-    # version 1.3
-    of_g.VERSION_1_3:dict(
-        error_msg               = 1,
-        experimenter            = 4,
-        flow_mod                = 14,
-        group_mod               = 15,
-        stats_request           = 18,  # FIXME Multipart
-        stats_reply             = 19,
-        )
-    }
-
-################################################################
-#
-# These are other objects that have a notion of type but are
-# not (yet) promoted to objects with inheritance
-#
-################################################################
-
-stats_types = {
-    # version 1.0
-    of_g.VERSION_1_0:dict(
-        desc = 0,
-        flow = 1,
-        aggregate = 2,
-        table = 3,
-        port = 4,
-        queue = 5,
-        experimenter = 0xffff
-        ),
-
-    # version 1.1
-    of_g.VERSION_1_1:dict(
-        desc = 0,
-        flow = 1,
-        aggregate = 2,
-        table = 3,
-        port = 4,
-        queue = 5,
-        group = 6,
-        group_desc = 7,
-        experimenter = 0xffff
-        ),
-
-    # version 1.2
-        of_g.VERSION_1_2:dict(
-        desc = 0,
-        flow = 1,
-        aggregate = 2,
-        table = 3,
-        port = 4,
-        queue = 5,
-        group = 6,
-        group_desc = 7,
-        group_features = 8,
-        experimenter = 0xffff
-        ),
-
-    # version 1.3
-        of_g.VERSION_1_3:dict(
-        desc = 0,
-        flow = 1,
-        aggregate = 2,
-        table = 3,
-        port = 4,
-        queue = 5,
-        group = 6,
-        group_desc = 7,
-        group_features = 8,
-        meter = 9,
-        meter_config = 10,
-        meter_features = 11,
-        table_features = 12,
-        port_desc = 13,
-        experimenter = 0xffff,
-        bsn_lacp = 0xffff,
-        bsn_switch_pipeline = 0xffff,
-        bsn_port_counter = 0xffff,
-        bsn_vlan_counter = 0xffff
-        )
-    }
-
-common_flow_mod_types = dict(
-    add = 0,
-    modify = 1,
-    modify_strict = 2,
-    delete = 3,
-    delete_strict = 4
-    )
-
-flow_mod_types = {
-    # version 1.0
-    of_g.VERSION_1_0:common_flow_mod_types,
-    of_g.VERSION_1_1:common_flow_mod_types,
-    of_g.VERSION_1_2:common_flow_mod_types,
-    of_g.VERSION_1_3:common_flow_mod_types
-    }
-
-# These do not translate to objects (yet)
-error_types = {
-    # version 1.0
-    of_g.VERSION_1_0:dict(
-        hello_failed        = 0,
-        bad_request         = 1,
-        bad_action          = 2,
-        flow_mod_failed     = 3,
-        port_mod_failed     = 4,
-        queue_op_failed     = 5
-        ),
-
-    # version 1.1
-    of_g.VERSION_1_1:dict(
-        hello_failed         = 0,
-        bad_request          = 1,
-        bad_action           = 2,
-        bad_instruction      = 3,
-        bad_match            = 4,
-        flow_mod_failed      = 5,
-        group_mod_failed     = 6,
-        port_mod_failed      = 7,
-        table_mod_failed     = 8,
-        queue_op_failed      = 9,
-        switch_config_failed = 10
-        ),
-
-    # version 1.2
-    of_g.VERSION_1_2:dict(
-        hello_failed         = 0,
-        bad_request          = 1,
-        bad_action           = 2,
-        bad_instruction      = 3,
-        bad_match            = 4,
-        flow_mod_failed      = 5,
-        group_mod_failed     = 6,
-        port_mod_failed      = 7,
-        table_mod_failed     = 8,
-        queue_op_failed      = 9,
-        switch_config_failed = 10,
-        role_request_failed  = 11,
-        experimenter = 0xffff
-        ),
-
-    # version 1.3
-    of_g.VERSION_1_3:dict(
-        hello_failed         = 0,
-        bad_request          = 1,
-        bad_action           = 2,
-        bad_instruction      = 3,
-        bad_match            = 4,
-        flow_mod_failed      = 5,
-        group_mod_failed     = 6,
-        port_mod_failed      = 7,
-        table_mod_failed     = 8,
-        queue_op_failed      = 9,
-        switch_config_failed = 10,
-        role_request_failed  = 11,
-        meter_mod_failed     = 12,
-        table_features_failed= 13,
-        experimenter = 0xffff
-        )
-    }
-
-group_mod_types = {
-    # version 1.0
-    of_g.VERSION_1_0:dict(),
-
-    # version 1.1
-    of_g.VERSION_1_1:dict(
-        add = 0,
-        modify = 1,
-        delete = 2
-        ),
-
-    # version 1.2
-    of_g.VERSION_1_2:dict(
-        add = 0,
-        modify = 1,
-        delete = 2
-        ),
-
-    # version 1.3
-    of_g.VERSION_1_3:dict(
-        add = 0,
-        modify = 1,
-        delete = 2
-        )
-    }
-
-################################################################
-#
-# type_val is the primary data structure that maps an
-# (class_name, version) pair to the wire data type value
-#
-################################################################
-
-type_val = dict()
-inheritance_map = dict()
+def class_is_inheritance_root(cls):
+    return cls in inheritance_map
 
 def generate_maps():
-    for parent, versioned in inheritance_data.items():
-        inheritance_map[parent] = set()
-        for ver, subclasses in versioned.items():
-            for subcls in subclasses:
-                inheritance_map[parent].add(subcls)
+    for version, protocol in loxi_globals.ir.items():
+        wire_version = version.wire_version
+        for ofclass in protocol.classes:
+            root = ofclass.inheritance_root()
+            if not root or root == ofclass or root.name == "of_header":
+                continue
 
-    for version, classes in message_types.items():
-        for cls in classes:
-            name = "of_" + cls
-            type_val[(name, version)] = classes[cls]
-
-    for parent, versioned in inheritance_data.items():
-        for version, subclasses in versioned.items():
-            for subcls, value in subclasses.items():
-                name = parent + "_" + subcls
-                type_val[(name, version)] = value
-
-    # Special case OF-1.2 match type
-    type_val[("of_match_v3", of_g.VERSION_1_2)] = 1
-    type_val[("of_match_v3", of_g.VERSION_1_3)] = 1
-
-# Utility function
-def dict_to_array(d, m_val, def_val=-1):
-    """
-    Given a dictionary, d, with each value a small integer,
-    produce an array indexed by the integer whose value is the key.
-    @param d The dictionary
-    @param m_val Ignore values greater than m_val
-    @param def_val The default value (for indices not in range of d)
-    """
-
-    # Get the max value in range for hash
-    max_val = 0
-    for key in d:
-        if (d[key] > max_val) and (d[key] < m_val):
-            max_val = d[key]
-    ar = []
-    for x in range(0, max_val + 1):
-        ar.append(def_val)
-    for key in d:
-        if (d[key] < m_val):
-            ar[d[key]] = key
-    return ar
-
-def type_array_len(version_indexed, max_val):
-    """
-    Given versioned information about a type, calculate how long
-    the unified array should be.
-
-    @param version_indexed A dict indexed by version. Each value is a
-    dict indexed by a name and whose value is an integer
-    @param max_val Ignore values greater than this for length calcs
-    """
-    # First, find the max length of all arrays
-    arr_len = 0
-    for version, val_dict in version_indexed.items():
-        ar = dict_to_array(val_dict, max_val, invalid_type)
-        if arr_len < len(ar):
-            arr_len = len(ar)
-    return arr_len
+            if root.name not in inheritance_map:
+                inheritance_map[root.name] = set()
+            inheritance_map[root.name].add(ofclass.name)
 
 def sub_class_map(base_type, version):
     """
@@ -461,99 +66,10 @@
     if base_type not in inheritance_map:
         return rv
 
-    for instance in inheritance_map[base_type]:
-        subcls = loxi_utils.instance_to_class(instance, base_type)
+    for subcls in inheritance_map[base_type]:
         if not loxi_utils.class_in_version(subcls, version):
             continue
+        instance = loxi_utils.class_to_instance(subcls, base_type)
         rv.append((instance, subcls))
 
     return rv
-
-################################################################
-#
-# Extension related data and functions
-#
-################################################################
-
-# Per OF Version, per experimenter, map exp msg type (subtype) to object IDs
-# @fixme Generate defines for OF_<exp>_SUBTYPE_<msg> for the values below?
-extension_message_subtype = {
-    # version 1.0
-    of_g.VERSION_1_0:dict(  # Version 1.0 extensions
-        bsn = {   # BSN extensions; indexed by class name, value is subtype
-            },
-        nicira = {   # Nicira extensions, value is subtype
-            },
-        ),
-    of_g.VERSION_1_1:dict(  # Version 1.0 extensions
-        bsn = {   # BSN extensions; indexed by class name, value is subtype
-            },
-        ),
-    of_g.VERSION_1_2:dict(  # Version 1.0 extensions
-        bsn = {   # BSN extensions; indexed by class name, value is subtype
-            },
-        ),
-    of_g.VERSION_1_3:dict(  # Version 1.0 extensions
-        bsn = {   # BSN extensions; indexed by class name, value is subtype
-            },
-        ),
-}
-
-# Set to empty dict if no extension actions defined
-# Per OF Version, per experimenter, map actions to subtype
-extension_action_subtype = {
-    # version 1.0
-    of_g.VERSION_1_0:dict(  # Version 1.0 extensions
-        bsn = {   # of_action_bsn_
-            },
-        nicira = {   # of_action_nicira_
-            }
-        ),
-    of_g.VERSION_1_1:dict(  # Version 1.0 extensions
-        bsn = {   # of_action_bsn_
-            },
-        nicira = {   # of_action_nicira_
-            }
-        ),
-    of_g.VERSION_1_2:dict(  # Version 1.0 extensions
-        bsn = {   # of_action_bsn_
-            },
-        nicira = {   # of_action_nicira_
-            }
-        ),
-    of_g.VERSION_1_3:dict(  # Version 1.0 extensions
-        bsn = {   # of_action_bsn_
-            },
-        nicira = {   # of_action_nicira_
-            }
-        ),
-}
-
-# Set to empty dict if no extension actions defined
-# Per OF Version, per experimenter, map actions to subtype
-extension_action_id_subtype = {
-    # version 1.0
-    of_g.VERSION_1_0:dict(),
-    of_g.VERSION_1_1:dict(),
-    of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:dict(  # Version 1.3 extensions
-        bsn = {   # of_action_bsn_
-            },
-        nicira = {   # of_action_nicira_
-            }
-        ),
-}
-
-# Set to empty dict if no extension instructions defined
-extension_instruction_subtype = {
-    # version 1.0
-    of_g.VERSION_1_0:dict(),
-    of_g.VERSION_1_1:dict(),
-    of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:dict(
-        bsn = {   # of_instruction_bsn_
-            },
-        nicira = {   # of_instruction_nicira_
-            }
-        ),
-}
diff --git a/loxi_ir/ir.py b/loxi_ir/ir.py
index 0e95473..7b9a0b6 100644
--- a/loxi_ir/ir.py
+++ b/loxi_ir/ir.py
@@ -145,6 +145,15 @@
     def is_subclassof(self, super_class_name):
         return self.name != super_class_name and self.is_instanceof(super_class_name)
 
+    def inheritance_root(self):
+        if not self.superclass:
+            if self.virtual:
+                return self
+            else:
+                return None
+        else:
+            return self.superclass.inheritance_root()
+
     @property
     def is_message(self):
         return self.is_instanceof("of_header")
diff --git a/openflow_input/bsn_tlv b/openflow_input/bsn_tlv
index cc81b96..3e52d02 100644
--- a/openflow_input/bsn_tlv
+++ b/openflow_input/bsn_tlv
@@ -274,3 +274,69 @@
     uint16_t length;
     uint64_t value;
 };
+
+struct of_bsn_tlv_actor_system_priority: of_bsn_tlv {
+    uint16_t type == 40;
+    uint16_t length;
+    uint16_t value;
+};
+
+struct of_bsn_tlv_actor_system_mac: of_bsn_tlv {
+    uint16_t type == 41;
+    uint16_t length;
+    of_mac_addr_t value;
+};
+
+struct of_bsn_tlv_actor_port_priority: of_bsn_tlv {
+    uint16_t type == 42;
+    uint16_t length;
+    uint16_t value;
+};
+
+struct of_bsn_tlv_actor_port_num: of_bsn_tlv {
+    uint16_t type == 43;
+    uint16_t length;
+    uint16_t value;
+};
+
+struct of_bsn_tlv_actor_key: of_bsn_tlv {
+    uint16_t type == 44;
+    uint16_t length;
+    uint16_t value;
+};
+
+struct of_bsn_tlv_convergence_status: of_bsn_tlv {
+    uint16_t type == 45;
+    uint16_t length;
+    uint8_t value;
+};
+
+struct of_bsn_tlv_partner_system_priority: of_bsn_tlv {
+    uint16_t type == 47;
+    uint16_t length;
+    uint16_t value;
+};
+
+struct of_bsn_tlv_partner_system_mac: of_bsn_tlv {
+    uint16_t type == 48;
+    uint16_t length;
+    of_mac_addr_t value;
+};
+
+struct of_bsn_tlv_partner_port_priority: of_bsn_tlv {
+    uint16_t type == 49;
+    uint16_t length;
+    uint16_t value;
+};
+
+struct of_bsn_tlv_partner_port_num: of_bsn_tlv {
+    uint16_t type == 50;
+    uint16_t length;
+    uint16_t value;
+};
+
+struct of_bsn_tlv_partner_key: of_bsn_tlv {
+    uint16_t type == 51;
+    uint16_t length;
+    uint16_t value;
+};