Merge into master from pull request #341:
Merge of14 branch into master (https://github.com/floodlight/loxigen/pull/341)
diff --git a/Makefile b/Makefile
index 018db0b..5f3d8e5 100644
--- a/Makefile
+++ b/Makefile
@@ -131,6 +131,7 @@
 	PYTHONPATH=${LOXI_OUTPUT_DIR}/pyloxi:. python py_gen/tests/of11.py
 	PYTHONPATH=${LOXI_OUTPUT_DIR}/pyloxi:. python py_gen/tests/of12.py
 	PYTHONPATH=${LOXI_OUTPUT_DIR}/pyloxi:. python py_gen/tests/of13.py
+	PYTHONPATH=${LOXI_OUTPUT_DIR}/pyloxi:. python py_gen/tests/of14.py
 
 check-c: c
 	make -j4 -C ${LOXI_OUTPUT_DIR}/locitest
diff --git a/c_gen/build_of_g.py b/c_gen/build_of_g.py
index 047b254..ff2dfe8 100755
--- a/c_gen/build_of_g.py
+++ b/c_gen/build_of_g.py
@@ -119,135 +119,6 @@
         if not m_name in of_g.ordered_members[cls]:
             of_g.ordered_members[cls].append(m_name)
 
-def update_offset(cls, wire_version, name, offset, m_type):
-    """
-    Update (and return) the offset based on type.
-    @param cls The parent class
-    @param wire_version The wire version being processed
-    @param name The name of the data member
-    @param offset The current offset
-    @param m_type The type declaration being processed
-    @returns A pair (next_offset, len_update)  next_offset is the new offset
-    of the next object or -1 if this is a var-length object.  len_update
-    is the increment that should be added to the length.  Note that (for
-    of_match_v3) it is variable length, but it adds 8 bytes to the fixed
-    length of the object
-    If offset is already -1, do not update
-    Otherwise map to base type and count and update (if possible)
-    """
-    if offset < 0:    # Don't update offset once set to -1
-        return offset, 0
-
-    count, base_type = loxi_utils.type_dec_to_count_base(m_type)
-
-    len_update = 0
-    if base_type in of_g.of_mixed_types:
-        base_type = of_g.of_mixed_types[base_type][wire_version]
-
-    base_class = base_type[:-2]
-    if (base_class, wire_version) in of_g.is_fixed_length:
-        bytes = of_g.base_length[(base_class, wire_version)]
-    else:
-        if base_type == "of_match_v3_t":
-            # This is a special case: it has non-zero min length
-            # but is variable length
-            bytes = -1
-            len_update = 8
-        elif base_type == "of_oxm_header_t":
-            # This is a special case: it has non-zero min length
-            # but is variable length
-            bytes = -1
-            len_update = 4
-        elif base_type == "of_bsn_vport_header_t":
-            # This is a special case: it has non-zero min length
-            # but is variable length
-            bytes = -1
-            len_update = 4
-        elif base_type in of_g.of_base_types:
-            bytes = of_g.of_base_types[base_type]["bytes"]
-        else:
-            print "UNKNOWN TYPE for %s %s: %s" % (cls, name, base_type)
-            log("UNKNOWN TYPE for %s %s: %s" % (cls, name, base_type))
-            bytes = -1
-
-    # If bytes
-    if bytes > 0:
-        len_update = count * bytes
-
-    if bytes == -1:
-        return -1, len_update
-
-    return offset + (count * bytes), len_update
-
-def calculate_offsets_and_lengths(ordered_classes, classes, wire_version):
-    """
-    Generate the offsets for fixed offset class members
-    Also calculate the class_sizes when possible.
-
-    @param classes The classes to process
-    @param wire_version The wire version for this set of classes
-
-    Updates global variables
-    """
-
-    lists = set()
-
-    # Generate offsets
-    for cls in ordered_classes:
-        fixed_offset = 0 # The last "good" offset seen
-        offset = 0
-        last_offset = 0
-        last_name = "-"
-        for member in classes[cls]:
-            m_type = member["m_type"]
-            name = member["name"]
-            if last_offset == -1:
-                if name == "pad":
-                    log("Skipping pad for special offset for %s" % cls)
-                else:
-                    log("SPECIAL OFS: Member %s (prev %s), class %s ver %d" %
-                          (name, last_name, cls, wire_version))
-                    if (((cls, name) in of_g.special_offsets) and
-                        (of_g.special_offsets[(cls, name)] != last_name)):
-                        debug("ERROR: special offset prev name changed")
-                        debug("  cls %s. name %s. version %d. was %s. now %s" %
-                              cls, name, wire_version,
-                              of_g.special_offsets[(cls, name)], last_name)
-                        sys.exit(1)
-                    of_g.special_offsets[(cls, name)] = last_name
-
-            member["offset"] = offset
-            if m_type.find("list(") == 0:
-                (list_name, base_type) = loxi_utils.list_name_extract(m_type)
-                lists.add(list_name)
-                member["m_type"] = list_name + "_t"
-                offset = -1
-            elif m_type.find("struct") == 0:
-                debug("ERROR found struct: %s.%s " % (cls, name))
-                sys.exit(1)
-            elif m_type == "octets":
-                log("offset gen skipping octets: %s.%s " % (cls, name))
-                offset = -1
-            else:
-                offset, len_update = update_offset(cls, wire_version, name,
-                                                  offset, m_type)
-                if offset != -1:
-                    fixed_offset = offset
-                else:
-                    fixed_offset += len_update
-                    log("offset is -1 for %s.%s version %d " %
-                        (cls, name, wire_version))
-            last_offset = offset
-            last_name = name
-        of_g.base_length[(cls, wire_version)] = fixed_offset
-        if (offset != -1):
-            of_g.is_fixed_length.add((cls, wire_version))
-
-    for list_type in lists:
-        classes[list_type] = []
-        of_g.ordered_classes[wire_version].append(list_type)
-        of_g.base_length[(list_type, wire_version)] = 0
-
 def order_and_assign_object_ids():
     """
     Order all classes and assign object ids to all classes.
@@ -329,11 +200,7 @@
             pad_count = 0
             for m in ofclass.members:
                 if type(m) == OFPadMember:
-                    m_name = 'pad%d' % pad_count
-                    if m_name == 'pad0': m_name = 'pad'
-                    legacy_members.append(dict(m_type='uint8_t[%d]' % m.length,
-                                               name=m_name))
-                    pad_count += 1
+                    continue
                 else:
                     # HACK the C backend does not yet support of_oxm_t
                     if m.oftype == 'of_oxm_t':
@@ -341,15 +208,28 @@
                     # HACK the C backend does not yet support of_bsn_vport_t
                     elif m.oftype == 'of_bsn_vport_t':
                         m_type = 'of_bsn_vport_header_t'
+                    elif m.oftype.find("list(") == 0:
+                        (list_name, base_type) = loxi_utils.list_name_extract(m.oftype)
+                        m_type = list_name + "_t"
                     else:
                         enum = find(lambda e: e.name == m.oftype, protocol.enums)
                         if enum and "wire_type" in enum.params:
                             m_type = enum.params["wire_type"]
                         else:
                             m_type = m.oftype
-                    legacy_members.append(dict(m_type=m_type, name=m.name))
+
+                    if m.offset is None:
+                        m_offset = -1
+                    else:
+                        m_offset = m.offset
+
+                    legacy_members.append(dict(m_type=m_type, name=m.name, offset=m_offset))
             versions[version_name]['classes'][ofclass.name] = legacy_members
 
+            of_g.base_length[(ofclass.name, version.wire_version)] = ofclass.base_length
+            if ofclass.is_fixed_length:
+                of_g.is_fixed_length.add((ofclass.name, version.wire_version))
+
         for enum in protocol.enums:
             for entry in enum.entries:
                 identifiers.add_identifier(
@@ -377,13 +257,42 @@
                 new_cls = cls + '_header'
                 of_g.ordered_classes[wire_version].append(new_cls)
                 classes[new_cls] = classes[cls]
+                of_g.base_length[(new_cls, wire_version)] = of_g.base_length[(cls, wire_version)]
+                if (cls, wire_version) in of_g.is_fixed_length:
+                    of_g.is_fixed_length.add((cls, wire_version))
 
-    for wire_version in of_g.wire_ver_map.keys():
-        version_name = of_g.of_version_wire2name[wire_version]
-        calculate_offsets_and_lengths(
-            of_g.ordered_classes[wire_version],
-            versions[version_name]['classes'],
-            wire_version)
+    # Create lists
+    for version, protocol in loxi_globals.ir.items():
+        lists = set()
+        classes = versions[of_g.of_version_wire2name[version.wire_version]]['classes']
+
+        for ofclass in protocol.classes:
+            for m in ofclass.members:
+                if isinstance(m, OFDataMember) and m.oftype.find("list(") == 0:
+                    (list_name, base_type) = loxi_utils.list_name_extract(m.oftype)
+                    lists.add(list_name)
+
+        for list_type in lists:
+            classes[list_type] = []
+            of_g.ordered_classes[version.wire_version].append(list_type)
+            of_g.base_length[(list_type, version.wire_version)] = 0
+
+    # Find special offsets
+    # These are defined as members (except padding) that don't have a fixed
+    # offset. The special_offsets map stores the name of the previous member.
+    for version, protocol in loxi_globals.ir.items():
+        for ofclass in protocol.classes:
+            prev_member = None
+            for m in ofclass.members:
+                if isinstance(m, OFPadMember):
+                    continue
+                if m.offset == None:
+                    old = of_g.special_offsets.get((ofclass.name, m.name))
+                    if old and old != prev_member.name:
+                        raise Exception("Error: special offset changed: version=%s cls=%s member=%s old=%s new=%s" %
+                                        (version, ofclass.name, m.name, old, prev_member.name))
+                    of_g.special_offsets[(ofclass.name, m.name)] = prev_member.name
+                prev_member = m
 
 def unify_input():
     """
diff --git a/c_gen/c_code_gen.py b/c_gen/c_code_gen.py
index 3229403..b708c65 100644
--- a/c_gen/c_code_gen.py
+++ b/c_gen/c_code_gen.py
@@ -403,7 +403,6 @@
 int of_object_wire_init(of_object_t *obj, of_object_id_t base_object_id, int max_len);
 
 extern const int *const of_object_fixed_len[OF_VERSION_ARRAY_MAX];
-extern const int *const of_object_extra_len[OF_VERSION_ARRAY_MAX];
 """)
     c_type_maps.gen_type_data_header(out)
     c_match.gen_declarations(out)
@@ -530,7 +529,7 @@
 /**
  * Check if a version is supported
  */
-#define OF_VERSION_OKAY(v) ((v) >= OF_VERSION_1_0 && (v) <= OF_VERSION_1_3)
+#define OF_VERSION_OKAY(v) ((v) >= OF_VERSION_1_0 && (v) <= OF_VERSION_1_4)
 
 """)
     gen_version_enum(out)
@@ -725,24 +724,6 @@
 #define U64_HTON(val) U64_NTOH(val)
 #define IPV6_HTON(dst, src) /* NOTE different syntax; currently no-op */
 #endif
-
-/****************************************************************
- *
- * The following are internal definitions used by the automatically
- * generated code.  Users should not reference these definitions
- * as they may change between versions of this code
- *
- ****************************************************************/
-
-#define OF_MESSAGE_IN_MATCH_POINTER(obj)                            \\
-    (WIRE_BUF_POINTER(&((obj)->wire_buffer), OF_MESSAGE_IN_MATCH_OFFSET))
-#define OF_MESSAGE_IN_MATCH_LEN(ptr) BUF_U16_GET(&ptr[2])
-#define OF_MESSAGE_IN_DATA_OFFSET(obj) \\
-    (FIXED_LEN + OF_MESSAGE_IN_MATCH_LEN(OF_MESSAGE_IN_MATCH_POINTER(obj)) + 2)
-
-#define OF_MESSAGE_OUT_DATA_OFFSET(obj) \\
-    (FIXED_LEN + of_message_out_actions_len_get(obj))
-
 """)
 
 def external_h_top_matter(out, name):
@@ -1618,7 +1599,7 @@
         MEMSET(obj, 0, sizeof(*obj));
     }
     if (bytes < 0) {
-        bytes = of_object_fixed_len[version][%(enum)s] + of_object_extra_len[version][%(enum)s];
+        bytes = of_object_fixed_len[version][%(enum)s];
     }
     obj->version = version;
     obj->length = bytes;
@@ -1637,64 +1618,6 @@
 
 """)
 
-## @fixme This should also be updated once there is a map from
-# class instance to wire length/type accessors
-def gen_wire_push_fn(cls, out):
-    """
-    Generate the calls to push values into the wire buffer
-    """
-    if type_maps.class_is_virtual(cls):
-        print "Push fn gen called for virtual class " + cls
-        return
-
-    out.write("""
-/**
- * Helper function to push values into the wire buffer
- */
-static inline int
-%(cls)s_push_wire_values(%(cls)s_t *obj)
-{
-""" % dict(cls=cls))
-
-    import loxi_globals
-    uclass = loxi_globals.unified.class_by_name(cls)
-    if uclass and not uclass.virtual and uclass.has_type_members:
-        out.write("""
-    %(cls)s_push_wire_types(obj);
-""" % dict(cls=cls))
-
-    if loxi_utils.class_is_message(cls):
-        out.write("""
-    /* Message obj; set length */
-    of_message_t msg;
-
-    if ((msg = OF_OBJECT_TO_MESSAGE(obj)) != NULL) {
-        of_message_length_set(msg, obj->length);
-    }
-""" % dict(name = enum_name(cls)))
-
-    else: # Not a message
-        if loxi_utils.class_is_tlv16(cls):
-            out.write("""
-    /* TLV obj; set length */
-    of_tlv16_wire_length_set((of_object_t *)obj, obj->length);
-""" % dict(enum=enum_name(cls)))
-
-        if loxi_utils.class_is_u16_len(cls) or cls == "of_packet_queue":
-            out.write("""
-    of_object_wire_length_set((of_object_t *)obj, obj->length);
-""")
-
-        if cls == "of_meter_stats":
-            out.write("""
-    of_meter_stats_wire_length_set((of_object_t *)obj, obj->length);
-""" % dict(enum=enum_name(cls)))
-
-    out.write("""
-    return OF_ERROR_NONE;
-}
-""")
-
 def gen_new_fn_body(cls, out):
     """
     Generate function body for new function
@@ -1708,9 +1631,6 @@
  */
 """ % dict(cls=cls))
 
-    if not type_maps.class_is_virtual(cls):
-        gen_wire_push_fn(cls, out)
-
     uclass = loxi_globals.unified.class_by_name(cls)
     is_fixed_length = uclass and uclass.is_fixed_length
     max_length = is_fixed_length and "bytes" or "OF_WIRE_BUFFER_MAX_LENGTH"
@@ -1734,7 +1654,7 @@
     %(cls)s_t *obj;
     int bytes;
 
-    bytes = of_object_fixed_len[version][%(enum)s] + of_object_extra_len[version][%(enum)s];
+    bytes = of_object_fixed_len[version][%(enum)s];
 
     if ((obj = (%(cls)s_t *)of_object_new(%(max_length)s)) == NULL) {
         return NULL;
@@ -1743,20 +1663,25 @@
     %(cls)s_init(obj, version, bytes, 0);
 """ % dict(cls=cls, enum=enum_name(cls), max_length=max_length))
     if not type_maps.class_is_virtual(cls):
-        out.write("""
-    if (%(cls)s_push_wire_values(obj) < 0) {
-        FREE(obj);
-        return NULL;
-    }
-""" % dict(cls=cls))
+        from codegen import class_metadata_dict
+        metadata = class_metadata_dict[cls]
+
+        if metadata.wire_type_set != 'NULL':
+            out.write("""\
+    %s(obj);
+""" % metadata.wire_type_set)
+
+        if metadata.wire_length_set != 'NULL':
+            out.write("""\
+    %s(obj, obj->length);
+""" % metadata.wire_length_set)
 
     match_offset = v3_match_offset_get(cls)
     if match_offset >= 0:
         # Init length field for match object
         out.write("""
     /* Initialize match TLV for 1.2 */
-    /* FIXME: Check 1.3 below */
-    if ((version == OF_VERSION_1_2) || (version == OF_VERSION_1_3)) {
+    if ((version >= OF_VERSION_1_2)) {
         of_object_u16_set((of_object_t *)obj, %(match_offset)d + 2, 4);
     }
 """ % dict(match_offset=match_offset))
diff --git a/c_gen/c_dump_gen.py b/c_gen/c_dump_gen.py
index 2f41c35..4e7db7a 100644
--- a/c_gen/c_dump_gen.py
+++ b/c_gen/c_dump_gen.py
@@ -242,20 +242,20 @@
         out.write("};\n\n")
 
     out.write("""
-static const loci_obj_dump_f *const dump_funs[5] = {
-    NULL,
-    dump_funs_v1,
-    dump_funs_v2,
-    dump_funs_v3,
-    dump_funs_v4
+static const loci_obj_dump_f *const dump_funs[] = {
+""")
+
+    for version in of_g.of_version_range:
+        out.write("    [%(v)d] = dump_funs_v%(v)d,\n" % dict(v=version))
+
+    out.write("""\
 };
 
 int
 of_object_dump(loci_writer_f writer, void* cookie, of_object_t *obj)
 {
     if ((obj->object_id > 0) && (obj->object_id < OF_OBJECT_COUNT)) {
-        if (((obj)->version > 0) && ((obj)->version <= OF_VERSION_1_3)) {
-            /* @fixme VERSION */
+        if (OF_VERSION_OKAY(obj->version)) {
             return dump_funs[obj->version][obj->object_id](writer, cookie, (of_object_t *)obj);
         } else {
             return writer(cookie, "Bad version %d\\n", obj->version);
diff --git a/c_gen/c_match.py b/c_gen/c_match.py
index 3afc99c..e5a41ef 100644
--- a/c_gen/c_match.py
+++ b/c_gen/c_match.py
@@ -104,6 +104,13 @@
 #define of_match_v4_to_match of_match_v3_to_match
 #define of_match_to_wire_match_v4 of_match_to_wire_match_v3
 #define of_match_v4_delete of_match_v3_delete
+
+#define of_match_v5_t of_match_v3_t
+#define of_match_v5_init of_match_v3_init
+#define of_match_v5_new of_match_v3_new
+#define of_match_v5_to_match of_match_v3_to_match
+#define of_match_to_wire_match_v5 of_match_to_wire_match_v3
+#define of_match_v5_delete of_match_v3_delete
 """)
 
 def gen_match_macros(out):
diff --git a/c_gen/c_show_gen.py b/c_gen/c_show_gen.py
index f4d7945..9a13b43 100644
--- a/c_gen/c_show_gen.py
+++ b/c_gen/c_show_gen.py
@@ -311,20 +311,20 @@
         out.write("};\n\n")
 
     out.write("""
-static const loci_obj_show_f *const show_funs[5] = {
-    NULL,
-    show_funs_v1,
-    show_funs_v2,
-    show_funs_v3,
-    show_funs_v4
+static const loci_obj_show_f *const show_funs[] = {
+""")
+
+    for version in of_g.of_version_range:
+        out.write("    [%(v)d] = show_funs_v%(v)d,\n" % dict(v=version))
+
+    out.write("""\
 };
 
 int
 of_object_show(loci_writer_f writer, void* cookie, of_object_t *obj)
 {
     if ((obj->object_id > 0) && (obj->object_id < OF_OBJECT_COUNT)) {
-        if (((obj)->version > 0) && ((obj)->version <= OF_VERSION_1_2)) {
-            /* @fixme VERSION */
+        if (OF_VERSION_OKAY(obj->version)) {
             return show_funs[obj->version][obj->object_id](writer, cookie, (of_object_t *)obj);
         } else {
             return writer(cookie, "Bad version %d\\n", obj->version);
diff --git a/c_gen/c_test_gen.py b/c_gen/c_test_gen.py
index 41fe122..db7ca28 100644
--- a/c_gen/c_test_gen.py
+++ b/c_gen/c_test_gen.py
@@ -101,6 +101,7 @@
         of_match_t="match",
         of_oxm_header_t="oxm",
         of_bsn_vport_header_t="bsn_vport",
+        of_table_desc_t="table_desc",
         # BSN extensions
         of_bsn_vport_q_in_q_t="vport",
         of_bitmap_128_t="bitmap_128",
@@ -541,7 +542,7 @@
     """
 
     members, member_types = scalar_member_types_get(cls, version)
-    length = of_g.base_length[(cls, version)] + of_g.extra_length.get((cls, version), 0)
+    length = of_g.base_length[(cls, version)]
     v_name = loxi_utils.version_to_name(version)
 
     out.write("""
@@ -773,13 +774,15 @@
     out.write("""
     %(base_type)s_t elt;
     int cur_len = 0;
+    (void) elt;
+    (void) cur_len;
 """ % dict(cls=cls, base_type=base_type))
 
     sub_classes =  type_maps.sub_class_map(base_type, version)
     sub_classes = [(instance, subcls) for (instance, subcls) in sub_classes if not type_maps.class_is_virtual(subcls)]
     v_name = loxi_utils.version_to_name(version)
 
-    if len(sub_classes) == 0:
+    if not type_maps.class_is_virtual(base_type):
         out.write("    /* No subclasses for %s */\n"% base_type)
         out.write("    %s_t *elt_p;\n" % base_type)
         out.write("\n    elt_p = &elt;\n")
@@ -791,7 +794,7 @@
         for instance, subcls in sub_classes:
             out.write("    %s = &elt.%s;\n" % (instance, instance))
 
-    if len(sub_classes) == 0: # No inheritance case
+    if not type_maps.class_is_virtual(base_type): # No inheritance case
         setup_instance(out, cls, base_type, "elt_p", v_name, version)
     else:
         for instance, subcls in sub_classes:
@@ -819,13 +822,14 @@
     base_type = loxi_utils.list_to_entry_type(cls)
     out.write("""
     %(base_type)s_t elt;
+    (void) elt;
 """ % dict(cls=cls, base_type=base_type))
 
     sub_classes =  type_maps.sub_class_map(base_type, version)
     sub_classes = [(instance, subcls) for (instance, subcls) in sub_classes if not type_maps.class_is_virtual(subcls)]
     v_name = loxi_utils.version_to_name(version)
 
-    if len(sub_classes) == 0:
+    if not type_maps.class_is_virtual(base_type):
         out.write("    /* No subclasses for %s */\n"% base_type)
         out.write("    %s_t *elt_p;\n" % base_type)
         out.write("\n    elt_p = &elt;\n")
@@ -837,8 +841,10 @@
         for instance, subcls in sub_classes:
             out.write("    %s = &elt.%s;\n" % (instance, instance))
 
-    out.write("    TEST_OK(%(cls)s_first(list, &elt));\n" % dict(cls=cls))
-    if len(sub_classes) == 0: # No inheritance case
+    if not type_maps.class_is_virtual(base_type) or sub_classes:
+        out.write("    TEST_OK(%(cls)s_first(list, &elt));\n" % dict(cls=cls))
+
+    if not type_maps.class_is_virtual(base_type): # No inheritance case
         check_instance(out, cls, base_type, "elt_p", v_name, version, True)
     else:
         count = 0
@@ -963,10 +969,12 @@
 static int
 test_match_1(void)
 {
-    of_match_v1_t *m_v1;
-    of_match_v2_t *m_v2;
-    of_match_v3_t *m_v3;
-    of_match_v4_t *m_v4;
+""")
+
+    for version in of_g.of_version_range:
+        out.write("    of_match_v%(v)d_t *m_v%(v)d;\n" % dict(v=version))
+
+    out.write("""\
     of_match_t match;
     int value = 1;
     int idx;
@@ -1001,10 +1009,12 @@
 static int
 test_match_2(void)
 {
-    of_match_v1_t *m_v1;
-    of_match_v2_t *m_v2;
-    of_match_v3_t *m_v3;
-    of_match_v3_t *m_v4;
+""")
+
+    for version in of_g.of_version_range:
+        out.write("    of_match_v%(v)d_t *m_v%(v)d;\n" % dict(v=version))
+
+    out.write("""\
     of_match_t match1;
     of_match_t match2;
     int value = 1;
@@ -1088,7 +1098,7 @@
                 continue
             if type_maps.class_is_virtual(cls):
                 continue
-            bytes = of_g.base_length[(cls, version)] + of_g.extra_length.get((cls, version), 0)
+            bytes = of_g.base_length[(cls, version)]
             out.write("""
 static int
 test_%(cls)s_create_%(v_name)s(void)
@@ -1173,13 +1183,15 @@
     out.write("""
     %(base_type)s_t elt;
     int cur_len = 0;
+    (void) elt;
+    (void) cur_len;
 """ % dict(cls=cls, base_type=base_type))
 
     sub_classes =  type_maps.sub_class_map(base_type, version)
     sub_classes = [(instance, subcls) for (instance, subcls) in sub_classes if not type_maps.class_is_virtual(subcls)]
     v_name = loxi_utils.version_to_name(version)
 
-    if len(sub_classes) == 0:
+    if not type_maps.class_is_virtual(base_type):
         out.write("    /* No subclasses for %s */\n"% base_type)
         out.write("    %s_t *elt_p;\n" % base_type)
         out.write("\n    elt_p = &elt;\n")
@@ -1191,7 +1203,7 @@
         for instance, subcls in sub_classes:
             out.write("    %s = &elt.%s;\n" % (instance, instance))
 
-    if len(sub_classes) == 0: # No inheritance case
+    if not type_maps.class_is_virtual(base_type): # No inheritance case
         setup_instance(out, cls, base_type, "elt_p", v_name, version)
     else:
         for instance, subcls in sub_classes:
@@ -1225,7 +1237,7 @@
     sub_classes = [(instance, subcls) for (instance, subcls) in sub_classes if not type_maps.class_is_virtual(subcls)]
     v_name = loxi_utils.version_to_name(version)
 
-    if len(sub_classes) == 0:
+    if not type_maps.class_is_virtual(base_type):
         entry_count = 2
         out.write("    /* No subclasses for %s */\n"% base_type)
         out.write("    %s_t *elt_p;\n" % base_type)
@@ -1239,8 +1251,10 @@
         for instance, subcls in sub_classes:
             out.write("    %s = &elt.%s;\n" % (instance, instance))
 
-    out.write("    TEST_OK(%(cls)s_first(list, &elt));\n" % dict(cls=cls))
-    if len(sub_classes) == 0: # No inheritance case
+    if not type_maps.class_is_virtual(base_type) or sub_classes:
+        out.write("    TEST_OK(%(cls)s_first(list, &elt));\n" % dict(cls=cls))
+
+    if not type_maps.class_is_virtual(base_type): # No inheritance case
         check_instance(out, cls, base_type, "elt_p", v_name,
                        version, True)
     else:
@@ -1518,7 +1532,7 @@
     """
 
     members, member_types = scalar_member_types_get(cls, version)
-    length = of_g.base_length[(cls, version)] + of_g.extra_length.get((cls, version), 0)
+    length = of_g.base_length[(cls, version)]
     v_name = loxi_utils.version_to_name(version)
 
     out.write("""
diff --git a/c_gen/c_type_maps.py b/c_gen/c_type_maps.py
index 6c45d4d..36ad5f6 100644
--- a/c_gen/c_type_maps.py
+++ b/c_gen/c_type_maps.py
@@ -64,6 +64,18 @@
 extern void of_meter_stats_wire_length_get(of_object_t *obj, int *bytes);
 extern void of_meter_stats_wire_length_set(of_object_t *obj, int bytes);
 
+extern void of_port_desc_wire_length_get(of_object_t *obj, int *bytes);
+extern void of_port_desc_wire_length_set(of_object_t *obj, int bytes);
+
+extern void of_port_stats_entry_wire_length_get(of_object_t *obj, int *bytes);
+extern void of_port_stats_entry_wire_length_set(of_object_t *obj, int bytes);
+
+extern void of_queue_stats_entry_wire_length_get(of_object_t *obj, int *bytes);
+extern void of_queue_stats_entry_wire_length_set(of_object_t *obj, int bytes);
+
+extern void of_queue_desc_wire_length_get(of_object_t *obj, int *bytes);
+extern void of_queue_desc_wire_length_set(of_object_t *obj, int bytes);
+
 """)
 
 
@@ -106,44 +118,3 @@
     out.write("""
 };
 """)
-
-
-def gen_extra_length_array(out):
-    """
-    Generate an array giving the extra lengths of all objects/versions
-    @param out The file handle to which to write
-    """
-    out.write("""
-/**
- * An array with the number of bytes in the extra length part
- * of each OF object
- */
-""")
-
-    for version in of_g.of_version_range:
-        out.write("""
-static const int\nof_object_extra_len_v%d[OF_OBJECT_COUNT] = {
-    -1,   /* of_object is not instantiable */
-""" % version)
-        for i, cls in enumerate(of_g.all_class_order):
-            comma = ","
-            if i == len(of_g.all_class_order) - 1:
-                comma = ""
-            val = "-1" + comma
-            if (cls, version) in of_g.base_length:
-                val = str(of_g.extra_length.get((cls, version), 0)) + comma
-            out.write("    %-5s /* %d: %s */\n" % (val, i + 1, cls))
-        out.write("};\n")
-
-    out.write("""
-/**
- * Unified map of extra length part of each object
- */
-const int *const of_object_extra_len[OF_VERSION_ARRAY_MAX] = {
-    NULL,
-""")
-    for version in of_g.of_version_range:
-        out.write("    of_object_extra_len_v%d,\n" % version)
-    out.write("""
-};
-""")
diff --git a/c_gen/codegen.py b/c_gen/codegen.py
index c1b00ad..f480453 100644
--- a/c_gen/codegen.py
+++ b/c_gen/codegen.py
@@ -186,7 +186,6 @@
     # Collect legacy code
     tmp = StringIO()
     c_type_maps.gen_length_array(tmp)
-    c_type_maps.gen_extra_length_array(tmp)
 
     with template_utils.open_output(install_dir, "loci/src/of_type_maps.c") as out:
         util.render_template(out, "of_type_maps.c", legacy_code=tmp.getvalue())
@@ -197,6 +196,14 @@
 class_metadata = []
 class_metadata_dict = {}
 
+# These classes have handwritten C code to get/set their length fields
+# See templates/of_type_maps.c
+special_length_classes = set([
+    'of_packet_queue', 'of_meter_stats', 'of_port_desc',
+    'of_port_stats_entry', 'of_queue_stats_entry',
+    'of_queue_desc',
+])
+
 def build_class_metadata():
     for uclass in loxi_globals.unified.classes:
         wire_length_get = 'NULL'
@@ -216,14 +223,9 @@
             wire_length_set = 'of_object_message_wire_length_set'
         elif uclass.is_oxm:
             wire_length_get = 'of_oxm_wire_length_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 special_length_classes:
+            wire_length_get = '%s_wire_length_get' % uclass.name
+            wire_length_set = '%s_wire_length_set' % uclass.name
         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'
diff --git a/c_gen/match.py b/c_gen/match.py
index 7b7dcce..d6b43fa 100644
--- a/c_gen/match.py
+++ b/c_gen/match.py
@@ -131,6 +131,7 @@
     2: of_v2_keys,
     3: [],
     4: [],
+    5: [],
 }
 
 # Complete list of match keys, sorted by the standard order
diff --git a/c_gen/of_g_legacy.py b/c_gen/of_g_legacy.py
index 2372b31..87e0680 100644
--- a/c_gen/of_g_legacy.py
+++ b/c_gen/of_g_legacy.py
@@ -111,6 +111,7 @@
         2: "uint32_t",
         3: "uint32_t",
         4: "uint32_t",
+        5: "uint32_t",
         "short_name":"port_no"
         },
     of_port_desc_t = {
@@ -118,6 +119,7 @@
         2: "of_port_desc_t",
         3: "of_port_desc_t",
         4: "of_port_desc_t",
+        5: "of_port_desc_t",
         "short_name":"port_desc"
         },
     of_bsn_vport_t = {
@@ -125,6 +127,7 @@
         2: "of_bsn_vport_t",
         3: "of_bsn_vport_t",
         4: "of_bsn_vport_t",
+        5: "of_bsn_vport_t",
         "short_name":"bsn_vport"
         },
     of_fm_cmd_t = { # Flow mod command went from u16 to u8
@@ -132,6 +135,7 @@
         2: "uint8_t",
         3: "uint8_t",
         4: "uint8_t",
+        5: "uint8_t",
         "short_name":"fm_cmd"
         },
     of_wc_bmap_t = { # Wildcard bitmap
@@ -139,6 +143,7 @@
         2: "uint32_t",
         3: "uint64_t",
         4: "uint64_t",
+        5: "uint64_t",
         "short_name":"wc_bmap"
         },
     of_match_bmap_t = { # Match bitmap
@@ -146,6 +151,7 @@
         2: "uint32_t",
         3: "uint64_t",
         4: "uint64_t",
+        5: "uint64_t",
         "short_name":"match_bmap"
         },
     of_match_t = { # Match object
@@ -153,6 +159,7 @@
         2: "of_match_v2_t",
         3: "of_match_v3_t",
         4: "of_match_v3_t",  # Currently uses same match as 1.2 (v3).
+        5: "of_match_v3_t",  # Currently uses same match as 1.2 (v3).
         "short_name":"match"
         },
 )
@@ -266,12 +273,6 @@
 ## Map from class, wire_version to size of fixed part of class
 base_length = {}
 
-## Map from class, wire_version to size of variable-offset, fixed length part of class
-extra_length = {
-    ("of_packet_in", 3): 2,
-    ("of_packet_in", 4): 2,
-}
-
 ## Boolean indication of variable length, per class, wire_version,
 is_fixed_length = set()
 
diff --git a/c_gen/templates/loci_int.h b/c_gen/templates/loci_int.h
index 1ea9eeb..eb1c923 100644
--- a/c_gen/templates/loci_int.h
+++ b/c_gen/templates/loci_int.h
@@ -30,6 +30,8 @@
 :: flow_mod = of_g.base_length[("of_flow_modify",of_g.VERSION_1_2)]
 :: packet_in = of_g.base_length[("of_packet_in",of_g.VERSION_1_2)]
 :: packet_in_1_3 = of_g.base_length[("of_packet_in",of_g.VERSION_1_3)]
+:: packet_in_1_4 = of_g.base_length[("of_packet_in",of_g.VERSION_1_4)]
+:: assert packet_in_1_3 == packet_in_1_4
 :: flow_stats = of_g.base_length[("of_flow_stats_entry", of_g.VERSION_1_2)]
 :: match1 = of_g.base_length[("of_match_v1",of_g.VERSION_1_0)]
 :: match2 = of_g.base_length[("of_match_v2",of_g.VERSION_1_1)]
@@ -239,13 +241,16 @@
  * @param obj An object of type of_packet_in_t
  *
  * Get length of preceding match object and add to fixed length
- * Applies only to version 1.2 and 1.3
- * The +2 comes from the 2 bytes of padding between the match and packet data.
+ * Applies only to version 1.2+
+ * There are 2 bytes of padding between the match and data. The
+ * _OFFSET_FOLLOWING_MATCH_V3 macro assumes the match is at the end of the
+ * fixed length, so we need to subtract 2 from the fixed length we pass and
+ * then add 2 to the resulting offset.
  */
 
 #define _PACKET_IN_DATA_OFFSET(obj) \
     (_OFFSET_FOLLOWING_MATCH_V3((obj), (obj)->version == OF_VERSION_1_2 ? \
-${packet_in} : ${packet_in_1_3}) + 2)
+(${packet_in} - 2) : (${packet_in_1_3} - 2)) + 2)
 
 /**
  * Macro to calculate variable offset of data (packet) member in packet_out
diff --git a/c_gen/templates/loci_validator.c b/c_gen/templates/loci_validator.c
index 1f98b21..c4e8b5c 100644
--- a/c_gen/templates/loci_validator.c
+++ b/c_gen/templates/loci_validator.c
@@ -199,7 +199,7 @@
         return ${validator_name(proto.class_by_name('of_header'))}(msg, len, &out_len);
 :: #endfor
     default:
-        VALIDATOR_LOG("Bad version %d", OF_VERSION_1_3);
+        VALIDATOR_LOG("Bad version %d", version);
         return -1;
     }
 }
diff --git a/c_gen/templates/of_type_maps.c b/c_gen/templates/of_type_maps.c
index 9fa59e0..3fcf024 100644
--- a/c_gen/templates/of_type_maps.c
+++ b/c_gen/templates/of_type_maps.c
@@ -305,3 +305,173 @@
     of_wire_buffer_u16_set(wbuf, 
         OF_OBJECT_ABSOLUTE_OFFSET(obj, OF_METER_STATS_LENGTH_OFFSET), bytes);
 }
+
+/**
+ * Get the wire length for a port desc object
+ * @param obj The object being referenced
+ * @param bytes Pointer to location to store length
+ *
+ * The length is only present for OF 1.4+.
+ */
+void
+of_port_desc_wire_length_get(of_object_t *obj, int *bytes)
+{
+    of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
+    uint16_t u16;
+
+    LOCI_ASSERT(wbuf != NULL);
+
+    if (obj->version >= OF_VERSION_1_4) {
+        of_wire_buffer_u16_get(wbuf, OF_OBJECT_ABSOLUTE_OFFSET(obj, 4),
+                               &u16);
+        *bytes = u16;
+    } else {
+        *bytes = OF_OBJECT_FIXED_LENGTH(obj);
+    }
+}
+
+/**
+ * Set the wire length for a port desc object
+ * @param obj The object being referenced
+ * @param bytes The length of the object
+ *
+ * The length is only present for OF 1.4+.
+ */
+
+void
+of_port_desc_wire_length_set(of_object_t *obj, int bytes)
+{
+    of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
+    LOCI_ASSERT(wbuf != NULL);
+
+    if (obj->version >= OF_VERSION_1_4) {
+        of_wire_buffer_u16_set(wbuf, OF_OBJECT_ABSOLUTE_OFFSET(obj, 4),
+                               bytes);
+    } else {
+        LOCI_ASSERT(obj->length == OF_OBJECT_FIXED_LENGTH(obj));
+    }
+}
+
+/**
+ * Get the wire length for a port stats_entry object
+ * @param obj The object being referenced
+ * @param bytes Pointer to location to store length
+ *
+ * The length is only present for OF 1.4+.
+ */
+void
+of_port_stats_entry_wire_length_get(of_object_t *obj, int *bytes)
+{
+    of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
+    uint16_t u16;
+
+    LOCI_ASSERT(wbuf != NULL);
+
+    if (obj->version >= OF_VERSION_1_4) {
+        of_wire_buffer_u16_get(wbuf, OF_OBJECT_ABSOLUTE_OFFSET(obj, 0),
+                               &u16);
+        *bytes = u16;
+    } else {
+        *bytes = OF_OBJECT_FIXED_LENGTH(obj);
+    }
+}
+
+/**
+ * Set the wire length for a port stats_entry object
+ * @param obj The object being referenced
+ * @param bytes The length of the object
+ *
+ * The length is only present for OF 1.4+.
+ */
+
+void
+of_port_stats_entry_wire_length_set(of_object_t *obj, int bytes)
+{
+    of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
+    LOCI_ASSERT(wbuf != NULL);
+
+    if (obj->version >= OF_VERSION_1_4) {
+        of_wire_buffer_u16_set(wbuf, OF_OBJECT_ABSOLUTE_OFFSET(obj, 0),
+                               bytes);
+    } else {
+        LOCI_ASSERT(obj->length == OF_OBJECT_FIXED_LENGTH(obj));
+    }
+}
+
+/**
+ * Get the wire length for a queue stats_entry object
+ * @param obj The object being referenced
+ * @param bytes Pointer to location to store length
+ *
+ * The length is only present for OF 1.4+.
+ */
+void
+of_queue_stats_entry_wire_length_get(of_object_t *obj, int *bytes)
+{
+    of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
+    uint16_t u16;
+
+    LOCI_ASSERT(wbuf != NULL);
+
+    if (obj->version >= OF_VERSION_1_4) {
+        of_wire_buffer_u16_get(wbuf, OF_OBJECT_ABSOLUTE_OFFSET(obj, 0),
+                               &u16);
+        *bytes = u16;
+    } else {
+        *bytes = OF_OBJECT_FIXED_LENGTH(obj);
+    }
+}
+
+/**
+ * Set the wire length for a queue stats_entry object
+ * @param obj The object being referenced
+ * @param bytes The length of the object
+ *
+ * The length is only present for OF 1.4+.
+ */
+
+void
+of_queue_stats_entry_wire_length_set(of_object_t *obj, int bytes)
+{
+    of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
+    LOCI_ASSERT(wbuf != NULL);
+
+    if (obj->version >= OF_VERSION_1_4) {
+        of_wire_buffer_u16_set(wbuf, OF_OBJECT_ABSOLUTE_OFFSET(obj, 0),
+                               bytes);
+    } else {
+        LOCI_ASSERT(obj->length == OF_OBJECT_FIXED_LENGTH(obj));
+    }
+}
+
+/**
+ * Get the wire length for a queue_desc object
+ * @param obj The object being referenced
+ * @param bytes Pointer to location to store length
+ */
+void
+of_queue_desc_wire_length_get(of_object_t *obj, int *bytes)
+{
+    of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
+    uint16_t u16;
+
+    LOCI_ASSERT(wbuf != NULL);
+
+    of_wire_buffer_u16_get(wbuf, OF_OBJECT_ABSOLUTE_OFFSET(obj, 8), &u16);
+    *bytes = u16;
+}
+
+/**
+ * Set the wire length for a queue_desc object
+ * @param obj The object being referenced
+ * @param bytes The length of the object
+ */
+
+void
+of_queue_desc_wire_length_set(of_object_t *obj, int bytes)
+{
+    of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
+    LOCI_ASSERT(wbuf != NULL);
+
+    of_wire_buffer_u16_set(wbuf, OF_OBJECT_ABSOLUTE_OFFSET(obj, 8), bytes);
+}
diff --git a/c_gen/templates/of_wire_buf.h b/c_gen/templates/of_wire_buf.h
index 0922a69..3d38f53 100644
--- a/c_gen/templates/of_wire_buf.h
+++ b/c_gen/templates/of_wire_buf.h
@@ -469,14 +469,10 @@
         of_wire_buffer_u16_get(wbuf, offset, &v16);
         *value = v16;
         break;
-    case OF_VERSION_1_1:
-    case OF_VERSION_1_2:
-    case OF_VERSION_1_3:
+    default:
         of_wire_buffer_u32_get(wbuf, offset, &v32);
         *value = v32;
         break;
-    default:
-        LOCI_ASSERT(0);
     }
 }
 
@@ -498,13 +494,9 @@
     case OF_VERSION_1_0:
         of_wire_buffer_u16_set(wbuf, offset, (uint16_t)value);
         break;
-    case OF_VERSION_1_1:
-    case OF_VERSION_1_2:
-    case OF_VERSION_1_3:
+    default:
         of_wire_buffer_u32_set(wbuf, offset, (uint32_t)value);
         break;
-    default:
-        LOCI_ASSERT(0);
     }
 }
 
@@ -527,14 +519,10 @@
         of_wire_buffer_u16_get(wbuf, offset, &v16);
         *value = v16;
         break;
-    case OF_VERSION_1_1:
-    case OF_VERSION_1_2:
-    case OF_VERSION_1_3:
+    default:
         of_wire_buffer_u8_get(wbuf, offset, &v8);
         *value = v8;
         break;
-    default:
-        LOCI_ASSERT(0);
     }
 }
 
@@ -553,13 +541,9 @@
     case OF_VERSION_1_0:
         of_wire_buffer_u16_set(wbuf, offset, (uint16_t)value);
         break;
-    case OF_VERSION_1_1:
-    case OF_VERSION_1_2:
-    case OF_VERSION_1_3:
+    default:
         of_wire_buffer_u8_set(wbuf, offset, (uint8_t)value);
         break;
-    default:
-        LOCI_ASSERT(0);
     }
 }
 
@@ -583,13 +567,10 @@
         of_wire_buffer_u32_get(wbuf, offset, &v32);
         *value = v32;
         break;
-    case OF_VERSION_1_2:
-    case OF_VERSION_1_3:
+    default:
         of_wire_buffer_u64_get(wbuf, offset, &v64);
         *value = v64;
         break;
-    default:
-        LOCI_ASSERT(0);
     }
 }
 
@@ -609,12 +590,9 @@
     case OF_VERSION_1_1:
         of_wire_buffer_u32_set(wbuf, offset, (uint32_t)value);
         break;
-    case OF_VERSION_1_2:
-    case OF_VERSION_1_3:
+    default:
         of_wire_buffer_u64_set(wbuf, offset, (uint64_t)value);
         break;
-    default:
-        LOCI_ASSERT(0);
     }
 }
 
diff --git a/cmdline.py b/cmdline.py
index 6395fff..de7070a 100644
--- a/cmdline.py
+++ b/cmdline.py
@@ -33,7 +33,7 @@
 # The default configuration dictionary for LOXI code generation
 options_default = {
     "lang"               : "c",
-    "version-list"       : "1.0 1.1 1.2 1.3",
+    "version-list"       : "1.0 1.1 1.2 1.3 1.4",
     "install-dir"        : "loxi_output",
 }
 
diff --git a/java_gen/java_model.py b/java_gen/java_model.py
index fc64f80..b3c3df0 100644
--- a/java_gen/java_model.py
+++ b/java_gen/java_model.py
@@ -826,7 +826,7 @@
             entry = enum.entry_by_version_value(self.msg.version, self.value)
             return "%s.%s" % ( enum.name, entry.name)
         except KeyError, e:
-            logger.debug("No enum found", e)
+            logger.debug("No enum found for type %s version %s value %s", java_type, self.msg.version, self.value)
             return self.value
 
     @property
diff --git a/java_gen/java_type.py b/java_gen/java_type.py
index 427de95..b23dcd0 100644
--- a/java_gen/java_type.py
+++ b/java_gen/java_type.py
@@ -528,6 +528,10 @@
 
 generic_t = JType("T")
 
+table_desc = JType('OFTableDesc') \
+        .op(read='OFTableDescVer$version.READER.readFrom(bb)', \
+            write='$name.writeTo(bb)')
+
 
 default_mtype_to_jtype_convert_map = {
         'uint8_t' : u8,
@@ -564,6 +568,7 @@
         'of_bitmap_128_t': port_bitmap,
         'of_checksum_128_t': u128,
         'of_bsn_vport_t': bsn_vport,
+        'of_table_desc_t': table_desc,
         }
 
 ## Map that defines exceptions from the standard loxi->java mapping scheme
diff --git a/java_gen/pre-written/pom.xml b/java_gen/pre-written/pom.xml
index 6b60fd1..58ddab3 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.4.1-SNAPSHOT</version>
+    <version>0.9.0-SNAPSHOT</version>
     <packaging>jar</packaging>
 
     <name>OpenFlowJ-Loxi</name>
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/OFVersion.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/OFVersion.java
index 6f54e5f..0c54fdc 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/OFVersion.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/OFVersion.java
@@ -1,7 +1,7 @@
 package org.projectfloodlight.openflow.protocol;
 
 public enum OFVersion {
-    OF_10(1), OF_11(2), OF_12(3), OF_13(4);
+    OF_10(1), OF_11(2), OF_12(3), OF_13(4), OF_14(5);
 
     public final int wireVersion;
 
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/ver14/ChannelUtilsVer14.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/ver14/ChannelUtilsVer14.java
new file mode 100644
index 0000000..c893cab
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/ver14/ChannelUtilsVer14.java
@@ -0,0 +1,26 @@
+package org.projectfloodlight.openflow.protocol.ver14;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.projectfloodlight.openflow.exceptions.OFParseError;
+import org.projectfloodlight.openflow.protocol.OFMatchBmap;
+import org.projectfloodlight.openflow.protocol.match.Match;
+
+/**
+ * Collection of helper functions for reading and writing into ChannelBuffers
+ *
+ * @author capveg
+ */
+
+public class ChannelUtilsVer14 {
+    public static Match readOFMatch(final ChannelBuffer bb) throws OFParseError {
+        return OFMatchV3Ver14.READER.readFrom(bb);
+    }
+
+    public static OFMatchBmap readOFMatchBmap(ChannelBuffer bb) {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    public static void writeOFMatchBmap(ChannelBuffer bb, OFMatchBmap match) {
+        throw new UnsupportedOperationException("not implemented");
+    }
+}
diff --git a/java_gen/templates/custom/OFMatchV3.Builder.java b/java_gen/templates/custom/OFMatchV3.Builder.java
new file mode 100644
index 0000000..54b35ba
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3.Builder.java
@@ -0,0 +1,106 @@
+
+    private OFOxmList.Builder oxmListBuilder;
+
+    private void initBuilder() {
+        if (oxmListBuilder != null)
+            return;
+        oxmListBuilder = new OFOxmList.Builder();
+    }
+
+    private void updateOxmList() {
+        this.oxmList = this.oxmListBuilder.build();
+        this.oxmListSet = true;
+    }
+
+    private <F extends OFValueType<F>> OFOxm<F> getOxm(MatchField<F> field) {
+//:: if has_parent:
+        return this.oxmListSet ? this.oxmList.get(field) : parentMessage.oxmList.get(field);
+//:: else:
+        return this.oxmListSet ? this.oxmList.get(field) : null;
+//:: #endif
+    }
+
+    @Override
+    public <F extends OFValueType<F>> F get(MatchField<F> field)
+            throws UnsupportedOperationException {
+        OFOxm<F> value = getOxm(field);
+        if (value == null)
+            return null;
+        return value.getValue();
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
+            throws UnsupportedOperationException {
+        OFOxm<F> value = getOxm(field);
+        if (value == null || !value.isMasked())
+            return null;
+        // TODO: If changing OXMs to extend Masked, then use it here
+        return Masked.of(value.getValue(), value.getMask());
+    }
+
+    @Override
+    public boolean supports(MatchField<?> field) {
+        return supportsField(field);
+    }
+
+    @Override
+    public boolean supportsMasked(MatchField<?> field) {
+        return supportsField(field);
+    }
+
+    @Override
+    public boolean isExact(MatchField<?> field) {
+        OFOxm<?> value = getOxm(field);
+        return (value != null && !value.isMasked());
+    }
+
+    @Override
+    public boolean isFullyWildcarded(MatchField<?> field) {
+        OFOxm<?> value = getOxm(field);
+        return (value == null);
+    }
+
+    @Override
+    public boolean isPartiallyMasked(MatchField<?> field) {
+        OFOxm<?> value = getOxm(field);
+        return (value != null && value.isMasked());
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Match.Builder setExact(
+            MatchField<F> field, F value) {
+        initBuilder();
+        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.${version.constant_version}).oxms().fromValue(value, field);
+        this.oxmListBuilder.set(oxm);
+        updateOxmList();
+        return this;
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Match.Builder setMasked(
+            MatchField<F> field, F value, F mask) {
+        initBuilder();
+        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.${version.constant_version}).oxms().fromValueAndMask(value, mask, field);
+        this.oxmListBuilder.set(oxm);
+        updateOxmList();
+        return this;
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Match.Builder setMasked(
+            MatchField<F> field, Masked<F> valueWithMask) {
+        initBuilder();
+        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.${version.constant_version}).oxms().fromMasked(valueWithMask, field);
+        this.oxmListBuilder.set(oxm);
+        updateOxmList();
+        return this;
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Match.Builder wildcard(MatchField<F> field) {
+        initBuilder();
+        this.oxmListBuilder.unset(field);
+        updateOxmList();
+        return this;
+    }
diff --git a/java_gen/templates/custom/OFMatchV3.java b/java_gen/templates/custom/OFMatchV3.java
new file mode 100644
index 0000000..799135c
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3.java
@@ -0,0 +1,112 @@
+//:: from generic_utils import OrderedSet
+//:: from java_gen.java_model import model
+    @Override
+    public <F extends OFValueType<F>> F get(MatchField<F> field)
+            throws UnsupportedOperationException {
+        if (!supports(field))
+            throw new UnsupportedOperationException("${msg.name} does not support matching on field " + field.getName());
+
+        OFOxm<F> oxm = this.oxmList.get(field);
+
+        if (oxm == null || !field.arePrerequisitesOK(this))
+            return null;
+
+        return oxm.getValue();
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
+            throws UnsupportedOperationException {
+        if (!supportsMasked(field))
+            throw new UnsupportedOperationException("${msg.name} does not support masked matching on field " + field.getName());
+
+        OFOxm<F> oxm = this.oxmList.get(field);
+
+        if (oxm == null || !field.arePrerequisitesOK(this))
+            return null;
+
+        if (oxm.getMask() == null)
+            return null;
+
+        // TODO: Make OfOxm extend Masked and just return the OXM?
+        return Masked.of(oxm.getValue(), oxm.getMask());
+    }
+
+    private static boolean supportsField(MatchField<?> field) {
+        switch (field.id) {
+            //:: for id_constant in sorted(set(id_constant for _, id_constant, _ in model.oxm_map.values())):
+            case ${id_constant}:
+            //:: #endfor
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public boolean supports(MatchField<?> field) {
+        return supportsField(field);
+    }
+
+    @Override
+    public boolean supportsMasked(MatchField<?> field) {
+        return supportsField(field);
+    }
+
+    @Override
+    public boolean isExact(MatchField<?> field) {
+        if (!supports(field))
+            throw new UnsupportedOperationException("${msg.name} does not support matching on field " + field.getName());
+
+        OFOxm<?> oxm = this.oxmList.get(field);
+
+        return oxm != null && !oxm.isMasked();
+    }
+
+    @Override
+    public boolean isFullyWildcarded(MatchField<?> field) {
+        if (!supports(field))
+            throw new UnsupportedOperationException("${msg.name} does not support matching on field " + field.getName());
+
+        OFOxm<?> oxm = this.oxmList.get(field);
+
+        return oxm == null;
+    }
+
+    @Override
+    public boolean isPartiallyMasked(MatchField<?> field) {
+        if (!supports(field))
+            throw new UnsupportedOperationException("${msg.name} does not support matching on field " + field.getName());
+
+        OFOxm<?> oxm = this.oxmList.get(field);
+
+        return oxm != null && oxm.isMasked();
+    }
+
+    private class MatchFieldIterator extends AbstractIterator<MatchField<?>> {
+        private Iterator<OFOxm<?>> oxmIterator;
+
+        MatchFieldIterator() {
+            oxmIterator = oxmList.iterator();
+        }
+
+        @Override
+        protected MatchField<?> computeNext() {
+            while(oxmIterator.hasNext()) {
+                OFOxm<?> oxm = oxmIterator.next();
+                if(oxm.getMatchField().arePrerequisitesOK(${msg.name}.this))
+                   return oxm.getMatchField();
+            }
+            endOfData();
+            return null;
+        }
+    }
+
+    @Override
+    public Iterable<MatchField<?>> getMatchFields() {
+        return new Iterable<MatchField<?>>() {
+            public Iterator<MatchField<?>> iterator() {
+                return new MatchFieldIterator();
+            }
+        };
+    }
diff --git a/java_gen/templates/custom/OFMatchV3Ver12.Builder.java b/java_gen/templates/custom/OFMatchV3Ver12.Builder.java
index 3fae367..ae2ecaa 100644
--- a/java_gen/templates/custom/OFMatchV3Ver12.Builder.java
+++ b/java_gen/templates/custom/OFMatchV3Ver12.Builder.java
@@ -1,106 +1 @@
-
-    private OFOxmList.Builder oxmListBuilder;
-
-    private synchronized void initBuilder() {
-        if (oxmListBuilder != null)
-            return;
-        oxmListBuilder = new OFOxmList.Builder();
-    }
-
-    private synchronized void updateOxmList() {
-        this.oxmList = this.oxmListBuilder.build();
-        this.oxmListSet = true;
-    }
-
-    private <F extends OFValueType<F>> OFOxm<F> getOxm(MatchField<F> field) {
-//:: if has_parent:
-        return this.oxmListSet ? this.oxmList.get(field) : parentMessage.oxmList.get(field);
-//:: else:
-        return this.oxmListSet ? this.oxmList.get(field) : null;
-//:: #endif
-    }
-
-    @Override
-    public synchronized <F extends OFValueType<F>> F get(MatchField<F> field)
-            throws UnsupportedOperationException {
-        OFOxm<F> value = getOxm(field);
-        if (value == null)
-            return null;
-        return value.getValue();
-    }
-
-    @Override
-    public synchronized <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
-            throws UnsupportedOperationException {
-        OFOxm<F> value = getOxm(field);
-        if (value == null || !value.isMasked())
-            return null;
-        // TODO: If changing OXMs to extend Masked, then use it here
-        return Masked.of(value.getValue(), value.getMask());
-    }
-
-    @Override
-    public boolean supports(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean supportsMasked(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public synchronized boolean isExact(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value != null && !value.isMasked());
-    }
-
-    @Override
-    public synchronized boolean isFullyWildcarded(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value == null);
-    }
-
-    @Override
-    public synchronized boolean isPartiallyMasked(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value != null && value.isMasked());
-    }
-
-    @Override
-    public synchronized <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);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public synchronized <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);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public synchronized <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);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public synchronized <F extends OFValueType<F>> Match.Builder wildcard(MatchField<F> field) {
-        initBuilder();
-        this.oxmListBuilder.unset(field);
-        updateOxmList();
-        return this;
-    }
+//:: include("custom/OFMatchV3.Builder.java", msg=msg, version=version, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver12.java b/java_gen/templates/custom/OFMatchV3Ver12.java
index 81092c1..225b0dc 100644
--- a/java_gen/templates/custom/OFMatchV3Ver12.java
+++ b/java_gen/templates/custom/OFMatchV3Ver12.java
@@ -1,114 +1 @@
-
-    @Override
-    public <F extends OFValueType<F>> F get(MatchField<F> field)
-            throws UnsupportedOperationException {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<F> oxm = this.oxmList.get(field);
-
-        if (oxm == null || !field.arePrerequisitesOK(this))
-            return null;
-
-        return oxm.getValue();
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
-            throws UnsupportedOperationException {
-        if (!supportsMasked(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support masked matching on field " + field.getName());
-
-        OFOxm<F> oxm = this.oxmList.get(field);
-
-        if (oxm == null || !field.arePrerequisitesOK(this))
-            return null;
-
-        if (oxm.getMask() == null)
-            return null;
-
-        // TODO: Make OfOxm extend Masked and just return the OXM?
-        return Masked.of(oxm.getValue(), oxm.getMask());
-    }
-
-    private static boolean supportsField(MatchField<?> field) {
-        switch (field.id) {
-            case IN_PORT:
-            case IN_PHY_PORT:
-            case METADATA:
-            case ETH_DST:
-            case ETH_SRC:
-            case ETH_TYPE:
-            case VLAN_VID:
-            case VLAN_PCP:
-            case IP_DSCP:
-            case IP_ECN:
-            case IP_PROTO:
-            case IPV4_SRC:
-            case IPV4_DST:
-            case TCP_SRC:
-            case TCP_DST:
-            case UDP_SRC:
-            case UDP_DST:
-            case SCTP_SRC:
-            case SCTP_DST:
-            case ICMPV4_TYPE:
-            case ICMPV4_CODE:
-            case ARP_OP:
-            case ARP_SPA:
-            case ARP_TPA:
-            case ARP_SHA:
-            case ARP_THA:
-            case IPV6_SRC:
-            case IPV6_DST:
-            case IPV6_FLABEL:
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    @Override
-    public boolean supports(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean supportsMasked(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean isExact(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm != null && !oxm.isMasked();
-    }
-
-    @Override
-    public boolean isFullyWildcarded(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm == null;
-    }
-
-    @Override
-    public boolean isPartiallyMasked(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm != null && oxm.isMasked();
-    }
-
-    @Override
-    public Iterable<MatchField<?>> getMatchFields() {
-        throw new UnsupportedOperationException();
-    }
+//:: include("custom/OFMatchV3.java", msg=msg, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver12_toString.java b/java_gen/templates/custom/OFMatchV3Ver12_toString.java
new file mode 100644
index 0000000..3b2783b
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3Ver12_toString.java
@@ -0,0 +1 @@
+//:: include("custom/OFMatch_toString.java", msg=msg, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver13.Builder.java b/java_gen/templates/custom/OFMatchV3Ver13.Builder.java
index 79cbdbc..ae2ecaa 100644
--- a/java_gen/templates/custom/OFMatchV3Ver13.Builder.java
+++ b/java_gen/templates/custom/OFMatchV3Ver13.Builder.java
@@ -1,107 +1 @@
-
-    private OFOxmList.Builder oxmListBuilder;
-
-    private void initBuilder() {
-        if (oxmListBuilder != null)
-            return;
-        oxmListBuilder = new OFOxmList.Builder();
-    }
-
-    private void updateOxmList() {
-        this.oxmList = this.oxmListBuilder.build();
-        this.oxmListSet = true;
-    }
-
-    private <F extends OFValueType<F>> OFOxm<F> getOxm(MatchField<F> field) {
-//:: if has_parent:
-        return this.oxmListSet ? this.oxmList.get(field) : parentMessage.oxmList.get(field);
-//:: else:
-        return this.oxmListSet ? this.oxmList.get(field) : null;
-//:: #endif
-    }
-
-    @Override
-    public <F extends OFValueType<F>> F get(MatchField<F> field)
-            throws UnsupportedOperationException {
-        OFOxm<F> value = getOxm(field);
-        if (value == null)
-            return null;
-        return value.getValue();
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
-            throws UnsupportedOperationException {
-        OFOxm<F> value = getOxm(field);
-        if (value == null || !value.isMasked())
-            return null;
-        // TODO: If changing OXMs to extend Masked, then use it here
-        return Masked.of(value.getValue(), value.getMask());
-    }
-
-    @Override
-    public boolean supports(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean supportsMasked(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean isExact(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value != null && !value.isMasked());
-    }
-
-    @Override
-    public boolean isFullyWildcarded(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value == null);
-    }
-
-    @Override
-    public boolean isPartiallyMasked(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value != null && value.isMasked());
-    }
-
-    @Override
-    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);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    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);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    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);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Match.Builder wildcard(MatchField<F> field) {
-        initBuilder();
-        this.oxmListBuilder.unset(field);
-        updateOxmList();
-        return this;
-    }
-
+//:: include("custom/OFMatchV3.Builder.java", msg=msg, version=version, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver13.java b/java_gen/templates/custom/OFMatchV3Ver13.java
index dc8e637..225b0dc 100644
--- a/java_gen/templates/custom/OFMatchV3Ver13.java
+++ b/java_gen/templates/custom/OFMatchV3Ver13.java
@@ -1,112 +1 @@
-//:: from generic_utils import OrderedSet
-//:: from java_gen.java_model import model
-    @Override
-    public <F extends OFValueType<F>> F get(MatchField<F> field)
-            throws UnsupportedOperationException {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<F> oxm = this.oxmList.get(field);
-
-        if (oxm == null || !field.arePrerequisitesOK(this))
-            return null;
-
-        return oxm.getValue();
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
-            throws UnsupportedOperationException {
-        if (!supportsMasked(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support masked matching on field " + field.getName());
-
-        OFOxm<F> oxm = this.oxmList.get(field);
-
-        if (oxm == null || !field.arePrerequisitesOK(this))
-            return null;
-
-        if (oxm.getMask() == null)
-            return null;
-
-        // TODO: Make OfOxm extend Masked and just return the OXM?
-        return Masked.of(oxm.getValue(), oxm.getMask());
-    }
-
-    private static boolean supportsField(MatchField<?> field) {
-        switch (field.id) {
-            //:: for id_constant in sorted(set(id_constant for _, id_constant, _ in model.oxm_map.values())):
-            case ${id_constant}:
-            //:: #endfor
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    @Override
-    public boolean supports(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean supportsMasked(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean isExact(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm != null && !oxm.isMasked();
-    }
-
-    @Override
-    public boolean isFullyWildcarded(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm == null;
-    }
-
-    @Override
-    public boolean isPartiallyMasked(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm != null && oxm.isMasked();
-    }
-
-    private class MatchFieldIterator extends AbstractIterator<MatchField<?>> {
-        private Iterator<OFOxm<?>> oxmIterator;
-
-        MatchFieldIterator() {
-            oxmIterator = oxmList.iterator();
-        }
-
-        @Override
-        protected MatchField<?> computeNext() {
-            while(oxmIterator.hasNext()) {
-                OFOxm<?> oxm = oxmIterator.next();
-                if(oxm.getMatchField().arePrerequisitesOK(OFMatchV3Ver13.this))
-                   return oxm.getMatchField();
-            }
-            endOfData();
-            return null;
-        }
-    }
-
-    @Override
-    public Iterable<MatchField<?>> getMatchFields() {
-        return new Iterable<MatchField<?>>() {
-            public Iterator<MatchField<?>> iterator() {
-                return new MatchFieldIterator();
-            }
-        };
-    }
+//:: include("custom/OFMatchV3.java", msg=msg, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver14.Builder.java b/java_gen/templates/custom/OFMatchV3Ver14.Builder.java
new file mode 100644
index 0000000..ae2ecaa
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3Ver14.Builder.java
@@ -0,0 +1 @@
+//:: include("custom/OFMatchV3.Builder.java", msg=msg, version=version, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver14.java b/java_gen/templates/custom/OFMatchV3Ver14.java
new file mode 100644
index 0000000..225b0dc
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3Ver14.java
@@ -0,0 +1 @@
+//:: include("custom/OFMatchV3.java", msg=msg, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver14_toString.java b/java_gen/templates/custom/OFMatchV3Ver14_toString.java
new file mode 100644
index 0000000..3b2783b
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3Ver14_toString.java
@@ -0,0 +1 @@
+//:: include("custom/OFMatch_toString.java", msg=msg, has_parent=False)
diff --git a/java_gen/templates/of_class.java b/java_gen/templates/of_class.java
index f1d72b2..071f177 100644
--- a/java_gen/templates/of_class.java
+++ b/java_gen/templates/of_class.java
@@ -88,7 +88,7 @@
     //:: include("_field_accessors.java", msg=msg, generate_setters=False, builder=False, has_parent=False)
 
     //:: if os.path.exists("%s/custom/%s.java" % (template_dir, msg.name)):
-    //:: include("custom/%s.java" % msg.name, msg=msg)
+    //:: include("custom/%s.java" % msg.name, msg=msg, version=version)
     //:: #endif
 
     //:: if msg.data_members:
@@ -124,7 +124,7 @@
 
                 //
                 //:: if os.path.exists("%s/custom/%s.Builder_normalize_stanza.java" % (template_dir, msg.name)):
-                //:: include("custom/%s.Builder_normalize_stanza.java" % msg.name, msg=msg, has_parent=False)
+                //:: include("custom/%s.Builder_normalize_stanza.java" % msg.name, msg=msg, version=version, has_parent=False)
                 //:: #endif
                 return new ${impl_class}(
                 //:: for i, prop in enumerate(msg.data_members):
@@ -134,7 +134,7 @@
                 );
         }
         //:: if os.path.exists("%s/custom/%s.Builder.java" % (template_dir, msg.name)):
-        //:: include("custom/%s.Builder.java" % msg.name, msg=msg, has_parent=True)
+        //:: include("custom/%s.Builder.java" % msg.name, msg=msg, version=version, has_parent=True)
         //:: #endif
 
     }
@@ -164,7 +164,7 @@
             //:: #endfor
 
             //:: if os.path.exists("%s/custom/%s.Builder_normalize_stanza.java" % (template_dir, msg.name)):
-            //:: include("custom/%s.Builder_normalize_stanza.java" % msg.name, msg=msg, has_parent=False)
+            //:: include("custom/%s.Builder_normalize_stanza.java" % msg.name, msg=msg, version=version, has_parent=False)
             //:: #endif
 
             return new ${impl_class}(
@@ -175,7 +175,7 @@
                 );
         }
         //:: if os.path.exists("%s/custom/%s.Builder.java" % (template_dir, msg.name)):
-        //:: include("custom/%s.Builder.java" % msg.name, msg=msg, has_parent=False)
+        //:: include("custom/%s.Builder.java" % msg.name, msg=msg, version=version, has_parent=False)
         //:: #endif
 
     }
@@ -249,7 +249,7 @@
 
             //:: if msg.data_members:
             //:: 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)
+            //:: include("custom/%s.Reader_normalize_stanza.java" % msg.name, msg=msg, version=version, has_parent=False)
             //:: #endif
             ${impl_class} ${msg.variable_name} = new ${impl_class}(
                     ${",\n                      ".join(
@@ -364,7 +364,7 @@
     }
 
     //:: if os.path.exists("%s/custom/%s_toString.java" % (template_dir, msg.name)):
-    //:: include("custom/%s_toString.java" % msg.name, msg=msg, has_parent=False)
+    //:: include("custom/%s_toString.java" % msg.name, msg=msg, version=version, has_parent=False)
     //:: else:
     @Override
     public String toString() {
diff --git a/loxi_globals.py b/loxi_globals.py
index e8b21bd..b4662cb 100644
--- a/loxi_globals.py
+++ b/loxi_globals.py
@@ -37,12 +37,14 @@
     VERSION_1_1 = OFVersion("1.1", 2)
     VERSION_1_2 = OFVersion("1.2", 3)
     VERSION_1_3 = OFVersion("1.3", 4)
+    VERSION_1_4 = OFVersion("1.4", 5)
 
     all_supported = (
         VERSION_1_0,
         VERSION_1_1,
         VERSION_1_2,
         VERSION_1_3,
+        VERSION_1_4,
     )
 
     wire_version_map   = { v.wire_version : v for v in all_supported }
diff --git a/loxi_ir/ir_offset.py b/loxi_ir/ir_offset.py
index 9c6a5c1..db55f24 100644
--- a/loxi_ir/ir_offset.py
+++ b/loxi_ir/ir_offset.py
@@ -35,6 +35,7 @@
         2: "uint32_t",
         3: "uint32_t",
         4: "uint32_t",
+        5: "uint32_t",
         "short_name":"port_no"
         },
     of_port_desc_t = {
@@ -42,6 +43,7 @@
         2: "of_port_desc_t",
         3: "of_port_desc_t",
         4: "of_port_desc_t",
+        5: "of_port_desc_t",
         "short_name":"port_desc"
         },
     of_bsn_vport_t = {
@@ -49,6 +51,7 @@
         2: "of_bsn_vport_t",
         3: "of_bsn_vport_t",
         4: "of_bsn_vport_t",
+        5: "of_bsn_vport_t",
         "short_name":"bsn_vport"
         },
     of_fm_cmd_t = { # Flow mod command went from u16 to u8
@@ -56,6 +59,7 @@
         2: "uint8_t",
         3: "uint8_t",
         4: "uint8_t",
+        5: "uint8_t",
         "short_name":"fm_cmd"
         },
     of_wc_bmap_t = { # Wildcard bitmap
@@ -63,6 +67,7 @@
         2: "uint32_t",
         3: "uint64_t",
         4: "uint64_t",
+        5: "uint64_t",
         "short_name":"wc_bmap"
         },
     of_match_bmap_t = { # Match bitmap
@@ -70,6 +75,7 @@
         2: "uint32_t",
         3: "uint64_t",
         4: "uint64_t",
+        5: "uint64_t",
         "short_name":"match_bmap"
         },
     of_match_t = { # Match object
@@ -77,6 +83,7 @@
         2: "of_match_v2_t",
         3: "of_match_v3_t",
         4: "of_match_v3_t",  # Currently uses same match as 1.2 (v3).
+        5: "of_match_v3_t",  # Currently uses same match as 1.2 (v3).
         "short_name":"match"
         },
 )
@@ -180,6 +187,8 @@
         member_ir_class = existing_classes[base_class]
         bytes = member_ir_class.base_length
         length_fixed = member_ir_class.is_fixed_length
+        if member_ir_class.has_external_alignment:
+            bytes = (bytes + 7) & ~7
     else:
         if base_type in existing_enums:
             enum = existing_enums[base_type]
diff --git a/openflow_input/oxm-1.2 b/openflow_input/oxm-1.2
index 5006cff..dd596bd 100644
--- a/openflow_input/oxm-1.2
+++ b/openflow_input/oxm-1.2
@@ -31,6 +31,7 @@
 
 #version 3
 #version 4
+#version 5
 
 struct of_oxm {
     uint32_t type_len == ?;
diff --git a/openflow_input/oxm-1.3 b/openflow_input/oxm-1.3
index 8574236..ac57589 100644
--- a/openflow_input/oxm-1.3
+++ b/openflow_input/oxm-1.3
@@ -29,6 +29,7 @@
 // governing permissions and limitations under the EPL.
 
 #version 4
+#version 5
 
 struct of_oxm_tunnel_id : of_oxm {
     uint32_t type_len == 0x80004c08;
diff --git a/openflow_input/standard-1.3 b/openflow_input/standard-1.3
index 64a465d..3a627e1 100644
--- a/openflow_input/standard-1.3
+++ b/openflow_input/standard-1.3
@@ -1700,7 +1700,7 @@
 struct of_table_feature_prop_experimenter : of_table_feature_prop {
     uint16_t         type == 65534;
     uint16_t         length;
-    uint32_t         experimenter;
+    uint32_t         experimenter == ?;
     uint32_t         subtype;
     of_octets_t      experimenter_data;
 };
@@ -1708,7 +1708,7 @@
 struct of_table_feature_prop_experimenter_miss : of_table_feature_prop {
     uint16_t         type == 65535;
     uint16_t         length;
-    uint32_t         experimenter;
+    uint32_t         experimenter == ?;
     uint32_t         subtype;
     of_octets_t      experimenter_data;
 };
diff --git a/openflow_input/standard-1.4 b/openflow_input/standard-1.4
new file mode 100644
index 0000000..23e96ed
--- /dev/null
+++ b/openflow_input/standard-1.4
@@ -0,0 +1,2634 @@
+// 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.
+//
+// Also derived from the OpenFlow header files which have these copyrights:
+// Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior University
+// Copyright (c) 2011, 2012 Open Networking Foundation
+
+#version 5
+
+enum macro_definitions {
+    OFP_MAX_TABLE_NAME_LEN = 32,
+    OFP_MAX_PORT_NAME_LEN = 16,
+
+    OFP_TCP_PORT = 6653,
+    OFP_SSL_PORT = 6653,
+
+    OFP_ETH_ALEN = 6,
+
+    OFP_DEFAULT_MISS_SEND_LEN = 128,
+
+    OFP_VLAN_NONE = 0,
+
+    OFP_FLOW_PERMANENT = 0,
+
+    OFP_DEFAULT_PRIORITY = 0x8000,
+
+    OFP_NO_BUFFER = 0xffffffff,
+
+    DESC_STR_LEN = 256,
+    SERIAL_NUM_LEN = 32,
+
+    OFPQ_ALL = 0xffffffff,
+    OFPQ_MAX_RATE_UNCFG = 0xffff,
+    OFPQ_MIN_RATE_UNCFG = 0xffff,
+};
+
+enum ofp_port(wire_type=uint32_t) {
+    OFPP_MAX = 0xffffff00,
+    OFPP_IN_PORT = 0xfffffff8,
+    OFPP_TABLE = 0xfffffff9,
+    OFPP_NORMAL = 0xfffffffa,
+    OFPP_FLOOD = 0xfffffffb,
+    OFPP_ALL = 0xfffffffc,
+    OFPP_CONTROLLER = 0xfffffffd,
+    OFPP_LOCAL = 0xfffffffe,
+    OFPP_ANY = 0xffffffff,
+};
+
+enum ofp_type(wire_type=uint8_t) {
+    OFPT_HELLO = 0,
+    OFPT_ERROR = 1,
+    OFPT_ECHO_REQUEST = 2,
+    OFPT_ECHO_REPLY = 3,
+    OFPT_EXPERIMENTER = 4,
+    OFPT_FEATURES_REQUEST = 5,
+    OFPT_FEATURES_REPLY = 6,
+    OFPT_GET_CONFIG_REQUEST = 7,
+    OFPT_GET_CONFIG_REPLY = 8,
+    OFPT_SET_CONFIG = 9,
+    OFPT_PACKET_IN = 10,
+    OFPT_FLOW_REMOVED = 11,
+    OFPT_PORT_STATUS = 12,
+    OFPT_PACKET_OUT = 13,
+    OFPT_FLOW_MOD = 14,
+    OFPT_GROUP_MOD = 15,
+    OFPT_PORT_MOD = 16,
+    OFPT_TABLE_MOD = 17,
+    OFPT_STATS_REQUEST = 18,
+    OFPT_STATS_REPLY = 19,
+    OFPT_BARRIER_REQUEST = 20,
+    OFPT_BARRIER_REPLY = 21,
+    OFPT_ROLE_REQUEST = 24,
+    OFPT_ROLE_REPLY = 25,
+    OFPT_GET_ASYNC_REQUEST = 26,
+    OFPT_GET_ASYNC_REPLY = 27,
+    OFPT_SET_ASYNC = 28,
+    OFPT_METER_MOD = 29,
+    OFPT_ROLE_STATUS = 30,
+    OFPT_TABLE_STATUS = 31,
+    OFPT_REQUESTFORWARD = 32,
+    OFPT_BUNDLE_CONTROL = 33,
+    OFPT_BUNDLE_ADD_MESSAGE = 34,
+};
+
+enum ofp_config_flags(wire_type=uint16_t, bitmask=True) {
+    OFPC_FRAG_NORMAL = 0,
+    OFPC_FRAG_DROP = 1,
+    OFPC_FRAG_REASM = 2,
+    OFPC_FRAG_MASK = 3,
+};
+
+enum ofp_table_config(wire_type=uint32_t, bitmask=True) {
+    OFPTC_DEPRECATED_MASK = 0x3,
+    OFPTC_EVICTION = 0x4,
+    OFPTC_VACANCY_EVENTS = 0x8,
+};
+
+enum ofp_table(wire_type=uint8_t, complete=False) {
+    OFPTT_MAX = 0xfe,
+    OFPTT_ALL = 0xff,
+};
+
+enum ofp_capabilities(wire_type=uint32_t, bitmask=True) {
+    OFPC_FLOW_STATS = 0x1,
+    OFPC_TABLE_STATS = 0x2,
+    OFPC_PORT_STATS = 0x4,
+    OFPC_GROUP_STATS = 0x8,
+    OFPC_IP_REASM = 0x20,
+    OFPC_QUEUE_STATS = 0x40,
+    OFPC_PORT_BLOCKED = 0x100,
+};
+
+enum ofp_port_config(wire_type=uint32_t, bitmask=True) {
+    OFPPC_PORT_DOWN = 0x1,
+    OFPPC_NO_RECV = 0x4,
+    OFPPC_NO_FWD = 0x20,
+    OFPPC_NO_PACKET_IN = 0x40,
+    OFPPC_BSN_MIRROR_DEST = 0x80000000,
+};
+
+enum ofp_port_state(wire_type=uint32_t, bitmask=True) {
+    OFPPS_LINK_DOWN = 0x1,
+    OFPPS_BLOCKED = 0x2,
+    OFPPS_LIVE = 0x4,
+};
+
+enum ofp_port_features(wire_type=uint32_t, bitmask=True) {
+    OFPPF_10MB_HD = 0x1,
+    OFPPF_10MB_FD = 0x2,
+    OFPPF_100MB_HD = 0x4,
+    OFPPF_100MB_FD = 0x8,
+    OFPPF_1GB_HD = 0x10,
+    OFPPF_1GB_FD = 0x20,
+    OFPPF_10GB_FD = 0x40,
+    OFPPF_40GB_FD = 0x80,
+    OFPPF_100GB_FD = 0x100,
+    OFPPF_1TB_FD = 0x200,
+    OFPPF_OTHER = 0x400,
+    OFPPF_COPPER = 0x800,
+    OFPPF_FIBER = 0x1000,
+    OFPPF_AUTONEG = 0x2000,
+    OFPPF_PAUSE = 0x4000,
+    OFPPF_PAUSE_ASYM = 0x8000,
+};
+
+enum ofp_port_reason(wire_type=uint8_t) {
+    OFPPR_ADD = 0,
+    OFPPR_DELETE = 1,
+    OFPPR_MODIFY = 2,
+};
+
+enum ofp_match_type(wire_type=uint16_t) {
+    OFPMT_STANDARD = 0,
+    OFPMT_OXM = 1,
+};
+
+enum ofp_oxm_class(wire_type=uint16_t) {
+    OFPXMC_NXM_0 = 0,
+    OFPXMC_NXM_1 = 1,
+    OFPXMC_OPENFLOW_BASIC = 0x8000,
+    OFPXMC_EXPERIMENTER = 0xffff,
+};
+
+enum ofp_vlan_id(wire_type=uint16_t) {
+    OFPVID_NONE = 0,
+    OFPVID_PRESENT = 0x1000,
+};
+
+// FIXME: OF spec specified this as '9' bits, implicitly adding
+// to full byte
+enum ofp_ipv6exthdr_flags(wire_type=uint16_t, bitmask=True) {
+    OFPIEH_NONEXT = 0x1,
+    OFPIEH_ESP = 0x2,
+    OFPIEH_AUTH = 0x4,
+    OFPIEH_DEST = 0x8,
+    OFPIEH_FRAG = 0x10,
+    OFPIEH_ROUTER = 0x20,
+    OFPIEH_HOP = 0x40,
+    OFPIEH_UNREP = 0x80,
+    OFPIEH_UNSEQ = 0x100,
+};
+
+enum ofp_action_type(wire_type=uint16_t) {
+    OFPAT_OUTPUT = 0,
+    OFPAT_COPY_TTL_OUT = 0xb,
+    OFPAT_COPY_TTL_IN = 0xc,
+    OFPAT_SET_MPLS_TTL = 0xf,
+    OFPAT_DEC_MPLS_TTL = 0x10,
+    OFPAT_PUSH_VLAN = 0x11,
+    OFPAT_POP_VLAN = 0x12,
+    OFPAT_PUSH_MPLS = 0x13,
+    OFPAT_POP_MPLS = 0x14,
+    OFPAT_SET_QUEUE = 0x15,
+    OFPAT_GROUP = 0x16,
+    OFPAT_SET_NW_TTL = 0x17,
+    OFPAT_DEC_NW_TTL = 0x18,
+    OFPAT_SET_FIELD = 0x19,
+    OFPAT_PUSH_PBB = 0x1a,
+    OFPAT_POP_PBB = 0x1b,
+    OFPAT_EXPERIMENTER = 0xffff,
+};
+
+enum ofp_controller_max_len(wire_type=uint16_t, complete=False) {
+    OFPCML_MAX = 0xffe5,
+    OFPCML_NO_BUFFER = 0xffff,
+};
+
+enum ofp_instruction_type(wire_type=uint16_t, bitmask=True) {
+    OFPIT_GOTO_TABLE = 0x1,
+    OFPIT_WRITE_METADATA = 0x2,
+    OFPIT_WRITE_ACTIONS = 0x3,
+    OFPIT_APPLY_ACTIONS = 0x4,
+    OFPIT_CLEAR_ACTIONS = 0x5,
+    OFPIT_METER = 0x6,
+    OFPIT_EXPERIMENTER = 0xffff,
+};
+
+enum ofp_flow_mod_command(wire_type=uint8_t) {
+    OFPFC_ADD = 0,
+    OFPFC_MODIFY = 1,
+    OFPFC_MODIFY_STRICT = 2,
+    OFPFC_DELETE = 3,
+    OFPFC_DELETE_STRICT = 4,
+};
+
+enum ofp_flow_mod_flags(wire_type=uint16_t, bitmask=True) {
+    OFPFF_SEND_FLOW_REM = 0x1,
+    OFPFF_CHECK_OVERLAP = 0x2,
+    OFPFF_RESET_COUNTS = 0x4,
+    OFPFF_NO_PKT_COUNTS = 0x8,
+    OFPFF_NO_BYT_COUNTS = 0x10,
+
+    /* Non-standard, enabled by an experimenter message */
+    /* See the bsn_flow_idle input file */
+    OFPFF_BSN_SEND_IDLE = 0x80,
+};
+
+enum ofp_group(wire_type=uint32_t, complete=False) {
+    OFPG_MAX = 0xffffff00,
+    OFPG_ALL = 0xfffffffc,
+    OFPG_ANY = 0xffffffff,
+};
+
+enum ofp_group_mod_command(wire_type=uint16_t) {
+    OFPGC_ADD = 0,
+    OFPGC_MODIFY = 1,
+    OFPGC_DELETE = 2,
+};
+
+enum ofp_group_type(wire_type=uint8_t) {
+    OFPGT_ALL = 0,
+    OFPGT_SELECT = 1,
+    OFPGT_INDIRECT = 2,
+    OFPGT_FF = 3,
+};
+
+enum ofp_packet_in_reason(wire_type=uint8_t) {
+    OFPR_NO_MATCH = 0,
+    OFPR_ACTION = 1,
+    OFPR_INVALID_TTL = 2,
+    OFPR_ACTION_SET = 3,
+    OFPR_GROUP = 4,
+    OFPR_PACKET_OUT = 5,
+
+    // non-standard BSN extensions. OF does not have a standard-conformant
+    // way to extend the set of packet_in reasons
+    OFPR_BSN_NEW_HOST = 128,
+    OFPR_BSN_STATION_MOVE = 129,
+    OFPR_BSN_BAD_VLAN = 130,
+    OFPR_BSN_DESTINATION_LOOKUP_FAILURE = 131,
+    OFPR_BSN_NO_ROUTE = 132,
+    OFPR_BSN_ICMP_ECHO_REQUEST = 133,
+    OFPR_BSN_DEST_NETWORK_UNREACHABLE = 134,
+    OFPR_BSN_DEST_HOST_UNREACHABLE = 135,
+    OFPR_BSN_DEST_PORT_UNREACHABLE = 136,
+    OFPR_BSN_FRAGMENTATION_REQUIRED = 137,
+    OFPR_BSN_ARP = 139,
+    OFPR_BSN_DHCP = 140,
+    OFPR_BSN_DEBUG = 141,
+    OFPR_BSN_PACKET_OF_DEATH = 142,
+};
+
+enum ofp_flow_removed_reason(wire_type=uint8_t) {
+    OFPRR_IDLE_TIMEOUT = 0,
+    OFPRR_HARD_TIMEOUT = 1,
+    OFPRR_DELETE = 2,
+    OFPRR_GROUP_DELETE = 3,
+    OFPRR_METER_DELETE = 4,
+    OFPRR_EVICTION = 5,
+};
+
+enum ofp_meter(wire_type=uint32_t, complete=False) {
+    OFPM_MAX = 0xffff0000,
+    OFPM_SLOWPATH = 0xfffffffd,
+    OFPM_CONTROLLER = 0xfffffffe,
+    OFPM_ALL = 0xffffffff,
+};
+
+enum ofp_meter_band_type(wire_type=uint16_t) {
+    OFPMBT_DROP = 0x1,
+    OFPMBT_DSCP_REMARK = 0x2,
+    OFPMBT_EXPERIMENTER = 0xffff,
+};
+
+enum ofp_meter_mod_command(wire_type=uint16_t) {
+    OFPMC_ADD = 0,
+    OFPMC_MODIFY = 1,
+    OFPMC_DELETE = 2,
+};
+
+enum ofp_meter_flags(wire_type=uint16_t, bitmask=True) {
+    OFPMF_KBPS = 0x1,
+    OFPMF_PKTPS = 0x2,
+    OFPMF_BURST = 0x4,
+    OFPMF_STATS = 0x8,
+};
+
+enum ofp_error_type(wire_type=uint16_t) {
+    OFPET_HELLO_FAILED = 0,
+    OFPET_BAD_REQUEST = 1,
+    OFPET_BAD_ACTION = 2,
+    OFPET_BAD_INSTRUCTION = 3,
+    OFPET_BAD_MATCH = 4,
+    OFPET_FLOW_MOD_FAILED = 5,
+    OFPET_GROUP_MOD_FAILED = 6,
+    OFPET_PORT_MOD_FAILED = 7,
+    OFPET_TABLE_MOD_FAILED = 8,
+    OFPET_QUEUE_OP_FAILED = 9,
+    OFPET_SWITCH_CONFIG_FAILED = 10,
+    OFPET_ROLE_REQUEST_FAILED = 11,
+    OFPET_METER_MOD_FAILED = 12,
+    OFPET_TABLE_FEATURES_FAILED = 13,
+    OFPET_BAD_PROPERTY = 14,
+    OFPET_ASYNC_CONFIG_FAILED = 15,
+    OFPET_FLOW_MONITOR_FAILED = 16,
+    OFPET_BUNDLE_FAILED = 17,
+    OFPET_EXPERIMENTER = 0xffff,
+};
+
+enum ofp_hello_failed_code(wire_type=uint16_t) {
+    OFPHFC_INCOMPATIBLE = 0,
+    OFPHFC_EPERM = 1,
+};
+
+enum ofp_bad_request_code(wire_type=uint16_t) {
+    OFPBRC_BAD_VERSION = 0,
+    OFPBRC_BAD_TYPE = 1,
+    OFPBRC_BAD_STAT = 2,
+    OFPBRC_BAD_EXPERIMENTER = 3,
+    OFPBRC_BAD_EXPERIMENTER_TYPE = 4,
+    OFPBRC_EPERM = 5,
+    OFPBRC_BAD_LEN = 6,
+    OFPBRC_BUFFER_EMPTY = 7,
+    OFPBRC_BUFFER_UNKNOWN = 8,
+    OFPBRC_BAD_TABLE_ID = 9,
+    OFPBRC_IS_SLAVE = 10,
+    OFPBRC_BAD_PORT = 11,
+    OFPBRC_BAD_PACKET = 12,
+    OFPBRC_MULTIPART_BUFFER_OVERFLOW = 13,
+    OFPBRC_MULTIPART_REQUEST_TIMEOUT = 14,
+    OFPBRC_MULTIPART_REPLY_TIMEOUT = 15,
+};
+
+enum ofp_bad_action_code(wire_type=uint16_t) {
+    OFPBAC_BAD_TYPE = 0,
+    OFPBAC_BAD_LEN = 1,
+    OFPBAC_BAD_EXPERIMENTER = 2,
+    OFPBAC_BAD_EXPERIMENTER_TYPE = 3,
+    OFPBAC_BAD_OUT_PORT = 4,
+    OFPBAC_BAD_ARGUMENT = 5,
+    OFPBAC_EPERM = 6,
+    OFPBAC_TOO_MANY = 7,
+    OFPBAC_BAD_QUEUE = 8,
+    OFPBAC_BAD_OUT_GROUP = 9,
+    OFPBAC_MATCH_INCONSISTENT = 10,
+    OFPBAC_UNSUPPORTED_ORDER = 11,
+    OFPBAC_BAD_TAG = 12,
+    OFPBAC_BAD_SET_TYPE = 13,
+    OFPBAC_BAD_SET_LEN = 14,
+    OFPBAC_BAD_SET_ARGUMENT = 15,
+};
+
+enum ofp_bad_instruction_code(wire_type=uint16_t) {
+    OFPBIC_UNKNOWN_INST = 0,
+    OFPBIC_UNSUP_INST = 1,
+    OFPBIC_BAD_TABLE_ID = 2,
+    OFPBIC_UNSUP_METADATA = 3,
+    OFPBIC_UNSUP_METADATA_MASK = 4,
+    OFPBIC_BAD_EXPERIMENTER = 5,
+    OFPBIC_BAD_EXPERIMENTER_TYPE = 6,
+    OFPBIC_BAD_LEN = 7,
+    OFPBIC_EPERM = 8,
+    OFPBIC_DUP_INST = 9,
+};
+
+enum ofp_bad_match_code(wire_type=uint16_t) {
+    OFPBMC_BAD_TYPE = 0,
+    OFPBMC_BAD_LEN = 1,
+    OFPBMC_BAD_TAG = 2,
+    OFPBMC_BAD_DL_ADDR_MASK = 3,
+    OFPBMC_BAD_NW_ADDR_MASK = 4,
+    OFPBMC_BAD_WILDCARDS = 5,
+    OFPBMC_BAD_FIELD = 6,
+    OFPBMC_BAD_VALUE = 7,
+    OFPBMC_BAD_MASK = 8,
+    OFPBMC_BAD_PREREQ = 9,
+    OFPBMC_DUP_FIELD = 10,
+    OFPBMC_EPERM = 11,
+};
+
+enum ofp_flow_mod_failed_code(wire_type=uint16_t) {
+    OFPFMFC_UNKNOWN = 0,
+    OFPFMFC_TABLE_FULL = 1,
+    OFPFMFC_BAD_TABLE_ID = 2,
+    OFPFMFC_OVERLAP = 3,
+    OFPFMFC_EPERM = 4,
+    OFPFMFC_BAD_TIMEOUT = 5,
+    OFPFMFC_BAD_COMMAND = 6,
+    OFPFMFC_BAD_FLAGS = 7,
+    OFPFMFC_CANT_SYNC = 8,
+    OFPFMFC_BAD_PRIORITY = 9,
+};
+
+enum ofp_group_mod_failed_code(wire_type=uint16_t) {
+    OFPGMFC_GROUP_EXISTS = 0,
+    OFPGMFC_INVALID_GROUP = 1,
+    OFPGMFC_WEIGHT_UNSUPPORTED = 2,
+    OFPGMFC_OUT_OF_GROUPS = 3,
+    OFPGMFC_OUT_OF_BUCKETS = 4,
+    OFPGMFC_CHAINING_UNSUPPORTED = 5,
+    OFPGMFC_WATCH_UNSUPPORTED = 6,
+    OFPGMFC_LOOP = 7,
+    OFPGMFC_UNKNOWN_GROUP = 8,
+    OFPGMFC_CHAINED_GROUP = 9,
+    OFPGMFC_BAD_TYPE = 10,
+    OFPGMFC_BAD_COMMAND = 11,
+    OFPGMFC_BAD_BUCKET = 12,
+    OFPGMFC_BAD_WATCH = 13,
+    OFPGMFC_EPERM = 14,
+};
+
+enum ofp_port_mod_failed_code(wire_type=uint16_t) {
+    OFPPMFC_BAD_PORT = 0,
+    OFPPMFC_BAD_HW_ADDR = 1,
+    OFPPMFC_BAD_CONFIG = 2,
+    OFPPMFC_BAD_ADVERTISE = 3,
+    OFPPMFC_EPERM = 4,
+};
+
+enum ofp_table_mod_failed_code(wire_type=uint16_t) {
+    OFPTMFC_BAD_TABLE = 0,
+    OFPTMFC_BAD_CONFIG = 1,
+    OFPTMFC_EPERM = 2,
+};
+
+enum ofp_queue_op_failed_code(wire_type=uint16_t) {
+    OFPQOFC_BAD_PORT = 0,
+    OFPQOFC_BAD_QUEUE = 1,
+    OFPQOFC_EPERM = 2,
+};
+
+enum ofp_switch_config_failed_code(wire_type=uint16_t) {
+    OFPSCFC_BAD_FLAGS = 0,
+    OFPSCFC_BAD_LEN = 1,
+    OFPSCFC_EPERM = 2,
+};
+
+enum ofp_role_request_failed_code(wire_type=uint16_t){
+    OFPRRFC_STALE = 0,
+    OFPRRFC_UNSUP = 1,
+    OFPRRFC_BAD_ROLE = 2,
+};
+
+enum ofp_meter_mod_failed_code(wire_type=uint16_t) {
+    OFPMMFC_UNKNOWN = 0,
+    OFPMMFC_METER_EXISTS = 1,
+    OFPMMFC_INVALID_METER = 2,
+    OFPMMFC_UNKNOWN_METER = 3,
+    OFPMMFC_BAD_COMMAND = 4,
+    OFPMMFC_BAD_FLAGS = 5,
+    OFPMMFC_BAD_RATE = 6,
+    OFPMMFC_BAD_BURST = 7,
+    OFPMMFC_BAD_BAND = 8,
+    OFPMMFC_BAD_BAND_VALUE = 9,
+    OFPMMFC_OUT_OF_METERS = 10,
+    OFPMMFC_OUT_OF_BANDS = 11,
+};
+
+enum ofp_table_features_failed_code(wire_type=uint16_t) {
+    OFPTFFC_BAD_TABLE = 0,
+    OFPTFFC_BAD_METADATA = 1,
+    OFPTFFC_EPERM = 5,
+};
+
+enum ofp_bad_property_code(wire_type=uint16_t) {
+    OFPBPC_BAD_TYPE = 0, /* Unknown property type. */
+    OFPBPC_BAD_LEN = 1, /* Length problem in property. */
+    OFPBPC_BAD_VALUE = 2, /* Unsupported property value. */
+    OFPBPC_TOO_MANY = 3, /* Can’t handle this many properties. */
+    OFPBPC_DUP_TYPE = 4, /* A property type was duplicated. */
+    OFPBPC_BAD_EXPERIMENTER = 5, /* Unknown experimenter id specified. */
+    OFPBPC_BAD_EXP_TYPE = 6, /* Unknown exp_type for experimenter id. */
+    OFPBPC_BAD_EXP_VALUE = 7, /* Unknown value for experimenter id. */
+    OFPBPC_EPERM = 8, /* Permissions error. */
+};
+
+
+enum ofp_async_config_failed_code(wire_type=uint16_t) {
+    OFPACFC_INVALID = 0, /* One mask is invalid. */
+    OFPACFC_UNSUPPORTED = 1, /* Requested configuration not supported. */
+    OFPACFC_EPERM = 2, /* Permissions error. */
+};
+
+enum ofp_flow_monitor_failed_code(wire_type=uint16_t) {
+    OFPMOFC_UNKNOWN = 0, /* Unspecified error. */
+    OFPMOFC_MONITOR_EXISTS = 1, /* Monitor not added because a Monitor ADD
+                                 * attempted to replace an existing Monitor. */
+    OFPMOFC_INVALID_MONITOR = 2, /* Monitor not added because Monitor specified
+                                  * is invalid. */
+    OFPMOFC_UNKNOWN_MONITOR = 3, /* Monitor not modified because a Monitor
+                                    MODIFY attempted to modify a non-existent
+                                    Monitor. */
+    OFPMOFC_BAD_COMMAND = 4, /* Unsupported or unknown command. */
+    OFPMOFC_BAD_FLAGS = 5, /* Flag configuration unsupported. */
+    OFPMOFC_BAD_TABLE_ID = 6, /* Specified table does not exist. */
+    OFPMOFC_BAD_OUT = 7, /* Error in output port/group. */
+};
+
+enum ofp_bundle_failed_code(wire_type=uint16_t) {
+    OFPBFC_UNKNOWN = 0, /* Unspecified error. */
+    OFPBFC_EPERM = 1, /* Permissions error. */
+    OFPBFC_BAD_ID = 2, /* Bundle ID doesn’t exist. */
+    OFPBFC_BUNDLE_EXIST = 3, /* Bundle ID already exist. */
+    OFPBFC_BUNDLE_CLOSED = 4, /* Bundle ID is closed. */
+    OFPBFC_OUT_OF_BUNDLES = 5, /* Too many bundles IDs. */
+    OFPBFC_BAD_TYPE = 6, /* Unsupported or unknown message control type. */
+    OFPBFC_BAD_FLAGS = 7, /* Unsupported, unknown, or inconsistent flags. */
+    OFPBFC_MSG_BAD_LEN = 8, /* Length problem in included message. */
+    OFPBFC_MSG_BAD_XID = 9, /* Inconsistent or duplicate XID. */
+    OFPBFC_MSG_UNSUP = 10, /* Unsupported message in this bundle. */
+    OFPBFC_MSG_CONFLICT = 11, /* Unsupported message combination in this bundle. */
+    OFPBFC_MSG_TOO_MANY = 12, /* Cant handle this many messages in bundle. */
+    OFPBFC_MSG_FAILED = 13, /* One message in bundle failed. */
+    OFPBFC_TIMEOUT = 14, /* Bundle is taking too long. */
+    OFPBFC_BUNDLE_IN_PROGRESS = 15, /* Bundle is locking the resource. */
+};
+
+/* Port stats property types.
+ */
+enum ofp_port_stats_prop_type(wire_type=uint16_t) {
+    OFPPSPT_ETHERNET          = 0,      /* Ethernet property. */
+    OFPPSPT_OPTICAL           = 1,      /* Optical property. */
+    OFPPSPT_EXPERIMENTER      = 0xFFFF, /* Experimenter property. */
+};
+
+
+enum ofp_stats_type(wire_type=uint16_t) {
+    OFPST_DESC = 0,
+    OFPST_FLOW = 1,
+    OFPST_AGGREGATE = 2,
+    OFPST_TABLE = 3,
+    OFPST_PORT = 4,
+    OFPST_QUEUE = 5,
+    OFPST_GROUP = 6,
+    OFPST_GROUP_DESC = 7,
+    OFPST_GROUP_FEATURES = 8,
+    OFPST_METER = 9,
+    OFPST_METER_CONFIG = 10,
+    OFPST_METER_FEATURES = 11,
+    OFPST_TABLE_FEATURES = 12,
+    OFPST_PORT_DESC = 13,
+    OFPMP_TABLE_DESC = 14,
+    OFPMP_QUEUE_DESC = 15,
+    OFPMP_FLOW_MONITOR = 16,
+    OFPST_EXPERIMENTER = 0xffff,
+};
+
+enum ofp_stats_request_flags(wire_type=uint16_t, bitmask=True) {
+    OFPSF_REQ_MORE = 0x1,
+};
+
+enum ofp_stats_reply_flags(wire_type=uint16_t, bitmask=True) {
+    OFPSF_REPLY_MORE = 0x1,
+};
+
+enum ofp_table_feature_prop_type(wire_type=uint16_t) {
+    OFPTFPT_INSTRUCTIONS = 0,
+    OFPTFPT_INSTRUCTIONS_MISS = 1,
+    OFPTFPT_NEXT_TABLES = 2,
+    OFPTFPT_NEXT_TABLES_MISS = 3,
+    OFPTFPT_WRITE_ACTIONS = 4,
+    OFPTFPT_WRITE_ACTIONS_MISS = 5,
+    OFPTFPT_APPLY_ACTIONS = 6,
+    OFPTFPT_APPLY_ACTIONS_MISS = 7,
+    OFPTFPT_MATCH = 8,
+    OFPTFPT_WILDCARDS = 0xa,
+    OFPTFPT_WRITE_SETFIELD = 0xc,
+    OFPTFPT_WRITE_SETFIELD_MISS = 0xd,
+    OFPTFPT_APPLY_SETFIELD = 0xe,
+    OFPTFPT_APPLY_SETFIELD_MISS = 0xf,
+    OFPTFPT_TABLE_SYNC_FROM = 0x10,
+    OFPTFPT_EXPERIMENTER = 0xfffe,
+    OFPTFPT_EXPERIMENTER_MISS = 0xffff,
+};
+
+enum ofp_group_capabilities(wire_type=uint32_t, bitmask=True) {
+    OFPGFC_SELECT_WEIGHT = 0x1,
+    OFPGFC_SELECT_LIVENESS = 0x2,
+    OFPGFC_CHAINING = 0x4,
+    OFPGFC_CHAINING_CHECKS = 0x8,
+};
+
+enum ofp_controller_role(wire_type=uint32_t) {
+    OFPCR_ROLE_NOCHANGE = 0,
+    OFPCR_ROLE_EQUAL = 1,
+    OFPCR_ROLE_MASTER = 2,
+    OFPCR_ROLE_SLAVE = 3,
+};
+
+enum ofp_hello_elem_type(wire_type=uint16_t) {
+    OFPHET_VERSIONBITMAP = 1,
+};
+
+enum ofp_optical_port_features(wire_type=uint32_t, bitmask=True) {
+    OFPOPF_RX_TUNE = 0x1,
+    OFPOPF_TX_TUNE = 0x2,
+    OFPOPF_TX_PWR = 0x4,
+    OFPOPF_USE_FREQ = 0x8,
+};
+
+enum ofp_table_mod_prop_eviction_flag(wire_type=uint32_t, bitmask=True) {
+    OFPTMPEF_OTHER = 0x1,
+    OFPTMPEF_IMPORTANCE = 0x2,
+    OFPTMPEF_LIFETIME = 0x4,
+};
+
+enum ofp_port_stats_optical_flags(wire_type=uint32_t, bitmask=True) {
+    OFPOSF_RX_TUNE = 0x1,
+    OFPOSF_TX_TUNE = 0x2,
+    OFPOSF_TX_PWR = 0x4,
+    OFPOSF_RX_PWR = 0x10,
+    OFPOSF_TX_BIAS = 0x20,
+    OFPOSF_TX_TEMP = 0x40,
+};
+
+enum ofp_bundle_ctrl_type(wire_type=uint16_t) {
+    OFPBCT_OPEN_REQUEST = 0,
+    OFPBCT_OPEN_REPLY = 1,
+    OFPBCT_CLOSE_REQUEST = 2,
+    OFPBCT_CLOSE_REPLY = 3,
+    OFPBCT_COMMIT_REQUEST = 4,
+    OFPBCT_COMMIT_REPLY = 5,
+    OFPBCT_DISCARD_REQUEST = 6,
+    OFPBCT_DISCARD_REPLY = 7,
+};
+
+enum ofp_bundle_flags(wire_type=uint16_t, bitmask=True) {
+    OFPBF_ATOMIC = 1,
+    OFPBF_ORDERED = 2,
+};
+
+enum ofp_controller_role_reason(wire_type=uint8_t) {
+    OFPCRR_MASTER_REQUEST = 0,
+    OFPCRR_CONFIG = 1,
+    OFPCRR_EXPERIMENTER = 2,
+};
+
+enum ofp_table_reason(wire_type=uint8_t) {
+    OFPTR_VACANCY_DOWN = 3,
+    OFPTR_VACANCY_UP = 4,
+};
+
+enum ofp_requestforward_reason {
+    OFPRFR_GROUP_MOD = 0,
+    OFPRFR_METER_MOD = 1,
+};
+
+/* XXX rename to of_message */
+struct of_header {
+    uint8_t version;
+    uint8_t type == ?;
+    uint16_t length;
+    uint32_t xid;
+};
+
+struct of_uint64 {
+    uint64_t value;
+};
+
+// Special structures used for managing scalar list elements
+struct of_uint32 {
+    uint32_t value;
+};
+
+// Special structures used for managing scalar list elements
+struct of_uint8 {
+    uint8_t value;
+};
+
+struct of_hello_elem {
+    uint16_t type == ?;
+    uint16_t length;
+};
+
+struct of_hello_elem_versionbitmap : of_hello_elem {
+    uint16_t type == 1;
+    uint16_t length;
+    list(of_uint32_t) bitmaps;
+};
+
+struct of_hello : of_header {
+    uint8_t version;
+    uint8_t type == 0;
+    uint16_t length;
+    uint32_t xid;
+    list(of_hello_elem_t) elements;
+};
+
+struct of_echo_request : of_header {
+    uint8_t version;
+    uint8_t type == 2;
+    uint16_t length;
+    uint32_t xid;
+    of_octets_t data;
+};
+
+struct of_echo_reply : of_header {
+    uint8_t version;
+    uint8_t type == 3;
+    uint16_t length;
+    uint32_t xid;
+    of_octets_t data;
+};
+
+struct of_experimenter : of_header {
+    uint8_t version;
+    uint8_t type == 4;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t experimenter == ?;
+    uint32_t subtype;
+    of_octets_t data;
+};
+
+struct of_barrier_request : of_header {
+    uint8_t version;
+    uint8_t type == 20;
+    uint16_t length;
+    uint32_t xid;
+};
+
+struct of_barrier_reply : of_header {
+    uint8_t version;
+    uint8_t type == 21;
+    uint16_t length;
+    uint32_t xid;
+};
+
+struct of_get_config_request : of_header {
+    uint8_t version;
+    uint8_t type == 7;
+    uint16_t length;
+    uint32_t xid;
+};
+
+struct of_get_config_reply : of_header {
+    uint8_t version;
+    uint8_t type == 8;
+    uint16_t length;
+    uint32_t xid;
+    enum ofp_config_flags flags;
+    uint16_t miss_send_len;
+};
+
+struct of_set_config : of_header {
+    uint8_t version;
+    uint8_t type == 9;
+    uint16_t length;
+    uint32_t xid;
+    enum ofp_config_flags flags;
+    uint16_t miss_send_len;
+};
+
+struct of_table_mod_prop {
+    uint16_t type == ?; /* One of OFPTMPT_*. */
+    uint16_t length; /* Length in bytes of this property. */
+};
+
+struct of_table_mod_prop_eviction {
+    uint16_t type == 2;
+    uint16_t length;
+    enum ofp_table_mod_prop_eviction_flag flags;
+};
+
+struct of_table_mod_prop_vacancy {
+    uint16_t type == 3;
+    uint16_t length;
+    uint8_t vacancy_down;
+    uint8_t vacancy_up;
+    uint8_t vacancy;
+    pad(1);
+};
+
+struct of_table_mod_prop_experimenter {
+    uint16_t type == 0xffff;
+    uint16_t length;
+    uint32_t experimenter == ?;
+    uint32_t exp_type;
+};
+
+struct of_table_mod : of_header {
+    uint8_t version;
+    uint8_t type == 17;
+    uint16_t length;
+    uint32_t xid;
+    uint8_t table_id;
+    pad(3);
+    uint32_t config;
+    list(of_table_mod_prop_t) properties;
+};
+
+struct of_port_desc_prop {
+    uint16_t type == ?; /* One of OFPPDPT_*. */
+    uint16_t length; /* Length in bytes of this property. */
+};
+
+struct of_port_desc_prop_ethernet : of_port_desc_prop {
+    uint16_t type == 0;
+    uint16_t length;
+    pad(4);
+    uint32_t curr;
+    uint32_t advertised;
+    uint32_t supported;
+    uint32_t peer;
+    uint32_t curr_speed;
+    uint32_t max_speed;
+};
+
+struct of_port_desc_prop_optical : of_port_desc_prop {
+    uint16_t type == 1;
+    uint16_t length;
+    pad(4);
+    uint32_t supported;
+    uint32_t tx_min_freq_lmda;
+    uint32_t tx_max_freq_lmda;
+    uint32_t tx_grid_freq_lmda;
+    uint32_t rx_min_freq_lmda;
+    uint32_t rx_max_freq_lmda;
+    uint32_t rx_grid_freq_lmda;
+    uint32_t tx_pwr_min;
+    uint32_t tx_pwr_max;
+};
+
+struct of_port_desc_prop_experimenter : of_port_desc_prop {
+    uint16_t type == 0xffff;
+    uint16_t length;
+    uint32_t experimenter == ?;
+    uint32_t exp_type;
+};
+
+struct of_port_desc {
+    of_port_no_t port_no;
+    uint16_t length;
+    pad(2);
+    of_mac_addr_t hw_addr;
+    pad(2);
+    of_port_name_t name;
+    enum ofp_port_config config; /* Bitmap of OFPPC_* flags. */
+    enum ofp_port_state state; /* Bitmap of OFPPS_* flags. */
+    list(of_port_desc_prop_t) properties;
+};
+
+struct of_features_request : of_header {
+    uint8_t version;
+    uint8_t type == 5;
+    uint16_t length;
+    uint32_t xid;
+};
+
+struct of_features_reply : of_header {
+    uint8_t version;
+    uint8_t type == 6;
+    uint16_t length;
+    uint32_t xid;
+    uint64_t datapath_id;
+    uint32_t n_buffers;
+    uint8_t n_tables;
+    uint8_t auxiliary_id;
+    pad(2);
+    enum ofp_capabilities capabilities;
+    uint32_t reserved;
+};
+
+struct of_port_status : of_header {
+    uint8_t version;
+    uint8_t type == 12;
+    uint16_t length;
+    uint32_t xid;
+    enum ofp_port_reason reason;
+    pad(7);
+    of_port_desc_t desc;
+};
+
+struct of_port_mod_prop {
+    uint16_t type == ?; /* One of OFPPMPT_*. */
+    uint16_t length; /* Length in bytes of this property. */
+};
+
+struct of_port_mod_prop_ethernet : of_port_mod_prop {
+    uint16_t type == 0;
+    uint16_t length;
+    uint32_t advertise;
+};
+
+struct of_port_mod_prop_optical : of_port_mod_prop {
+    uint16_t type == 1;
+    uint16_t length;
+    uint32_t configure;
+    uint32_t freq_ldma;
+    uint32_t fl_offset; /* TODO signed */
+    uint32_t grid_span;
+    uint32_t tx_pwr;
+};
+
+struct of_port_mod_prop_experimenter : of_port_mod_prop {
+    uint16_t type == 0xffff;
+    uint16_t length;
+    uint32_t experimenter == ?;
+    uint32_t exp_type;
+};
+
+struct of_port_mod : of_header {
+    uint8_t version;
+    uint8_t type == 16;
+    uint16_t length;
+    uint32_t xid;
+    of_port_no_t port_no;
+    pad(4);
+    of_mac_addr_t hw_addr;
+    pad(2);
+    uint32_t config;
+    uint32_t mask;
+    list(of_port_mod_prop_t) properties;
+};
+
+struct of_match_v3(align=8, length_includes_align=False) {
+    uint16_t type == 1;
+    uint16_t length;
+    list(of_oxm_t) oxm_list;
+};
+
+// This looks like an action header, but is standalone.  See
+// ofp_table_features_prop_actions
+struct of_action_id {
+    uint16_t type;
+    uint16_t len;
+    of_octets_t exp_data;
+};
+
+struct of_action_output : of_action {
+    uint16_t type == 0;
+    uint16_t len;
+    of_port_no_t port;
+    uint16_t max_len;
+    pad(6);
+};
+
+struct of_action_copy_ttl_out : of_action {
+    uint16_t type == 11;
+    uint16_t len;
+    pad(4);
+};
+
+struct of_action_copy_ttl_in : of_action {
+    uint16_t type == 12;
+    uint16_t len;
+    pad(4);
+};
+
+struct of_action_set_mpls_ttl : of_action {
+    uint16_t type == 15;
+    uint16_t len;
+    uint8_t mpls_ttl;
+    pad(3);
+};
+
+struct of_action_dec_mpls_ttl : of_action {
+    uint16_t type == 16;
+    uint16_t len;
+    pad(4);
+};
+
+struct of_action_push_vlan : of_action {
+    uint16_t type == 17;
+    uint16_t len;
+    uint16_t ethertype;
+    pad(2);
+};
+
+struct of_action_pop_vlan : of_action {
+    uint16_t type == 18;
+    uint16_t len;
+    pad(4);
+};
+
+struct of_action_push_mpls : of_action {
+    uint16_t type == 19;
+    uint16_t len;
+    uint16_t ethertype;
+    pad(2);
+};
+
+struct of_action_pop_mpls : of_action {
+    uint16_t type == 20;
+    uint16_t len;
+    uint16_t ethertype;
+    pad(2);
+};
+
+struct of_action_set_queue : of_action {
+    uint16_t type == 21;
+    uint16_t len;
+    uint32_t queue_id;
+};
+
+struct of_action_group : of_action {
+    uint16_t type == 22;
+    uint16_t len;
+    uint32_t group_id;
+};
+
+struct of_action_set_nw_ttl : of_action {
+    uint16_t type == 23;
+    uint16_t len;
+    uint8_t nw_ttl;
+    pad(3);
+};
+
+struct of_action_dec_nw_ttl : of_action {
+    uint16_t type == 24;
+    uint16_t len;
+    pad(4);
+};
+
+struct of_action_set_field(align=8, length_includes_align=True) : of_action {
+    uint16_t type == 25;
+    uint16_t len;
+    of_oxm_t field;
+};
+
+struct of_action_experimenter(align=8, length_includes_align=True): of_action {
+    uint16_t type == 65535;
+    uint16_t len;
+    uint32_t experimenter == ?;
+    of_octets_t data;
+};
+
+struct of_action_pop_pbb : of_action {
+    uint16_t type == 27;
+    uint16_t len;
+    pad(4);
+};
+
+struct of_action_push_pbb : of_action {
+    uint16_t type == 26;
+    uint16_t len;
+    uint16_t ethertype;
+    pad(2);
+};
+
+struct of_action {
+    uint16_t type == ?;
+    uint16_t len;
+    pad(4);
+};
+
+struct of_instruction_id {
+    uint16_t type;
+    uint16_t len;
+};
+
+struct of_instruction {
+    uint16_t type == ?;
+    uint16_t len;
+};
+
+struct of_instruction_goto_table : of_instruction {
+    uint16_t type == 1;
+    uint16_t len;
+    uint8_t table_id;
+    pad(3);
+};
+
+struct of_instruction_write_metadata : of_instruction {
+    uint16_t type == 2;
+    uint16_t len;
+    pad(4);
+    uint64_t metadata;
+    uint64_t metadata_mask;
+};
+
+struct of_instruction_write_actions : of_instruction {
+    uint16_t type == 3;
+    uint16_t len;
+    pad(4);
+    list(of_action_t) actions;
+};
+
+struct of_instruction_apply_actions : of_instruction {
+    uint16_t type == 4;
+    uint16_t len;
+    pad(4);
+    list(of_action_t) actions;
+};
+
+struct of_instruction_clear_actions : of_instruction {
+    uint16_t type == 5;
+    uint16_t len;
+    pad(4);
+};
+
+struct of_instruction_meter : of_instruction {
+    uint16_t type == 6;
+    uint16_t len;
+    uint32_t meter_id;
+};
+
+struct of_instruction_experimenter : of_instruction {
+    uint16_t type == 65535;
+    uint16_t len;
+    uint32_t experimenter == ?;
+    of_octets_t data;
+};
+
+struct of_flow_mod : of_header {
+    uint8_t version;
+    uint8_t type == 14;
+    uint16_t length;
+    uint32_t xid;
+    uint64_t cookie;
+    uint64_t cookie_mask;
+    uint8_t table_id;
+    of_fm_cmd_t _command == ?;
+    uint16_t idle_timeout;
+    uint16_t hard_timeout;
+    uint16_t priority;
+    uint32_t buffer_id;
+    of_port_no_t out_port;
+    uint32_t out_group;
+    enum ofp_flow_mod_flags flags;
+    pad(2);
+    of_match_t match;
+    list(of_instruction_t) instructions;
+};
+
+struct of_flow_add : of_flow_mod {
+    uint8_t version;
+    uint8_t type == 14;
+    uint16_t length;
+    uint32_t xid;
+    uint64_t cookie;
+    uint64_t cookie_mask;
+    uint8_t table_id;
+    of_fm_cmd_t _command == 0;
+    uint16_t idle_timeout;
+    uint16_t hard_timeout;
+    uint16_t priority;
+    uint32_t buffer_id;
+    of_port_no_t out_port;
+    uint32_t out_group;
+    enum ofp_flow_mod_flags flags;
+    uint16_t importance;
+    of_match_t match;
+    list(of_instruction_t) instructions;
+};
+
+struct of_flow_modify : of_flow_mod {
+    uint8_t version;
+    uint8_t type == 14;
+    uint16_t length;
+    uint32_t xid;
+    uint64_t cookie;
+    uint64_t cookie_mask;
+    uint8_t table_id;
+    of_fm_cmd_t _command == 1;
+    uint16_t idle_timeout;
+    uint16_t hard_timeout;
+    uint16_t priority;
+    uint32_t buffer_id;
+    of_port_no_t out_port;
+    uint32_t out_group;
+    enum ofp_flow_mod_flags flags;
+    uint16_t importance;
+    of_match_t match;
+    list(of_instruction_t) instructions;
+};
+
+struct of_flow_modify_strict : of_flow_mod {
+    uint8_t version;
+    uint8_t type == 14;
+    uint16_t length;
+    uint32_t xid;
+    uint64_t cookie;
+    uint64_t cookie_mask;
+    uint8_t table_id;
+    of_fm_cmd_t _command == 2;
+    uint16_t idle_timeout;
+    uint16_t hard_timeout;
+    uint16_t priority;
+    uint32_t buffer_id;
+    of_port_no_t out_port;
+    uint32_t out_group;
+    enum ofp_flow_mod_flags flags;
+    uint16_t importance;
+    of_match_t match;
+    list(of_instruction_t) instructions;
+};
+
+struct of_flow_delete : of_flow_mod {
+    uint8_t version;
+    uint8_t type == 14;
+    uint16_t length;
+    uint32_t xid;
+    uint64_t cookie;
+    uint64_t cookie_mask;
+    uint8_t table_id;
+    of_fm_cmd_t _command == 3;
+    uint16_t idle_timeout;
+    uint16_t hard_timeout;
+    uint16_t priority;
+    uint32_t buffer_id;
+    of_port_no_t out_port;
+    uint32_t out_group;
+    enum ofp_flow_mod_flags flags;
+    uint16_t importance;
+    of_match_t match;
+    list(of_instruction_t) instructions;
+};
+
+struct of_flow_delete_strict : of_flow_mod {
+    uint8_t version;
+    uint8_t type == 14;
+    uint16_t length;
+    uint32_t xid;
+    uint64_t cookie;
+    uint64_t cookie_mask;
+    uint8_t table_id;
+    of_fm_cmd_t _command == 4;
+    uint16_t idle_timeout;
+    uint16_t hard_timeout;
+    uint16_t priority;
+    uint32_t buffer_id;
+    of_port_no_t out_port;
+    uint32_t out_group;
+    enum ofp_flow_mod_flags flags;
+    uint16_t importance;
+    of_match_t match;
+    list(of_instruction_t) instructions;
+};
+
+struct of_bucket {
+    uint16_t len;
+    uint16_t weight;
+    of_port_no_t watch_port;
+    uint32_t watch_group;
+    pad(4);
+    list(of_action_t) actions;
+};
+
+struct of_group_mod : of_header {
+    uint8_t version;
+    uint8_t type == 15;
+    uint16_t length;
+    uint32_t xid;
+    enum ofp_group_mod_command command == ?;
+    enum ofp_group_type group_type;
+    pad(1);
+    uint32_t group_id;
+    list(of_bucket_t) buckets;
+};
+
+struct of_group_add : of_group_mod {
+    uint8_t version;
+    uint8_t type == 15;
+    uint16_t length;
+    uint32_t xid;
+    enum ofp_group_mod_command command == 0;
+    enum ofp_group_type group_type;
+    pad(1);
+    uint32_t group_id;
+    list(of_bucket_t) buckets;
+};
+
+struct of_group_modify : of_group_mod {
+    uint8_t version;
+    uint8_t type == 15;
+    uint16_t length;
+    uint32_t xid;
+    enum ofp_group_mod_command command == 1;
+    enum ofp_group_type group_type;
+    pad(1);
+    uint32_t group_id;
+    list(of_bucket_t) buckets;
+};
+
+struct of_group_delete : of_group_mod {
+    uint8_t version;
+    uint8_t type == 15;
+    uint16_t length;
+    uint32_t xid;
+    enum ofp_group_mod_command command == 2;
+    enum ofp_group_type group_type;
+    pad(1);
+    uint32_t group_id;
+    list(of_bucket_t) buckets;
+};
+
+struct of_packet_out : of_header {
+    uint8_t version;
+    uint8_t type == 13;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t buffer_id;
+    of_port_no_t in_port;
+    uint16_t actions_len;
+    pad(6);
+    list(of_action_t) actions;
+    of_octets_t data;
+};
+
+struct of_packet_in : of_header {
+    uint8_t version;
+    uint8_t type == 10;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t buffer_id;
+    uint16_t total_len;
+    uint8_t reason;
+    uint8_t table_id;
+    uint64_t cookie;
+    of_match_t match;
+    pad(2);
+    of_octets_t data;
+};
+
+struct of_flow_removed : of_header {
+    uint8_t version;
+    uint8_t type == 11;
+    uint16_t length;
+    uint32_t xid;
+    uint64_t cookie;
+    uint16_t priority;
+    uint8_t reason;
+    uint8_t table_id;
+    uint32_t duration_sec;
+    uint32_t duration_nsec;
+    uint16_t idle_timeout;
+    uint16_t hard_timeout;
+    uint64_t packet_count;
+    uint64_t byte_count;
+    of_match_t match;
+};
+
+struct of_meter_band {
+    uint16_t        type == ?;
+    uint16_t        len;
+//    uint32_t        rate;  // These are excluded b/c this is the header
+//    uint32_t        burst_size;  // These are excluded b/c this is the header
+};
+
+struct of_meter_band_drop : of_meter_band {
+    uint16_t        type == 1;
+    uint16_t        len;
+    uint32_t        rate;
+    uint32_t        burst_size;
+    pad(4);
+};
+
+struct of_meter_band_dscp_remark : of_meter_band {
+    uint16_t        type == 2;
+    uint16_t        len;
+    uint32_t        rate;
+    uint32_t        burst_size;
+    uint8_t         prec_level;
+    pad(3);
+};
+
+struct of_meter_band_experimenter : of_meter_band {
+    uint16_t        type == 65535;
+    uint16_t        len;
+    uint32_t        rate;
+    uint32_t        burst_size;
+    uint32_t        experimenter;
+};
+
+struct of_meter_mod : of_header {
+    uint8_t version;
+    uint8_t type == 29;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t command;
+    uint16_t flags;
+    uint32_t meter_id;
+    list(of_meter_band_t) bands;
+};
+
+struct of_error_msg : of_header {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == ?;
+};
+
+struct of_hello_failed_error_msg : of_error_msg {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == 0;
+    enum ofp_hello_failed_code code;
+    of_octets_t data;
+};
+
+struct of_bad_request_error_msg : of_error_msg {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == 1;
+    enum ofp_bad_request_code code;
+    of_octets_t data;
+};
+
+struct of_bad_action_error_msg : of_error_msg {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == 2;
+    enum ofp_bad_action_code code;
+    of_octets_t data;
+};
+
+struct of_bad_instruction_error_msg : of_error_msg {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == 3;
+    enum ofp_bad_instruction_code code;
+    of_octets_t data;
+};
+
+struct of_bad_match_error_msg : of_error_msg {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == 4;
+    enum ofp_bad_match_code code;
+    of_octets_t data;
+};
+
+struct of_flow_mod_failed_error_msg : of_error_msg {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == 5;
+    enum ofp_flow_mod_failed_code code;
+    of_octets_t data;
+};
+
+struct of_group_mod_failed_error_msg : of_error_msg {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == 6;
+    enum ofp_group_mod_failed_code code;
+    of_octets_t data;
+};
+
+struct of_port_mod_failed_error_msg : of_error_msg {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == 7;
+    enum ofp_port_mod_failed_code code;
+    of_octets_t data;
+};
+
+struct of_table_mod_failed_error_msg : of_error_msg {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == 8;
+    enum ofp_table_mod_failed_code code;
+    of_octets_t data;
+};
+
+struct of_queue_op_failed_error_msg : of_error_msg {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == 9;
+    enum ofp_queue_op_failed_code code;
+    of_octets_t data;
+};
+
+struct of_switch_config_failed_error_msg : of_error_msg {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == 10;
+    enum ofp_switch_config_failed_code code;
+    of_octets_t data;
+};
+
+struct of_role_request_failed_error_msg : of_error_msg {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == 11;
+    enum ofp_role_request_failed_code code;
+    of_octets_t data;
+};
+
+struct of_meter_mod_failed_error_msg : of_error_msg {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == 12;
+    enum ofp_meter_mod_failed_code code;
+    of_octets_t data;
+};
+
+struct of_table_features_failed_error_msg : of_error_msg {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == 13;
+    enum ofp_table_features_failed_code code;
+    of_octets_t data;
+};
+
+struct of_experimenter_error_msg : of_error_msg {
+    uint8_t version;
+    uint8_t type == 1;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t err_type == 0xffff;
+    uint16_t subtype;
+    uint32_t experimenter;
+    of_octets_t data;
+};
+
+// STATS ENTRIES: flow, table, port, queue, group stats, group desc stats
+
+struct of_flow_stats_entry {
+    uint16_t length;
+    uint8_t table_id;
+    pad(1);
+    uint32_t duration_sec;
+    uint32_t duration_nsec;
+    uint16_t priority;
+    uint16_t idle_timeout;
+    uint16_t hard_timeout;
+    enum ofp_flow_mod_flags flags;
+    uint16_t importance;
+    pad(2);
+    uint64_t cookie;
+    uint64_t packet_count;
+    uint64_t byte_count;
+    of_match_t match;
+    list(of_instruction_t) instructions;
+};
+
+
+struct of_table_stats_entry {
+    uint8_t table_id;
+    pad(3);
+    uint32_t active_count;
+    uint64_t lookup_count;
+    uint64_t matched_count;
+};
+
+/* Common header for all port stats properties. */
+struct of_port_stats_prop {
+    uint16_t         type == ?; /* One of OFPPSPT_*. */
+    uint16_t         length;  /* Length in bytes of this property. */
+};
+
+/* Ethernet port stats property. */
+struct of_port_stats_prop_ethernet : of_port_stats_prop {
+    uint16_t         type == 0; /* OFPPSPT_ETHERNET. */
+    uint16_t         length;  /* Length in bytes of this property. */
+    pad(4);
+
+    uint64_t rx_frame_err;   /* Number of frame alignment errors. */
+    uint64_t rx_over_err;    /* Number of packets with RX overrun. */
+    uint64_t rx_crc_err;     /* Number of CRC errors. */
+    uint64_t collisions;     /* Number of collisions. */
+};
+
+/* Optical port stats property. */
+struct of_port_stats_prop_optical : of_port_stats_prop {
+    uint16_t         type == 1;
+    uint16_t         length;
+    pad(4);
+
+    uint32_t flags;       
+    uint32_t tx_freq_lmda;
+    uint32_t tx_offset;
+    uint32_t tx_grid_span;
+    uint32_t rx_freq_lmda;
+    uint32_t rx_offset;
+    uint32_t rx_grid_span;
+    uint16_t tx_pwr;
+    uint16_t rx_pwr;
+    uint16_t bias_current;
+    uint16_t temperature;
+};
+
+/* Experimenter port stats property. */
+struct of_port_stats_prop_experimenter : of_port_stats_prop {
+    uint16_t         type == 0xffff;
+    uint16_t         length;
+    uint32_t         experimenter == ?;
+    uint32_t         exp_type;
+    of_octets_t      experimenter_data;
+};
+
+struct of_port_stats_entry {
+    uint16_t length;
+    pad(2);
+    of_port_no_t port_no;
+    uint32_t duration_sec;
+    uint32_t duration_nsec;
+    uint64_t rx_packets;
+    uint64_t tx_packets;
+    uint64_t rx_bytes;
+    uint64_t tx_bytes;
+    uint64_t rx_dropped;
+    uint64_t tx_dropped;
+    uint64_t rx_errors;
+    uint64_t tx_errors;
+    list(of_port_stats_prop_t) properties;
+};
+
+struct of_queue_stats_prop {
+    uint16_t         type == ?;
+    uint16_t         length;
+};
+
+struct of_queue_stats_prop_experimenter : of_queue_stats_prop {
+    uint16_t type == 0xffff;
+    uint16_t length;
+    uint32_t experimenter == ?;
+    uint32_t exp_type;
+};
+
+struct of_queue_stats_entry {
+    uint16_t length;
+    pad(6);
+    of_port_no_t port_no;
+    uint32_t queue_id;
+    uint64_t tx_bytes;
+    uint64_t tx_packets;
+    uint64_t tx_errors;
+    uint32_t duration_sec;
+    uint32_t duration_nsec;
+    list(of_queue_stats_prop_t) properties;
+};
+
+struct of_bucket_counter {
+    uint64_t packet_count;
+    uint64_t byte_count;
+};
+
+struct of_group_stats_entry {
+    uint16_t length;
+    pad(2);
+    uint32_t group_id;
+    uint32_t ref_count;
+    pad(4);
+    uint64_t packet_count;
+    uint64_t byte_count;
+    uint32_t duration_sec;
+    uint32_t duration_nsec;
+    list(of_bucket_counter_t) bucket_stats;
+};
+
+struct of_group_desc_stats_entry {
+    uint16_t length;
+    enum ofp_group_type group_type;
+    pad(1);
+    uint32_t group_id;
+    list(of_bucket_t) buckets;
+};
+
+// STATS:
+//  Desc, flow, agg, table, port, queue, group, group_desc, group_feat, experi
+
+struct of_stats_request : of_header {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == ?;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+};
+
+struct of_stats_reply : of_header {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == ?;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+};
+
+struct of_desc_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 0;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+};
+
+struct of_desc_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 0;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    of_desc_str_t mfr_desc;
+    of_desc_str_t hw_desc;
+    of_desc_str_t sw_desc;
+    of_serial_num_t serial_num;
+    of_desc_str_t dp_desc;
+};
+
+struct of_flow_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 1;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+    uint8_t table_id;
+    pad(3);
+    of_port_no_t out_port;
+    uint32_t out_group;
+    pad(4);
+    uint64_t cookie;
+    uint64_t cookie_mask;
+    of_match_t match;
+};
+
+struct of_flow_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 1;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    list(of_flow_stats_entry_t) entries;
+};
+
+struct of_aggregate_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 2;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+    uint8_t table_id;
+    pad(3);
+    of_port_no_t out_port;
+    uint32_t out_group;
+    pad(4);
+    uint64_t cookie;
+    uint64_t cookie_mask;
+    of_match_t match;
+};
+
+struct of_aggregate_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 2;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    uint64_t packet_count;
+    uint64_t byte_count;
+    uint32_t flow_count;
+    pad(4);
+};
+
+struct of_table_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 3;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+};
+
+struct of_table_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 3;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    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 {
+    uint16_t         type == ?;
+    uint16_t         length;
+};
+
+struct of_table_feature_prop_instructions : of_table_feature_prop {
+    uint16_t         type == 0;
+    uint16_t         length;
+    // FIXME Check if instruction_t is right for ids here
+    list(of_instruction_id_t)   instruction_ids;
+};
+
+struct of_table_feature_prop_instructions_miss : of_table_feature_prop {
+    uint16_t         type == 1;
+    uint16_t         length;
+    list(of_instruction_id_t)   instruction_ids;
+};
+
+struct of_table_feature_prop_next_tables : of_table_feature_prop {
+    uint16_t         type == 2;
+    uint16_t         length;
+    list(of_uint8_t) next_table_ids;
+};
+
+struct of_table_feature_prop_next_tables_miss : of_table_feature_prop {
+    uint16_t         type == 3;
+    uint16_t         length;
+    list(of_uint8_t) next_table_ids;
+};
+
+struct of_table_feature_prop_write_actions : of_table_feature_prop {
+    uint16_t         type == 4;
+    uint16_t         length;
+    list(of_action_id_t) action_ids;
+};
+
+struct of_table_feature_prop_write_actions_miss : of_table_feature_prop {
+    uint16_t         type == 5;
+    uint16_t         length;
+    list(of_action_id_t) action_ids;
+};
+
+struct of_table_feature_prop_apply_actions : of_table_feature_prop {
+    uint16_t         type == 6;
+    uint16_t         length;
+    list(of_action_id_t) action_ids;
+};
+
+struct of_table_feature_prop_apply_actions_miss : of_table_feature_prop {
+    uint16_t         type == 7;
+    uint16_t         length;
+    list(of_action_id_t) action_ids;
+};
+
+struct of_table_feature_prop_match : of_table_feature_prop {
+    uint16_t         type == 8;
+    uint16_t         length;
+    list(of_uint32_t) oxm_ids;
+};
+
+struct of_table_feature_prop_wildcards : of_table_feature_prop {
+    uint16_t         type == 10;
+    uint16_t         length;
+    list(of_uint32_t) oxm_ids;
+};
+
+struct of_table_feature_prop_write_setfield : of_table_feature_prop {
+    uint16_t         type == 12;
+    uint16_t         length;
+    list(of_uint32_t) oxm_ids;
+};
+
+struct of_table_feature_prop_write_setfield_miss : of_table_feature_prop {
+    uint16_t         type == 13;
+    uint16_t         length;
+    list(of_uint32_t) oxm_ids;
+};
+
+struct of_table_feature_prop_apply_setfield : of_table_feature_prop {
+    uint16_t         type == 14;
+    uint16_t         length;
+    list(of_uint32_t) oxm_ids;
+};
+
+struct of_table_feature_prop_apply_setfield_miss : of_table_feature_prop {
+    uint16_t         type == 15;
+    uint16_t         length;
+    list(of_uint32_t) oxm_ids;
+};
+
+struct of_table_feature_prop_table_sync_from : of_table_feature_prop {
+    uint16_t         type == 16;
+    uint16_t         length;
+    list(of_uint8_t) table_ids;
+};
+
+struct of_table_feature_prop_experimenter : of_table_feature_prop {
+    uint16_t         type == 65534;
+    uint16_t         length;
+    uint32_t         experimenter == ?;
+    uint32_t         subtype;
+    of_octets_t      experimenter_data;
+};
+
+struct of_table_feature_prop_experimenter_miss : of_table_feature_prop {
+    uint16_t         type == 65535;
+    uint16_t         length;
+    uint32_t         experimenter == ?;
+    uint32_t         subtype;
+    of_octets_t      experimenter_data;
+};
+
+struct of_table_features {
+    uint16_t length;
+    uint8_t table_id;
+    pad(5);
+    of_table_name_t name;
+    uint64_t metadata_match;
+    uint64_t metadata_write;
+    uint32_t config;
+    uint32_t max_entries;
+    list(of_table_feature_prop_t) properties;
+};
+
+struct of_meter_features {
+    uint32_t    max_meter;
+    uint32_t    band_types;
+    uint32_t    capabilities;
+    uint8_t     max_bands;
+    uint8_t     max_color;
+    pad(2);
+};
+
+struct of_port_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 4;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+    of_port_no_t port_no;
+    pad(4);
+};
+
+struct of_port_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 4;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    list(of_port_stats_entry_t) entries;
+};
+
+struct of_queue_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 5;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+    of_port_no_t port_no;
+    uint32_t queue_id;
+};
+
+struct of_queue_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 5;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    list(of_queue_stats_entry_t) entries;
+};
+
+struct of_group_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 6;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+    uint32_t group_id;
+    pad(4);
+};
+
+struct of_group_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 6;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    list(of_group_stats_entry_t) entries;
+};
+
+struct of_group_desc_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 7;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+};
+
+struct of_group_desc_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 7;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    list(of_group_desc_stats_entry_t) entries;
+};
+
+struct of_group_features_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 8;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+};
+
+struct of_group_features_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 8;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    uint32_t types;
+    uint32_t capabilities;
+    uint32_t max_groups_all;
+    uint32_t max_groups_select;
+    uint32_t max_groups_indirect;
+    uint32_t max_groups_ff;
+    uint32_t actions_all;
+    uint32_t actions_select;
+    uint32_t actions_indirect;
+    uint32_t actions_ff;
+};
+
+struct of_meter_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 9;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+    uint32_t meter_id;
+    pad(4);
+};
+
+struct of_meter_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 9;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    list(of_meter_stats_t) entries;
+};
+
+struct of_meter_config_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 10;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+    uint32_t meter_id;
+    pad(4);
+};
+
+struct of_meter_config_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 10;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    list(of_meter_band_t) entries;
+};
+
+// FIXME stats added to get things working
+struct of_meter_features_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 11;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+};
+
+// FIXME stats added to get things working
+struct of_meter_features_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 11;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    of_meter_features_t features;
+};
+
+// FIXME stats added to get things working
+struct of_table_features_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 12;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+    list(of_table_features_t) entries;
+};
+
+// FIXME stats added to get things working
+struct of_table_features_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 12;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    list(of_table_features_t) entries;
+};
+
+// FIXME stats added to get things working
+struct of_port_desc_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 13;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+};
+
+// FIXME stats added to get things working
+struct of_port_desc_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 13;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    list(of_port_desc_t) entries;
+};
+
+struct of_table_desc_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 14;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+};
+
+struct of_table_desc {
+    uint16_t length;
+    uint8_t table_id;
+    pad(1);
+    enum ofp_table_config config;
+};
+
+struct of_table_desc_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 14;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    list(of_table_desc_t) entries;
+};
+
+struct of_queue_desc_stats_request : of_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 15;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+};
+
+struct of_queue_desc_prop {
+    uint16_t type == ?;
+    uint16_t length;
+};
+
+struct of_queue_desc_prop_min_rate : of_queue_desc_prop {
+    uint16_t type == 1;
+    uint16_t length;
+    uint16_t rate;
+    pad(2);
+};
+
+struct of_queue_desc_prop_max_rate : of_queue_desc_prop {
+    uint16_t type == 2;
+    uint16_t length;
+    uint16_t rate;
+    pad(2);
+};
+
+struct of_queue_desc_prop_experimenter : of_queue_desc_prop {
+    uint16_t type == 0xffff;
+    uint16_t length;
+    uint32_t experimenter == ?;
+    uint32_t exp_type;
+};
+
+struct of_queue_desc {
+    uint32_t port_no;
+    uint32_t queue_id;
+    uint16_t length;
+    pad(6);
+    list(of_queue_desc_prop_t) properties;
+};
+
+struct of_queue_desc_stats_reply : of_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 15;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    list(of_queue_desc_t) entries;
+};
+
+/*
+ * Not supporting the flow monitor multipart messages. This message is
+ * poorly designed because it includes a variable length match inside a
+ * struct (ofp_flow_monitor_request) with no explicit length member.
+ * I'm not writing the special case code to figure out the total length.
+ */
+
+struct of_meter_band_stats {
+    uint64_t        packet_band_count;
+    uint64_t        byte_band_count;
+};
+
+struct of_meter_stats {
+    uint32_t        meter_id;
+    uint16_t        len;
+    pad(6);
+    uint32_t        flow_count;
+    uint64_t        packet_in_count;
+    uint64_t        byte_in_count;
+    uint32_t   duration_sec;
+    uint32_t   duration_nsec;
+    list(of_meter_band_stats_t) band_stats;
+};
+
+struct of_meter_config {
+    uint16_t        length;
+    uint16_t        flags;
+    uint32_t        meter_id;
+    list(of_meter_band_t) entries;
+};
+
+// END OF STATS OBJECTS
+
+struct of_queue_prop {
+    uint16_t type == ?;
+    uint16_t len;
+    pad(4);
+};
+
+struct of_queue_prop_min_rate : of_queue_prop {
+    uint16_t type == 1;
+    uint16_t len;
+    pad(4);
+    uint16_t rate;
+    pad(6);
+};
+
+struct of_queue_prop_max_rate : of_queue_prop {
+    uint16_t type == 2;
+    uint16_t len;
+    pad(4);
+    uint16_t rate;
+    pad(6);
+};
+
+struct of_queue_prop_experimenter : of_queue_prop {
+    uint16_t type == 65535;
+    uint16_t len;
+    pad(4);
+    uint32_t experimenter == ?;
+    pad(4);
+    of_octets_t data;
+};
+
+struct of_packet_queue {
+    uint32_t queue_id;
+    of_port_no_t port;
+    uint16_t len;
+    pad(6);
+    list(of_queue_prop_t) properties;
+};
+
+struct of_role_request : of_header {
+    uint8_t version;
+    uint8_t type == 24;
+    uint16_t length;
+    uint32_t xid;
+    enum ofp_controller_role role;
+    pad(4);
+    uint64_t generation_id;
+};
+
+struct of_role_reply : of_header {
+    uint8_t version;
+    uint8_t type == 25;
+    uint16_t length;
+    uint32_t xid;
+    enum ofp_controller_role role;
+    pad(4);
+    uint64_t generation_id;
+};
+
+/* Bundle messages */
+
+struct of_bundle_prop {
+    uint16_t type == ?;
+    uint16_t length;
+};
+
+struct of_bundle_prop_experimenter : of_bundle_prop {
+    uint16_t type == 0xffff;
+    uint16_t length;
+    uint32_t experimenter == ?;
+    uint32_t exp_type;
+};
+
+struct of_bundle_ctrl_msg : of_header {
+    uint8_t version;
+    uint8_t type == 33;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t bundle_id;
+    enum ofp_bundle_ctrl_type bundle_ctrl_type;
+    enum ofp_bundle_flags flags;
+    list(of_bundle_prop_t) properties;
+};
+
+struct of_bundle_add_msg : of_header {
+    uint8_t version;
+    uint8_t type == 34;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t bundle_id;
+    pad(2);
+    enum ofp_bundle_flags flags;
+    // TODO support embedding of_header
+    of_octets_t data;
+    // TODO support trailing properties
+};
+
+/* Async config messages */
+
+struct of_async_config_prop {
+    uint16_t type == ?;
+    uint16_t length;
+};
+
+struct of_async_config_prop_packet_in_slave : of_async_config_prop {
+    uint16_t type == 0;
+    uint16_t length;
+    uint32_t mask;
+};
+
+struct of_async_config_prop_packet_in_master : of_async_config_prop {
+    uint16_t type == 1;
+    uint16_t length;
+    uint32_t mask;
+};
+
+struct of_async_config_prop_port_status_slave : of_async_config_prop {
+    uint16_t type == 2;
+    uint16_t length;
+    uint32_t mask;
+};
+
+struct of_async_config_prop_port_status_master : of_async_config_prop {
+    uint16_t type == 3;
+    uint16_t length;
+    uint32_t mask;
+};
+
+struct of_async_config_prop_flow_removed_slave : of_async_config_prop {
+    uint16_t type == 4;
+    uint16_t length;
+    uint32_t mask;
+};
+
+struct of_async_config_prop_flow_removed_master : of_async_config_prop {
+    uint16_t type == 5;
+    uint16_t length;
+    uint32_t mask;
+};
+
+struct of_async_config_prop_role_status_slave : of_async_config_prop {
+    uint16_t type == 6;
+    uint16_t length;
+    uint32_t mask;
+};
+
+struct of_async_config_prop_role_status_master : of_async_config_prop {
+    uint16_t type == 7;
+    uint16_t length;
+    uint32_t mask;
+};
+
+struct of_async_config_prop_table_status_slave : of_async_config_prop {
+    uint16_t type == 8;
+    uint16_t length;
+    uint32_t mask;
+};
+
+struct of_async_config_prop_table_status_master : of_async_config_prop {
+    uint16_t type == 9;
+    uint16_t length;
+    uint32_t mask;
+};
+
+struct of_async_config_prop_requestforward_slave : of_async_config_prop {
+    uint16_t type == 10;
+    uint16_t length;
+    uint32_t mask;
+};
+
+struct of_async_config_prop_requestforward_master : of_async_config_prop {
+    uint16_t type == 11;
+    uint16_t length;
+    uint32_t mask;
+};
+
+struct of_async_config_prop_experimenter_slave : of_async_config_prop {
+    uint16_t type == 0xfffe;
+    uint16_t length;
+};
+
+struct of_async_config_prop_experimenter_master : of_async_config_prop {
+    uint16_t type == 0xffff;
+    uint16_t length;
+};
+
+struct of_async_get_request : of_header {
+    uint8_t version;
+    uint8_t type == 26;
+    uint16_t length;
+    uint32_t xid;
+    list(of_async_config_prop_t) properties;
+};
+
+struct of_async_get_reply : of_header {
+    uint8_t version;
+    uint8_t type == 27;
+    uint16_t length;
+    uint32_t xid;
+    list(of_async_config_prop_t) properties;
+};
+
+struct of_async_set : of_header {
+    uint8_t version;
+    uint8_t type == 28;
+    uint16_t length;
+    uint32_t xid;
+    list(of_async_config_prop_t) properties;
+};
+
+/* Role status message */
+
+struct of_role_prop {
+    uint16_t type == ?;
+    uint16_t length;
+};
+
+struct of_role_prop_experimenter : of_role_prop {
+    uint16_t type == 0xffff;
+    uint16_t length;
+    uint32_t experimenter == ?;
+    uint32_t exp_type;
+};
+
+struct of_role_status : of_header {
+    uint8_t version;
+    uint8_t type == 30;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t role;
+    enum ofp_controller_role_reason reason;
+    pad(3);
+    uint64_t generation_id;
+    list(of_role_prop_t) properties;
+};
+
+/* Table status messages */
+
+struct of_table_status : of_header {
+    uint8_t version;
+    uint8_t type == 31;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t role;
+    enum ofp_table_reason reason;
+    pad(7);
+    of_table_desc_t table;
+};
+
+/* Request forward message */
+
+struct of_requestforward : of_header {
+    uint8_t version;
+    uint8_t type == 32;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t role;
+    // TODO support embedding of_header
+    of_octets_t data;
+};
diff --git a/py_gen/codegen.py b/py_gen/codegen.py
index 3b0988e..f8a7211 100644
--- a/py_gen/codegen.py
+++ b/py_gen/codegen.py
@@ -43,6 +43,18 @@
     'of_instruction_id': 'instruction_id',
     'of_meter_band': 'meter_band',
     'of_bsn_tlv': 'bsn_tlv',
+    'of_port_desc_prop': 'port_desc_prop',
+    'of_port_stats_prop': 'port_stats_prop',
+    'of_port_mod_prop': 'port_mod_prop',
+    'of_table_mod_prop': 'table_mod_prop',
+    'of_queue_desc_prop': 'queue_desc_prop',
+    'of_queue_stats_prop': 'queue_stats_prop',
+    'of_async_config_prop': 'async_config_prop',
+    'of_bundle_prop': 'bundle_prop',
+    #'of_queue_prop': 'queue_prop',
+    'of_role_prop': 'role_prop',
+    #'of_table_feature_prop': 'table_feature_prop',
+    'of_table_mod_prop': 'table_mod_prop',
 }
 
 # Return the module and class names for the generated Python class
diff --git a/py_gen/oftype.py b/py_gen/oftype.py
index 7bd242e..51b8e13 100644
--- a/py_gen/oftype.py
+++ b/py_gen/oftype.py
@@ -143,6 +143,7 @@
     'of_port_desc_t': 'common.port_desc',
     'of_meter_features_t': 'common.meter_features',
     'of_bsn_vport_t': 'common.bsn_vport',
+    'of_table_desc_t': 'common.table_desc',
 }
 
 for (cls, pyclass) in embedded_structs.items():
@@ -190,6 +191,7 @@
     elif oftype_is_list(oftype):
         ofproto = loxi_globals.ir[version]
         ofclass = ofproto.class_by_name(oftype_list_elem(oftype))
+        assert ofclass, "No class named %r" % oftype_list_elem(oftype)
         module_name, class_name = py_gen.codegen.generate_pyname(ofclass)
         return 'loxi.generic_util.unpack_list(%s, %s.%s.unpack)' % \
             (reader_expr, module_name, class_name)
diff --git a/py_gen/templates/_common_extra.py b/py_gen/templates/_common_extra.py
index 8639dc4..f801965 100644
--- a/py_gen/templates/_common_extra.py
+++ b/py_gen/templates/_common_extra.py
@@ -35,4 +35,7 @@
 :: elif version == OFVersions.VERSION_1_3:
 :: # HACK
 match = match_v3
+:: elif version == OFVersions.VERSION_1_4:
+:: # HACK
+match = match_v3
 :: #endif
diff --git a/py_gen/templates/toplevel_init.py b/py_gen/templates/toplevel_init.py
index e5493a5..0393169 100644
--- a/py_gen/templates/toplevel_init.py
+++ b/py_gen/templates/toplevel_init.py
@@ -39,20 +39,13 @@
     """
     Import and return the protocol module for the given wire version.
     """
-    if ver == 1:
-        import of10
-        return of10
-    elif ver == 2:
-        import of11
-        return of11
-    elif ver == 3:
-        import of12
-        return of12
-    elif ver == 4:
-        import of13
-        return of13
-    else:
-        raise ValueError
+:: for v in loxi_globals.OFVersions.all_supported:
+    if ver == ${v.wire_version}:
+        import of${v.version.replace('.', '')}
+        return of${v.version.replace('.', '')}
+
+:: #endfor
+    raise ValueError
 
 class ProtocolError(Exception):
     """
diff --git a/py_gen/tests/of14.py b/py_gen/tests/of14.py
new file mode 100644
index 0000000..3fdfb8e
--- /dev/null
+++ b/py_gen/tests/of14.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+# Copyright 2014, 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 2014, 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.
+import unittest
+from testutil import test_serialization
+from testutil import add_datafiles_tests
+
+try:
+    import loxi
+    import loxi.of14 as ofp
+    from loxi.generic_util import OFReader
+except ImportError:
+    exit("loxi package not found. Try setting PYTHONPATH.")
+
+class TestImports(unittest.TestCase):
+    def test_toplevel(self):
+        import loxi
+        self.assertTrue(hasattr(loxi, "ProtocolError"))
+        self.assertEquals(loxi.version_names[5], "1.4")
+        ofp = loxi.protocol(5)
+        self.assertEquals(ofp.OFP_VERSION, 5)
+        self.assertTrue(hasattr(ofp, "action"))
+        self.assertTrue(hasattr(ofp, "common"))
+        self.assertTrue(hasattr(ofp, "const"))
+        self.assertTrue(hasattr(ofp, "message"))
+        self.assertTrue(hasattr(ofp, "oxm"))
+
+    def test_version(self):
+        import loxi
+        self.assertTrue(hasattr(loxi.of14, "ProtocolError"))
+        self.assertTrue(hasattr(loxi.of14, "OFP_VERSION"))
+        self.assertEquals(loxi.of14.OFP_VERSION, 5)
+        self.assertTrue(hasattr(loxi.of14, "action"))
+        self.assertTrue(hasattr(loxi.of14, "common"))
+        self.assertTrue(hasattr(loxi.of14, "const"))
+        self.assertTrue(hasattr(loxi.of14, "message"))
+        self.assertTrue(hasattr(loxi.of14, "oxm"))
+
+# The majority of the serialization tests are created here using the files in
+# the test_data directory.
+class TestDataFiles(unittest.TestCase):
+    pass
+add_datafiles_tests(TestDataFiles, 'of14/', ofp)
+
+class TestAllof14(unittest.TestCase):
+    """
+    Round-trips every class through serialization/deserialization.
+    Not a replacement for handcoded tests because it only uses the
+    default member values.
+    """
+
+    def setUp(self):
+        mods = [ofp.action,ofp.message,ofp.common,ofp.oxm]
+        self.klasses = [klass for mod in mods
+                              for klass in mod.__dict__.values()
+                              if isinstance(klass, type) and
+                                 issubclass(klass, loxi.OFObject) and
+                                 not hasattr(klass, 'subtypes')]
+        self.klasses.sort(key=lambda x: str(x))
+
+    def test_serialization(self):
+        expected_failures = [
+            ofp.action.set_field, # field defaults to None
+        ]
+        for klass in self.klasses:
+            def fn():
+                obj = klass()
+                if hasattr(obj, "xid"): obj.xid = 42
+                buf = obj.pack()
+                obj2 = klass.unpack(OFReader(buf))
+                self.assertEquals(obj, obj2)
+            if klass in expected_failures:
+                self.assertRaises(Exception, fn)
+            else:
+                fn()
+
+    def test_parse_message(self):
+        expected_failures = [
+        ]
+        for klass in self.klasses:
+            if not issubclass(klass, ofp.message.message):
+                continue
+            def fn():
+                obj = klass(xid=42)
+                buf = obj.pack()
+                obj2 = ofp.message.parse_message(buf)
+                self.assertEquals(obj, obj2)
+            if klass in expected_failures:
+                self.assertRaises(Exception, fn)
+            else:
+                fn()
+
+    def test_show(self):
+        expected_failures = []
+        for klass in self.klasses:
+            def fn():
+                obj = klass()
+                if hasattr(obj, "xid"): obj.xid = 42
+                obj.show()
+            if klass in expected_failures:
+                self.assertRaises(Exception, fn)
+            else:
+                fn()
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/test_data/of13/port_status.data b/test_data/of13/port_status.data
index 10f7c16..e56705f 100644
--- a/test_data/of13/port_status.data
+++ b/test_data/of13/port_status.data
@@ -34,3 +34,24 @@
         peer=ofp.OFPPF_100MB_FD,
         curr_speed=10,
         max_speed=20))
+-- c
+obj = of_port_status_new(OF_VERSION_1_3);
+of_port_status_xid_set(obj, 0x12345678);
+of_port_status_reason_set(obj, OF_PORT_CHANGE_REASON_MODIFY);
+{
+    of_object_t desc;
+    of_port_status_desc_bind(obj, &desc);
+    of_port_desc_port_no_set(&desc, 4);
+    of_mac_addr_t hw_addr = { { 1, 2, 3, 4, 5, 6 } };
+    of_port_desc_hw_addr_set(&desc, hw_addr);
+    of_port_name_t name = "foo";
+    of_port_desc_name_set(&desc, name);
+    of_port_desc_config_set(&desc, OF_PORT_CONFIG_FLAG_NO_FWD|OF_PORT_CONFIG_FLAG_NO_RECV);
+    of_port_desc_state_set(&desc, OF_PORT_STATE_FLAG_BLOCKED);
+    of_port_desc_curr_set(&desc, OF_PORT_FEATURE_FLAG_10MB_HD);
+    of_port_desc_advertised_set(&desc, OF_PORT_FEATURE_FLAG_10MB_FD);
+    of_port_desc_supported_set(&desc, OF_PORT_FEATURE_FLAG_100MB_HD);
+    of_port_desc_peer_set(&desc, OF_PORT_FEATURE_FLAG_100MB_FD);
+    of_port_desc_curr_speed_set(&desc, 10);
+    of_port_desc_max_speed_set(&desc, 20);
+}
diff --git a/test_data/of14/flow_add.data b/test_data/of14/flow_add.data
new file mode 100644
index 0000000..88cf939
--- /dev/null
+++ b/test_data/of14/flow_add.data
@@ -0,0 +1,104 @@
+-- binary
+05 0e # version, type
+00 80 # length
+12 34 56 78 # xid
+
+fe dc ba 98 76 54 32 10 # cookie
+
+ff 00 ff 00 ff 00 ff 00 # cookie_mask
+
+03 # table_id
+00 # _command
+00 05 # idle_timeout
+00 0a # hard_timeout
+17 70 # priority
+
+00 00 00 32 # buffer_id
+00 00 00 06 # out_port
+
+00 00 00 08 # out_group
+00 00 # flags
+aa bb # importance
+
+00 01 # match.type
+00 3F # match.length # 59 bytes OXMs + 4 bytes match header
+
+80 00 01 08 # match.oxm_list[0].type_len - IN_PORT
+00 00 00 04 # match.oxm_list[0].value
+00 00 00 05 # match.oxm_list[0].mask
+
+80 00 0A 02 # match.oxm_list[1].type_len - ETH_TYPE
+86 DD # match.oxm_list[1].value - ETH_TYPE = IPv6
+
+80 00 14 01 # match.oxm_list[2].type_len - IP Proto
+06 # match.oxm_list[2].value = IP_PROTO = TCP
+
+80 00 35 20 # match.oxm_list[3].type_len - IPV6_SRC
+1C CA FE 1C B1 10 1C 00 00 28 00 00 00 00 00 00 # match.oxm_list[3].value
+FF FF FF FF FF F0 FF FF 1C 2C 3C 00 00 00 00 00 # match.oxm_list[3].mask
+
+00 # match.pad
+
+00 01 # instructions[0].type
+00 08 # instructions[0].length
+04 # instructions[0].table_id
+00 00 00 # pad
+
+00 01 # instructions[1].type
+00 08 # instructions[1].length
+07 # instructions[1].table_id
+00 00 00 # pad
+-- python
+ofp.message.flow_add(
+    xid=0x12345678,
+    cookie=0xFEDCBA9876543210,
+    cookie_mask=0xFF00FF00FF00FF00,
+    table_id=3,
+    idle_timeout=5,
+    hard_timeout=10,
+    priority=6000,
+    buffer_id=50,
+    out_port=6,
+    out_group=8,
+    flags=0,
+    importance=0xaabb,
+    match=ofp.match(oxm_list=[
+        ofp.oxm.in_port_masked(value=4, value_mask=5),
+        ofp.oxm.eth_type(value=0x86dd),
+        ofp.oxm.ip_proto(value=6),
+        ofp.oxm.ipv6_src_masked(
+            value     ='\x1C\xCA\xFE\x1C\xB1\x10\x1C\x00\x00\x28\x00\x00\x00\x00\x00\x00',
+            value_mask='\xFF\xFF\xFF\xFF\xFF\xF0\xFF\xFF\x1C\x2C\x3C\x00\x00\x00\x00\x00')
+        ]),
+    instructions=[
+        ofp.instruction.goto_table(table_id=4),
+        ofp.instruction.goto_table(table_id=7)])
+-- java
+builder.setXid(0x12345678)
+    .setCookie(U64.parseHex("FEDCBA9876543210"))
+    .setCookieMask(U64.parseHex("FF00FF00FF00FF00"))
+    .setTableId(TableId.of(3))
+    .setIdleTimeout(5)
+    .setHardTimeout(10)
+    .setPriority(6000)
+    .setBufferId(OFBufferId.of(50))
+    .setOutPort(OFPort.of(6))
+    .setOutGroup(OFGroup.of(8))
+    .setFlags(ImmutableSet.<OFFlowModFlags>of())
+    .setImportance(0xaabb)
+    .setMatch(
+        factory.buildMatch()
+            .setMasked(MatchField.IN_PORT, OFPort.of(4), OFPort.of(5))
+            .setExact(MatchField.ETH_TYPE, EthType.IPv6)
+            .setExact(MatchField.IP_PROTO, IpProtocol.TCP)
+            .setMasked(MatchField.IPV6_SRC, 
+                       IPv6Address.of(0x1CCAFE1CB1101C00l, 0x0028000000000000l),
+                       IPv6Address.of(0xFFFFFFFFFFF0FFFFl, 0x1C2C3C0000000000l))
+        	.build()
+    )
+    .setInstructions(
+        ImmutableList.<OFInstruction>of(
+                factory.instructions().gotoTable(TableId.of(4)),
+                factory.instructions().gotoTable(TableId.of(7))
+        )
+    );
diff --git a/test_data/of14/port_desc_stats_reply.data b/test_data/of14/port_desc_stats_reply.data
new file mode 100644
index 0000000..8e38975
--- /dev/null
+++ b/test_data/of14/port_desc_stats_reply.data
@@ -0,0 +1,54 @@
+-- binary
+05 13 # version/type
+00 58 # length
+00 00 00 05 # xid
+00 0d # stats_type
+00 00 # flags
+00 00 00 00 # pad
+00 00 00 01 # entries[0].port_no
+00 48 # entries[0].length
+00 00 # pad
+01 02 03 04 05 06 # entries[0].hw_addr
+00 00 # pad
+69 6e 74 65 72 66 61 63 65 31 32 33 34 35 36 37 # entries[0].name
+00 00 00 60 # entries[0].config
+00 00 00 03 # entries[0].state
+00 00 # entries[0].properties[0].type (ethernet)
+00 20 # entries[0].properties[0].length 
+00 00 00 00 # pad
+00 00 00 70 # entries[0].properties[0].curr
+00 00 00 00 # entries[0].properties[0].advertised
+00 00 00 00 # entries[0].properties[0].supported
+00 00 00 00 # entries[0].properties[0].peer
+00 00 00 00 # entries[0].properties[0].curr_speed
+00 00 00 80 # entries[0].properties[0].max_speed
+-- python
+ofp.message.port_desc_stats_reply(
+    xid=5, flags=0, entries=[
+        ofp.port_desc(
+            port_no=1,
+            hw_addr=[1,2,3,4,5,6],
+            name="interface1234567",
+            config=0x60,
+            state=0x03,
+            properties=[
+                ofp.port_desc_prop.ethernet(
+                    curr=0x70,
+                    max_speed=0x80)])])
+-- java
+builder
+        .setXid(5)
+        .setFlags(ImmutableSet.<OFStatsReplyFlags>of())
+        .setEntries(ImmutableList.of(
+                factory.buildPortDesc()
+                   .setPortNo(OFPort.of(1))
+                   .setHwAddr(MacAddress.of("01:02:03:04:05:06"))
+                   .setName("interface1234567")
+                   .setConfig(Sets.immutableEnumSet(OFPortConfig.NO_FWD, OFPortConfig.NO_PACKET_IN))
+                   .setState(Sets.immutableEnumSet(OFPortState.LINK_DOWN, OFPortState.BLOCKED))
+                   .setProperties(ImmutableList.<OFPortDescProp>of(factory.buildPortDescPropEthernet()
+                                                         .setCurr(0x70)
+                                                         .setMaxSpeed(0x80)
+                                                         .build()))
+                   .build())
+         );
diff --git a/test_data/of14/port_stats_reply.data b/test_data/of14/port_stats_reply.data
new file mode 100644
index 0000000..0ff4902
--- /dev/null
+++ b/test_data/of14/port_stats_reply.data
@@ -0,0 +1,104 @@
+-- binary
+05 13 # version/type
+00 d8 # length
+00 00 00 05 # xid
+00 04 # stats_type
+00 00 # flags
+00 00 00 00 # pad
+00 78 # entries[0].length
+00 00 # pad
+00 00 00 01 # entries[0].port_no
+00 00 00 00 # entries[0].duration_sec
+00 00 00 00 # entries[0].duration_nsec
+00 00 00 00 00 00 00 01 # entries[0].rx_packets
+00 00 00 00 00 00 00 00 # entries[0].tx_packets
+00 00 00 00 00 00 00 00 # entries[0].rx_bytes
+00 00 00 00 00 00 00 00 # entries[0].tx_bytes
+00 00 00 00 00 00 00 00 # entries[0].rx_dropped
+00 00 00 00 00 00 00 00 # entries[0].tx_dropped
+00 00 00 00 00 00 00 00 # entries[0].rx_errors
+00 00 00 00 00 00 00 02 # entries[0].tx_errors
+00 00 # entries[0].properties[0].type (ethernet)
+00 28 # entries[0].properties[0].length 
+00 00 00 00 # pad
+00 00 00 00 00 00 00 01 # entries[0].properties[0].rx_frame_err
+00 00 00 00 00 00 00 02 # entries[0].properties[0].rx_over_err
+00 00 00 00 00 00 00 03 # entries[0].properties[0].rx_crc_err
+00 00 00 00 00 00 00 04 # entries[0].properties[0].collisions
+00 50 # entries[1].length
+00 00 # pad
+ff ff ff fe # entries[1].port_no
+00 00 00 00 # entries[1].duration_sec
+00 00 00 00 # entries[1].duration_nsec
+00 00 00 00 00 00 00 03 # entries[1].rx_packets
+00 00 00 00 00 00 00 00 # entries[1].tx_packets
+00 00 00 00 00 00 00 00 # entries[1].rx_bytes
+00 00 00 00 00 00 00 00 # entries[1].tx_bytes
+00 00 00 00 00 00 00 00 # entries[1].rx_dropped
+00 00 00 00 00 00 00 00 # entries[1].tx_dropped
+00 00 00 00 00 00 00 00 # entries[1].rx_errors
+00 00 00 00 00 00 00 04 # entries[1].tx_errors
+-- python
+ofp.message.port_stats_reply(
+    xid=5, flags=0, entries=[
+        ofp.port_stats_entry(port_no=1, rx_packets=1, tx_errors=2,
+            properties=[
+                ofp.port_stats_prop.ethernet(
+                    rx_frame_err=1,
+                    rx_over_err=2,
+                    rx_crc_err=3,
+                    collisions=4)]),
+        ofp.port_stats_entry(port_no=ofp.OFPP_LOCAL, rx_packets=3, tx_errors=4)])
+-- c
+obj = of_port_stats_reply_new(OF_VERSION_1_4);
+{
+    of_object_t list;
+    of_port_stats_reply_entries_bind(obj, &list);
+    {
+        of_object_t *obj = of_port_stats_entry_new(OF_VERSION_1_4);
+        of_port_stats_entry_port_no_set(obj, 1);
+        of_port_stats_entry_rx_packets_set(obj, 1);
+        of_port_stats_entry_tx_packets_set(obj, 0);
+        of_port_stats_entry_rx_bytes_set(obj, 0);
+        of_port_stats_entry_tx_bytes_set(obj, 0);
+        of_port_stats_entry_rx_dropped_set(obj, 0);
+        of_port_stats_entry_tx_dropped_set(obj, 0);
+        of_port_stats_entry_rx_errors_set(obj, 0);
+        of_port_stats_entry_tx_errors_set(obj, 2);
+
+        /* Append property */
+        {
+            of_object_t list;
+            of_port_stats_entry_properties_bind(obj, &list);
+            {
+                of_object_t *obj = of_port_stats_prop_ethernet_new(OF_VERSION_1_4);
+                of_port_stats_prop_ethernet_rx_frame_err_set(obj, 1);
+                of_port_stats_prop_ethernet_rx_over_err_set(obj, 2);
+                of_port_stats_prop_ethernet_rx_crc_err_set(obj, 3);
+                of_port_stats_prop_ethernet_collisions_set(obj, 4);
+                of_list_append(&list, obj);
+                of_object_delete(obj);
+            }
+        }
+
+
+        of_list_append(&list, obj);
+        of_object_delete(obj);
+    }
+    {
+        of_object_t *obj = of_port_stats_entry_new(OF_VERSION_1_4);
+        of_port_stats_entry_port_no_set(obj, OF_PORT_DEST_LOCAL);
+        of_port_stats_entry_rx_packets_set(obj, 3);
+        of_port_stats_entry_tx_packets_set(obj, 0);
+        of_port_stats_entry_rx_bytes_set(obj, 0);
+        of_port_stats_entry_tx_bytes_set(obj, 0);
+        of_port_stats_entry_rx_dropped_set(obj, 0);
+        of_port_stats_entry_tx_dropped_set(obj, 0);
+        of_port_stats_entry_rx_errors_set(obj, 0);
+        of_port_stats_entry_tx_errors_set(obj, 4);
+        of_list_append(&list, obj);
+        of_object_delete(obj);
+    }
+}
+of_port_stats_reply_flags_set(obj, 0);
+of_port_stats_reply_xid_set(obj, 5);
diff --git a/test_data/of14/port_status.data b/test_data/of14/port_status.data
new file mode 100644
index 0000000..9fb5bde
--- /dev/null
+++ b/test_data/of14/port_status.data
@@ -0,0 +1,72 @@
+-- binary
+05 0c # version, type
+00 58 # length
+12 34 56 78 # xid
+02 # reason
+00 00 00 00 00 00 00 # pad
+00 00 00 04 # port_no
+00 48 # length
+00 00 # pad
+01 02 03 04 05 06 # hw_addr
+00 00 # pad
+66 6f 6f 00 00 00 00 00 # name
+00 00 00 00 00 00 00 00 # ...
+00 00 00 24 # config
+00 00 00 02 # state
+00 00 # properties[0].type (ethernet)
+00 20 # properties[0].length
+00 00 00 00 # pad
+00 00 00 01 # properties[0].curr
+00 00 00 02 # properties[0].advertised
+00 00 00 04 # properties[0].supported
+00 00 00 08 # properties[0].peer
+00 00 00 0a # properties[0].curr_speed
+00 00 00 14 # properties[0].max_speed
+-- python
+ofp.message.port_status(
+    xid=0x12345678,
+    reason=ofp.OFPPR_MODIFY,
+    desc=ofp.port_desc(
+        port_no=4,
+        hw_addr=[1,2,3,4,5,6],
+        name="foo",
+        config=ofp.OFPPC_NO_FWD|ofp.OFPPC_NO_RECV,
+        state=ofp.OFPPS_BLOCKED,
+        properties=[
+            ofp.port_desc_prop.ethernet(
+                curr=ofp.OFPPF_10MB_HD,
+                advertised=ofp.OFPPF_10MB_FD,
+                supported=ofp.OFPPF_100MB_HD,
+                peer=ofp.OFPPF_100MB_FD,
+                curr_speed=10,
+                max_speed=20)]))
+-- c
+obj = of_port_status_new(OF_VERSION_1_4);
+of_port_status_xid_set(obj, 0x12345678);
+of_port_status_reason_set(obj, OF_PORT_CHANGE_REASON_MODIFY);
+{
+    of_object_t desc;
+    of_port_status_desc_bind(obj, &desc);
+    of_port_desc_port_no_set(&desc, 4);
+    of_mac_addr_t hw_addr = { { 1, 2, 3, 4, 5, 6 } };
+    of_port_desc_hw_addr_set(&desc, hw_addr);
+    of_port_name_t name = "foo";
+    of_port_desc_name_set(&desc, name);
+    of_port_desc_config_set(&desc, OF_PORT_CONFIG_FLAG_NO_FWD|OF_PORT_CONFIG_FLAG_NO_RECV);
+    of_port_desc_state_set(&desc, OF_PORT_STATE_FLAG_BLOCKED);
+    {
+        of_list_port_desc_prop_t list;
+        of_port_desc_properties_bind(&desc, &list);
+        {
+            of_object_t *obj = of_port_desc_prop_ethernet_new(OF_VERSION_1_4);
+            of_port_desc_prop_ethernet_curr_set(obj, OF_PORT_FEATURE_FLAG_10MB_HD);
+            of_port_desc_prop_ethernet_advertised_set(obj, OF_PORT_FEATURE_FLAG_10MB_FD);
+            of_port_desc_prop_ethernet_supported_set(obj, OF_PORT_FEATURE_FLAG_100MB_HD);
+            of_port_desc_prop_ethernet_peer_set(obj, OF_PORT_FEATURE_FLAG_100MB_FD);
+            of_port_desc_prop_ethernet_curr_speed_set(obj, 10);
+            of_port_desc_prop_ethernet_max_speed_set(obj, 20);
+            of_list_append(&list, obj);
+            of_object_delete(obj);
+        }
+    }
+}
diff --git a/wireshark_gen/__init__.py b/wireshark_gen/__init__.py
index a610f3f..75918f8 100644
--- a/wireshark_gen/__init__.py
+++ b/wireshark_gen/__init__.py
@@ -38,7 +38,7 @@
 
 DissectorField = namedtuple("DissectorField", ["fullname", "name", "type", "base", "enum_table"])
 
-proto_names = { 1: 'of10', 2: 'of11', 3: 'of12', 4: 'of13' }
+proto_names = { 1: 'of10', 2: 'of11', 3: 'of12', 4: 'of13', 5: 'of14' }
 def make_field_name(version, ofclass_name, member_name):
     return "%s.%s.%s" % (proto_names[version.wire_version],
                          ofclass_name[3:],