Merge into master from pull request #161:
List reading (https://github.com/floodlight/loxigen/pull/161)
diff --git a/c_gen/build_of_g.py b/c_gen/build_of_g.py
index 46aedda..f117bfd 100755
--- a/c_gen/build_of_g.py
+++ b/c_gen/build_of_g.py
@@ -317,8 +317,6 @@
         version_name = of_g.of_version_wire2name[wire_version]
 
         for ofclass in protocol.classes:
-            if ofclass.name in ("of_group_add", "of_group_modify", "of_group_delete"):
-                continue
             of_g.ordered_classes[wire_version].append(ofclass.name)
             legacy_members = []
             pad_count = 0
@@ -417,9 +415,6 @@
                 continue
             if type_maps.class_is_virtual(cls):
                 continue
-            # HACK hide of_group subclasses from legacy c backend
-            if ofclass.name in ("of_group_add", "of_group_modify", "of_group_delete"):
-                continue
             subcls = cls[3:]
             val = find_type_value(ofclass, 'type')
             if not val in type_maps.message_types[wire_version].values():
@@ -448,28 +443,6 @@
                 of_g.ordered_classes[wire_version].append(new_cls)
                 classes[new_cls] = classes[cls]
 
-    # Generate action_id classes for OF 1.3
-    for wire_version, ordered_classes in of_g.ordered_classes.items():
-        if not wire_version in [of_g.VERSION_1_3]:
-            continue
-        classes = versions[of_g.of_version_wire2name[wire_version]]['classes']
-        for cls in ordered_classes:
-            if not loxi_utils.class_is_action(cls):
-                continue
-            action = cls[10:]
-            if action == '' or action == 'header':
-                continue
-            name = "of_action_id_" + action
-            members = classes["of_action"][:]
-            of_g.ordered_classes[wire_version].append(name)
-            if type_maps.action_id_is_extension(name, wire_version):
-                # Copy the base action classes thru subtype
-                members = classes["of_action_" + action][:4]
-            classes[name] = members
-
-    # @fixme If we support extended actions in OF 1.3, need to add IDs
-    # for them here
-
     for wire_version in of_g.wire_ver_map.keys():
         version_name = of_g.of_version_wire2name[wire_version]
         calculate_offsets_and_lengths(
diff --git a/c_gen/c_code_gen.py b/c_gen/c_code_gen.py
index e2bb467..640687f 100644
--- a/c_gen/c_code_gen.py
+++ b/c_gen/c_code_gen.py
@@ -37,6 +37,7 @@
 from c_gen import flags, type_maps, c_type_maps
 import c_gen.loxi_utils_legacy as loxi_utils
 from c_gen.loxi_utils_legacy import config_check
+import loxi_globals
 
 import c_gen.identifiers as identifiers
 
@@ -425,7 +426,6 @@
                                     of_match_t *match);
 extern int of_wire_buffer_of_match_set(of_object_t *obj, int offset,
                                     of_match_t *match, int cur_len);
-extern void of_extension_object_id_set(of_object_t *obj, of_object_id_t id);
 """)
 
     # gen_base_types(out)
@@ -785,6 +785,7 @@
 #include <loci/loci.h>
 #include <loci/of_object.h>
 #include "loci_log.h"
+#include "loci_push_wire_types.h"
 
 """)
     gen_object_enum_str(out)
@@ -2501,7 +2502,7 @@
 typedef void (*of_wire_length_get_f)(of_object_t *obj, int *bytes);
 typedef void (*of_wire_length_set_f)(of_object_t *obj, int bytes);
 typedef void (*of_wire_type_get_f)(of_object_t *obj, of_object_id_t *id);
-typedef void (*of_wire_type_set_f)(of_object_t *obj, of_object_id_t id);
+typedef void (*of_wire_type_set_f)(of_object_t *obj);
 """)
     # If not using function pointers in classes, don't gen typedefs below
     if not config_check("gen_fn_ptrs"):
@@ -2687,62 +2688,30 @@
 {
 """ % 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; push version, length and type to wire */
+    /* Message obj; set length */
     of_message_t msg;
 
     if ((msg = OF_OBJECT_TO_MESSAGE(obj)) != NULL) {
-        of_message_version_set(msg, obj->version);
         of_message_length_set(msg, obj->length);
-        OF_TRY(of_wire_message_object_id_set(OF_OBJECT_TO_WBUF(obj),
-                 %(name)s));
     }
 """ % dict(name = enum_name(cls)))
 
-        for version in of_g.of_version_range:
-            if type_maps.class_is_extension(cls, version):
-                exp_name = type_maps.extension_to_experimenter_macro_name(cls)
-                subtype = type_maps.extension_message_to_subtype(cls, version)
-                if subtype is None or exp_name is None:
-                    print "Error in mapping extension message"
-                    print cls, version
-                    sys.exit(1)
-                out.write("""
-    if (obj->version == %(version)s) {
-        of_message_experimenter_id_set(OF_OBJECT_TO_MESSAGE(obj),
-                                       %(exp_name)s);
-        of_message_experimenter_subtype_set(OF_OBJECT_TO_MESSAGE(obj),
-                                            %(subtype)s);
-    }
-""" % dict(exp_name=exp_name, version=of_g.wire_ver_map[version],
-           subtype=str(subtype)))
-
     else: # Not a message
         if loxi_utils.class_is_tlv16(cls):
             out.write("""
-    /* TLV obj; set length and type */
+    /* TLV obj; set length */
     of_tlv16_wire_length_set((of_object_t *)obj, obj->length);
-    of_tlv16_wire_object_id_set((of_object_t *)obj,
-           %(enum)s);
 """ % dict(enum=enum_name(cls)))
-            # Some tlv16 types may be extensions requiring more work
-            if cls in ["of_action_bsn_mirror", "of_action_id_bsn_mirror",
-                       "of_action_bsn_set_tunnel_dst", "of_action_id_bsn_set_tunnel_dst",
-                       "of_action_nicira_dec_ttl", "of_action_id_nicira_dec_ttl",
-                       "of_instruction_bsn_disable_src_mac_check"]:
-                out.write("""
-    /* Extended TLV obj; Call specific accessor */
-    of_extension_object_id_set(obj, %(enum)s);
-""" % dict(cls=cls, enum=enum_name(cls)))
 
-
-        if loxi_utils.class_is_oxm(cls):
-            out.write("""\
-    /* OXM obj; set length and type */
-    of_oxm_wire_length_set((of_object_t *)obj, obj->length);
-    of_oxm_wire_object_id_set((of_object_t *)obj, %(enum)s);
-""" % dict(enum=enum_name(cls)))
         if loxi_utils.class_is_u16_len(cls) or cls == "of_packet_queue":
             out.write("""
     obj->wire_length_set((of_object_t *)obj, obj->length);
@@ -3050,6 +3019,12 @@
     /* Set up the object's function pointers */
 """)
 
+    uclass = loxi_globals.unified.class_by_name(cls)
+    if uclass and not uclass.virtual and uclass.has_type_members:
+        out.write("""
+    obj->wire_type_set = %(cls)s_push_wire_types;
+""" % dict(cls=cls))
+
     if loxi_utils.class_is_message(cls):
         out.write("""
     obj->wire_length_get = of_object_message_wire_length_get;
@@ -3060,7 +3035,6 @@
             if not (cls in type_maps.inheritance_map): # Don't set for super
                 out.write("""
     obj->wire_length_set = of_tlv16_wire_length_set;
-    obj->wire_type_set = of_tlv16_wire_object_id_set;\
 """)
             out.write("""
     obj->wire_length_get = of_tlv16_wire_length_get;
@@ -3096,9 +3070,7 @@
         if loxi_utils.class_is_oxm(cls):
             out.write("""
     obj->wire_length_get = of_oxm_wire_length_get;
-    obj->wire_length_set = of_oxm_wire_length_set;
     obj->wire_type_get = of_oxm_wire_object_id_get;
-    obj->wire_type_set = of_oxm_wire_object_id_set;
 """)
         if loxi_utils.class_is_u16_len(cls):
             out.write("""
diff --git a/c_gen/c_test_gen.py b/c_gen/c_test_gen.py
index 83fa4a3..ac2845f 100644
--- a/c_gen/c_test_gen.py
+++ b/c_gen/c_test_gen.py
@@ -132,7 +132,11 @@
     classes = ["of_bsn_lacp_stats_request",
                "of_bsn_lacp_stats_reply",
                "of_bsn_switch_pipeline_stats_request",
-               "of_bsn_switch_pipeline_stats_reply"]
+               "of_bsn_switch_pipeline_stats_reply",
+               "of_bsn_port_counter_stats_request",
+               "of_bsn_port_counter_stats_reply",
+               "of_bsn_vlan_counter_stats_request",
+               "of_bsn_vlan_counter_stats_reply"]
 
     if (cls in classes and (
             m_name == "experimenter" or
diff --git a/c_gen/c_type_maps.py b/c_gen/c_type_maps.py
index 43cd4a8..b0775d1 100644
--- a/c_gen/c_type_maps.py
+++ b/c_gen/c_type_maps.py
@@ -75,6 +75,10 @@
                 out.write("    %d%s /* %s */\n" %
                           (type_maps.type_val[("of_flow_mod", version)],
                            comma, cls))
+            elif cls in type_maps.group_mod_list and version > 1:
+                out.write("    %d%s /* %s */\n" %
+                          (type_maps.type_val[("of_group_mod", version)],
+                           comma, cls))
             elif (cls, version) in type_maps.type_val and \
                     type_maps.type_val[(cls, version)] != type_maps.invalid_type:
                 out.write("    %d%s /* %s */\n" %
@@ -261,6 +265,9 @@
     gen_type_to_object_id(out, "hello_elem_type_to_id", "OF_HELLO_ELEM",
                           "OF_HELLO_ELEM_%s", type_maps.hello_elem_types,
                           max_type_value)
+    gen_type_to_object_id(out, "group_mod_type_to_id", "OF_GROUP_MOD",
+                          "OF_GROUP_%s", type_maps.group_mod_types,
+                          max_type_value)
 
     # FIXME:  Multipart re-organization
     gen_type_to_object_id(out, "stats_request_type_to_id", "OF_STATS_REQUEST",
@@ -504,6 +511,7 @@
     uint16_t err_type;
     uint8_t flow_mod_cmd;
     uint32_t experimenter, subtype;
+    uint16_t group_mod_cmd;
 
     if (length < OF_MESSAGE_MIN_LENGTH) {
         return OF_OBJECT_INVALID;
@@ -570,6 +578,14 @@
         obj_id = of_error_msg_to_object_id(err_type, ver);
     }
 
+    if (obj_id == OF_GROUP_MOD) {
+        if (length < OF_MESSAGE_MIN_GROUP_MOD_LENGTH) {
+            return OF_OBJECT_INVALID;
+        }
+        group_mod_cmd = of_message_group_mod_command_get(msg);
+        obj_id = of_group_mod_to_object_id(group_mod_cmd, ver);
+    }
+
     return obj_id;
 }
 """
@@ -692,6 +708,11 @@
     out.write(map_template %
               dict(name="flow_mod", u_name="FLOW_MOD", ar_len=ar_len))
 
+    ar_len = type_maps.type_array_len(type_maps.group_mod_types,
+                                      max_type_value)
+    out.write(map_template %
+              dict(name="group_mod", u_name="GROUP_MOD", ar_len=ar_len))
+
     # OXM
     ar_len = type_maps.type_array_len(type_maps.oxm_types, max_type_value)
     out.write("""
@@ -947,6 +968,48 @@
 
 """)
 
+    ################################################################
+    # Generate object ID to the group mod sub-type map
+    ################################################################
+
+    out.write("""
+/**
+ * Map an object ID to a group-mod command value
+ * @param id An object ID
+ * @return The wire value for the group-mod command
+ * @return -1 if not supported for this version
+ * @return -1 if id is not a specific stats type ID
+ *
+ * Note that the value is returned as a signed integer.  So -1 is
+ * an error code, while 0xffff is the usual "experimenter" code.
+ */
+
+static inline int
+of_object_to_group_mod_command(of_object_id_t id, of_version_t version)
+{
+    if (!OF_VERSION_OKAY(version)) {
+        return -1;
+    }
+    switch (id) {""")
+    group_mod_names = set()
+    for ver in of_g.of_version_range:
+        for name in type_maps.group_mod_types[ver]:
+            group_mod_names.add(name)
+    for name in group_mod_names:
+        out.write("""
+    case OF_GROUP_%(name)s:
+        if (OF_GROUP_MOD_COMMAND_%(name)s_SUPPORTED(version))
+            return OF_GROUP_MOD_COMMAND_%(name)s_BY_VERSION(version);
+        break;""" % {"name": name.upper()})
+    out.write("""
+    default:
+        break;
+    }
+    return -1; /* Not recognized as group mod type object for this version */
+}
+
+""")
+
 def gen_type_maps_header(out):
     """
     Generate various header file declarations for type maps
@@ -1031,78 +1094,6 @@
 
 """)
 
-    # Generate the function that sets the object type fields
-    out.write("""
-
-/**
- * Map a message in a wire buffer object to its OF object id.
- * @param wbuf Pointer to a wire buffer object, populated with an OF message
- * @returns The object ID of the message
- * @returns OF_OBJECT_INVALID if unable to parse the message type
- *
- * Version must be set in the buffer prior to calling this routine
- */
-
-static inline int
-of_wire_message_object_id_set(of_wire_buffer_t *wbuf, of_object_id_t id)
-{
-    int type;
-    of_version_t ver;
-    of_message_t msg;
-
-    msg = (of_message_t)WBUF_BUF(wbuf);
-
-    ver = of_message_version_get(msg);
-
-    /* ASSERT(id is a message object) */
-
-    if ((type = of_object_to_wire_type(id, ver)) < 0) {
-        return OF_ERROR_PARAM;
-    }
-    of_message_type_set(msg, type);
-
-    if ((type = of_object_to_stats_type(id, ver)) >= 0) {
-        /* It's a stats obj */
-        of_message_stats_type_set(msg, type);
-        if (type == OF_STATS_TYPE_EXPERIMENTER) {
-            switch (id) {
-            case OF_BSN_LACP_STATS_REQUEST:
-            case OF_BSN_LACP_STATS_REPLY:
-                of_message_stats_experimenter_id_set(msg, OF_EXPERIMENTER_ID_BSN);
-                of_message_stats_experimenter_subtype_set(msg, 1);
-                break;
-            case OF_BSN_SWITCH_PIPELINE_STATS_REQUEST:
-            case OF_BSN_SWITCH_PIPELINE_STATS_REPLY:
-                of_message_stats_experimenter_id_set(msg, OF_EXPERIMENTER_ID_BSN);
-                of_message_stats_experimenter_subtype_set(msg, 6);
-                break;
-            default:
-                break;
-            }
-        }
-    }
-    if ((type = of_object_to_error_type(id, ver)) >= 0) {
-        /* It's an error obj */
-        of_message_error_type_set(msg, type);
-    }
-    if ((type = of_object_to_flow_mod_command(id, ver)) >= 0) {
-        /* It's a flow mod obj */
-        of_message_flow_mod_command_set(msg, ver, type);
-    }
-    if (of_object_id_is_extension(id, ver)) {
-        uint32_t val32;
-
-        /* Set the experimenter and subtype codes */
-        val32 = of_extension_to_experimenter_id(id, ver);
-        of_message_experimenter_id_set(msg, val32);
-        val32 = of_extension_to_experimenter_subtype(id, ver);
-        of_message_experimenter_subtype_set(msg, val32);
-    }
-
-    return OF_ERROR_NONE;
-}
-""")
-
 def gen_type_data_header(out):
 
     out.write("""
@@ -1172,15 +1163,11 @@
 extern void of_object_message_wire_length_set(of_object_t *obj, int bytes);
 
 extern void of_oxm_wire_length_get(of_object_t *obj, int *bytes);
-extern void of_oxm_wire_length_set(of_object_t *obj, int bytes);
 extern void of_oxm_wire_object_id_get(of_object_t *obj, of_object_id_t *id);
-extern void of_oxm_wire_object_id_set(of_object_t *obj, of_object_id_t id);
 
 extern void of_tlv16_wire_length_get(of_object_t *obj, int *bytes);
 extern void of_tlv16_wire_length_set(of_object_t *obj, int bytes);
 
-extern void of_tlv16_wire_object_id_set(of_object_t *obj, of_object_id_t id);
-
 /* Wire length is uint16 at front of structure */
 extern void of_u16_len_wire_length_get(of_object_t *obj, int *bytes);
 extern void of_u16_len_wire_length_set(of_object_t *obj, int bytes);
diff --git a/c_gen/codegen.py b/c_gen/codegen.py
new file mode 100644
index 0000000..cc4a58c
--- /dev/null
+++ b/c_gen/codegen.py
@@ -0,0 +1,79 @@
+# 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.
+
+"""
+Code generation
+
+These functions extract data from the IR and render templates with it.
+"""
+
+from collections import namedtuple
+from itertools import groupby
+import template_utils
+import loxi_globals
+import loxi_ir.ir as ir
+import util
+
+PushWireTypesFn = namedtuple('PushWireTypesFn',
+    ['class_name', 'versioned_type_members'])
+PushWireTypesMember = namedtuple('PushWireTypesMember',
+    ['name', 'offset', 'length', 'value'])
+
+def gen_push_wire_types(install_dir):
+    fns = []
+    for uclass in loxi_globals.unified.classes:
+        if uclass.virtual or not uclass.has_type_members:
+            continue
+
+        # Generate a dict of version -> list of PushWireTypesMember
+        type_members_by_version = {}
+        for version, ofclass in sorted(uclass.version_classes.items()):
+            pwtms = []
+            for m in ofclass.members:
+                if isinstance(m, ir.OFTypeMember):
+                    if m.name == "version" and m.value == version.wire_version:
+                        # Special case for version
+                        pwtms.append(PushWireTypesMember(m.name, m.offset, m.length, "obj->version"))
+                    else:
+                        pwtms.append(PushWireTypesMember(m.name, m.offset, m.length, hex(m.value)))
+            type_members_by_version[version] = pwtms
+
+        # Merge versions with identical type members
+        all_versions = sorted(type_members_by_version.keys())
+        versioned_type_members = []
+        for pwtms, versions in groupby(all_versions, type_members_by_version.get):
+            versioned_type_members.append((pwtms, list(versions)))
+
+        fns.append(PushWireTypesFn(
+            class_name=uclass.name,
+            versioned_type_members=versioned_type_members))
+
+    with template_utils.open_output(install_dir, "loci/src/loci_push_wire_types.c") as out:
+        util.render_template(out, "loci_push_wire_types.c", fns=fns)
+
+    with template_utils.open_output(install_dir, "loci/src/loci_push_wire_types.h") as out:
+        util.render_template(out, "loci_push_wire_types.h", fns=fns)
diff --git a/c_gen/loxi_utils_legacy.py b/c_gen/loxi_utils_legacy.py
index 699006f..9abca52 100644
--- a/c_gen/loxi_utils_legacy.py
+++ b/c_gen/loxi_utils_legacy.py
@@ -125,7 +125,8 @@
     Return True if cls_name is an object which uses initial uint16 length
     """
     return cls in ["of_group_desc_stats_entry", "of_group_stats_entry",
-                   "of_flow_stats_entry", "of_bucket", "of_table_features"]
+                   "of_flow_stats_entry", "of_bucket", "of_table_features",
+                   "of_bsn_port_counter_stats_entry", "of_bsn_vlan_counter_stats_entry"]
 
 def class_is_oxm(cls):
     """
@@ -179,6 +180,8 @@
     """
     Return True if cls_name is an instruction object
     """
+    if cls.find("of_instruction_id") == 0:
+        return False
     if cls.find("of_instruction") == 0:
         return True
 
diff --git a/c_gen/of_g_legacy.py b/c_gen/of_g_legacy.py
index adf5c2b..80d922c 100644
--- a/c_gen/of_g_legacy.py
+++ b/c_gen/of_g_legacy.py
@@ -99,7 +99,7 @@
 ## These members do not get normal accessors
 
 skip_members = ["version", "type", "length", "err_type", "stats_type", "len",
-                "type_len", "actions_len", "_command"]
+                "type_len", "actions_len", "_command", "command"]
 
 ## Some OpenFlow string length constants
 #
diff --git a/c_gen/templates/loci_push_wire_types.c b/c_gen/templates/loci_push_wire_types.c
new file mode 100644
index 0000000..2103aad
--- /dev/null
+++ b/c_gen/templates/loci_push_wire_types.c
@@ -0,0 +1,83 @@
+:: # 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.
+::
+:: include('_copyright.c')
+
+/****************************************************************
+ *
+ * Functions for each concrete class that set the type fields
+ *
+ ****************************************************************/
+
+#include <loci/loci.h>
+#include <loci/of_message.h>
+#include <endian.h>
+
+#ifdef __GNUC__
+#define UNREACHABLE() __builtin_unreachable()
+#else
+#define UNREACHABLE()
+#endif
+
+/*
+ * In a separate function to give the compiler the choice of whether to inline.
+ */
+static unsigned char *
+loci_object_to_buffer(of_object_t *obj)
+{
+    return OF_OBJECT_BUFFER_INDEX(obj, 0);
+}
+
+:: for fn in fns:
+
+void
+${fn.class_name}_push_wire_types(of_object_t *obj)
+{
+    unsigned char *buf = loci_object_to_buffer(obj);
+    switch (obj->version) {
+:: for ms, versions in fn.versioned_type_members:
+:: for version in versions:
+    case ${version.constant_version(prefix='OF_VERSION_')}:
+:: #endfor
+:: for m in ms:
+:: if m.length == 1:
+        *(uint8_t *)(buf + ${m.offset}) = ${m.value}; /* ${m.name} */
+:: elif m.length == 2:
+        *(uint16_t *)(buf + ${m.offset}) = htobe16(${m.value}); /* ${m.name} */
+:: elif m.length == 4:
+        *(uint32_t *)(buf + ${m.offset}) = htobe32(${m.value}); /* ${m.name} */
+:: else:
+:: raise("unsupported push_wire_types length %d" % m.length)
+:: #endif
+:: #endfor
+        break;
+:: #endfor
+    default:
+        UNREACHABLE();
+    }
+}
+:: #endfor
diff --git a/c_gen/templates/loci_push_wire_types.h b/c_gen/templates/loci_push_wire_types.h
new file mode 100644
index 0000000..5b2c1e8
--- /dev/null
+++ b/c_gen/templates/loci_push_wire_types.h
@@ -0,0 +1,40 @@
+:: # 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.
+::
+:: include('_copyright.c')
+#ifndef LOCI_PUSH_WIRE_TYPES_H
+#define LOCI_PUSH_WIRE_TYPES_H
+
+#include <loci/loci.h>
+
+/* Declarations of public functions from loci_push_wire_types.c */
+
+:: for fn in fns:
+void ${fn.class_name}_push_wire_types(of_object_t *obj);
+:: #endfor
+
+#endif
diff --git a/c_gen/templates/loci_show.h b/c_gen/templates/loci_show.h
index 70f18d5..3d55da8 100644
--- a/c_gen/templates/loci_show.h
+++ b/c_gen/templates/loci_show.h
@@ -236,7 +236,14 @@
 #define LOCI_SHOW_u32_supported(writer, cookie, val) LOCI_SHOW_x32(writer, cookie, val)
 #define LOCI_SHOW_u32_peer(writer, cookie, val) LOCI_SHOW_x32(writer, cookie, val)
 #define LOCI_SHOW_u64_rx_packets(writer, cookie, val) LOCI_SHOW_u64(writer, cookie, val)
+#define LOCI_SHOW_u64_rx_packets_unicast(writer, cookie, val) LOCI_SHOW_u64(writer, cookie, val)
+#define LOCI_SHOW_u64_rx_packets_multicast(writer, cookie, val) LOCI_SHOW_u64(writer, cookie, val)
+#define LOCI_SHOW_u64_rx_packets_broadcast(writer, cookie, val) LOCI_SHOW_u64(writer, cookie, val)
+#define LOCI_SHOW_u64_uint64_value(writer, cookie, val) LOCI_SHOW_u64(writer, cookie, val)
 #define LOCI_SHOW_u64_tx_packets(writer, cookie, val) LOCI_SHOW_u64(writer, cookie, val)
+#define LOCI_SHOW_u64_tx_packets_unicast(writer, cookie, val) LOCI_SHOW_u64(writer, cookie, val)
+#define LOCI_SHOW_u64_tx_packets_multicast(writer, cookie, val) LOCI_SHOW_u64(writer, cookie, val)
+#define LOCI_SHOW_u64_tx_packets_broadcast(writer, cookie, val) LOCI_SHOW_u64(writer, cookie, val)
 #define LOCI_SHOW_u64_rx_bytes(writer, cookie, val) LOCI_SHOW_u64(writer, cookie, val)
 #define LOCI_SHOW_u64_tx_bytes(writer, cookie, val) LOCI_SHOW_u64(writer, cookie, val)
 #define LOCI_SHOW_u64_rx_dropped(writer, cookie, val) LOCI_SHOW_u64(writer, cookie, val)
diff --git a/c_gen/templates/of_message.h b/c_gen/templates/of_message.h
index c1b6785..df74231 100644
--- a/c_gen/templates/of_message.h
+++ b/c_gen/templates/of_message.h
@@ -48,11 +48,13 @@
 #define OF_MESSAGE_ERROR_TYPE_OFFSET 8
 #define OF_MESSAGE_STATS_TYPE_OFFSET 8
 #define OF_MESSAGE_FLOW_MOD_COMMAND_OFFSET(version) ((version) == 1 ? 56 : 25)
+#define OF_MESSAGE_GROUP_MOD_COMMAND_OFFSET 8
 
 #define OF_MESSAGE_MIN_LENGTH 8
 #define OF_MESSAGE_MIN_STATS_LENGTH (OF_MESSAGE_STATS_TYPE_OFFSET + 2)
 #define OF_MESSAGE_MIN_ERROR_LENGTH (OF_MESSAGE_ERROR_TYPE_OFFSET + 4)
 #define OF_MESSAGE_MIN_FLOW_MOD_LENGTH(version)  ((version) == 1 ? 57 : 26)
+#define OF_MESSAGE_MIN_GROUP_MOD_LENGTH (OF_MESSAGE_GROUP_MOD_COMMAND_OFFSET + 2)
 
 #define OF_MESSAGE_EXPERIMENTER_ID_OFFSET 8
 #define OF_MESSAGE_EXPERIMENTER_SUBTYPE_OFFSET 12
@@ -103,11 +105,6 @@
     return (of_version_t)msg[OF_MESSAGE_VERSION_OFFSET];
 }
 
-static inline void
-of_message_version_set(of_message_t msg, of_version_t version) {
-    buf_u8_set(msg, (uint8_t)version);
-}
-
 /**
  * @brief Get/set OpenFlow type of a message
  * @param msg Pointer to the message buffer of sufficient length
@@ -120,11 +117,6 @@
     return msg[OF_MESSAGE_TYPE_OFFSET];
 }
 
-static inline void
-of_message_type_set(of_message_t msg, uint8_t value) {
-    buf_u8_set(msg + OF_MESSAGE_TYPE_OFFSET, value);
-}
-
 /**
  * @brief Get/set in-buffer length of a message
  * @param msg Pointer to the message buffer of sufficient length
@@ -159,11 +151,6 @@
     return val;
 }
 
-static inline void
-of_message_xid_set(of_message_t msg, uint32_t xid) {
-    buf_u32_set(msg + OF_MESSAGE_XID_OFFSET, xid);
-}
-
 /**
  * @brief Get/set stats type of a message
  * @param msg Pointer to the message buffer of sufficient length
@@ -178,11 +165,6 @@
     return val;
 }
 
-static inline void
-of_message_stats_type_set(of_message_t msg, uint16_t type) {
-    buf_u16_set(msg + OF_MESSAGE_STATS_TYPE_OFFSET, type);
-}
-
 /**
  * @brief Get/set error type of a message
  * @param msg Pointer to the message buffer of sufficient length
@@ -197,11 +179,6 @@
     return val;
 }
 
-static inline void
-of_message_error_type_set(of_message_t msg, uint16_t type) {
-    buf_u16_set(msg + OF_MESSAGE_ERROR_TYPE_OFFSET, type);
-}
-
 
 /**
  * @brief Get/set experimenter ID of a message
@@ -217,11 +194,6 @@
     return val;
 }
 
-static inline void
-of_message_experimenter_id_set(of_message_t msg, uint32_t experimenter_id) {
-    buf_u32_set(msg + OF_MESSAGE_EXPERIMENTER_ID_OFFSET, experimenter_id);
-}
-
 
 /**
  * @brief Get/set experimenter message type (subtype) of a message
@@ -237,13 +209,6 @@
     return val;
 }
 
-static inline void
-of_message_experimenter_subtype_set(of_message_t msg,
-                                    uint32_t subtype) {
-    buf_u32_set(msg + OF_MESSAGE_EXPERIMENTER_SUBTYPE_OFFSET,
-                subtype);
-}
-
 /**
  * Flow mod command changed from 16 to 8 bits on the wire from 1.0 to 1.1
  */
@@ -261,19 +226,6 @@
     return val8;
 }
 
-static inline void
-of_message_flow_mod_command_set(of_message_t msg, of_version_t version, 
-                                uint8_t command) {
-    uint16_t val16;
-
-    if (version == OF_VERSION_1_0) {
-        val16 = command;
-        buf_u16_set(msg + OF_MESSAGE_FLOW_MOD_COMMAND_OFFSET(version), val16);
-    } else {
-        buf_u8_set(msg + OF_MESSAGE_FLOW_MOD_COMMAND_OFFSET(version), command);
-    }
-}
-
 /**
  * @brief Get/set stats request/reply experimenter ID of a message
  * @param msg Pointer to the message buffer of sufficient length
@@ -288,11 +240,6 @@
     return val;
 }
 
-static inline void
-of_message_stats_experimenter_id_set(of_message_t msg, uint32_t experimenter_id) {
-    buf_u32_set(msg + OF_MESSAGE_STATS_EXPERIMENTER_ID_OFFSET, experimenter_id);
-}
-
 /**
  * @brief Get/set stats request/reply experimenter subtype of a message
  * @param msg Pointer to the message buffer of sufficient length
@@ -307,9 +254,18 @@
     return val;
 }
 
-static inline void
-of_message_stats_experimenter_subtype_set(of_message_t msg, uint32_t subtype) {
-    buf_u32_set(msg + OF_MESSAGE_STATS_EXPERIMENTER_SUBTYPE_OFFSET, subtype);
+/**
+ * @brief Get/set group mod command of a message
+ * @param msg Pointer to the message buffer of sufficient length
+ * @param subtype Data for set operation
+ * @returns get returns command in host order
+ */
+
+static inline uint16_t
+of_message_group_mod_command_get(of_message_t msg) {
+    uint16_t val;
+    buf_u16_get(msg + OF_MESSAGE_GROUP_MOD_COMMAND_OFFSET, &val);
+    return val;
 }
 
 #endif /* _OF_MESSAGE_H_ */
diff --git a/c_gen/templates/of_object.c b/c_gen/templates/of_object.c
index 0b3ec3a..ae22f64 100644
--- a/c_gen/templates/of_object.c
+++ b/c_gen/templates/of_object.c
@@ -510,7 +510,7 @@
     }
 
     if (child->wire_type_set) {
-        child->wire_type_set(child, child->object_id);
+        child->wire_type_set(child);
     }
 
     /* Update the parent's length */
diff --git a/c_gen/templates/of_type_maps.c b/c_gen/templates/of_type_maps.c
index fa3e05c..4b35dcc 100644
--- a/c_gen/templates/of_type_maps.c
+++ b/c_gen/templates/of_type_maps.c
@@ -139,30 +139,6 @@
 }
 
 /**
- * Set the object ID based on the wire buffer for any TLV object
- * @param obj The object being referenced
- * @param id The ID value representing what should be stored.
- */
-
-void
-of_tlv16_wire_object_id_set(of_object_t *obj, of_object_id_t id)
-{
-    int wire_type;
-    of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
-    ASSERT(wbuf != NULL);
-
-    wire_type = of_object_to_type_map[obj->version][id];
-    ASSERT(wire_type >= 0);
-
-    of_wire_buffer_u16_set(wbuf, 
-        OF_OBJECT_ABSOLUTE_OFFSET(obj, TLV16_WIRE_TYPE_OFFSET), wire_type);
-
-    if (wire_type == OF_EXPERIMENTER_TYPE) {
-        of_extension_object_id_set(obj, id);
-    }
-}
-
-/**
  * Get the object ID of an extended action
  * @param obj The object being referenced
  * @param id Where to store the object ID
@@ -209,44 +185,6 @@
 }
 
 /**
- * Set wire data for extension objects, not messages.
- */
-
-void
-of_extension_object_id_set(of_object_t *obj, of_object_id_t id)
-{
-    uint8_t *buf = OF_OBJECT_BUFFER_INDEX(obj, 0);
-    
-    switch (id) {
-    case OF_ACTION_BSN_MIRROR:
-    case OF_ACTION_ID_BSN_MIRROR:
-        buf_u32_set(buf + OF_ACTION_EXPERIMENTER_ID_OFFSET,
-                    OF_EXPERIMENTER_ID_BSN);
-        buf_u32_set(buf + OF_ACTION_EXPERIMENTER_SUBTYPE_OFFSET, 1);
-        break;
-    case OF_ACTION_BSN_SET_TUNNEL_DST:
-    case OF_ACTION_ID_BSN_SET_TUNNEL_DST:
-        buf_u32_set(buf + OF_ACTION_EXPERIMENTER_ID_OFFSET,
-                    OF_EXPERIMENTER_ID_BSN);
-        buf_u32_set(buf + OF_ACTION_EXPERIMENTER_SUBTYPE_OFFSET, 2);
-        break;
-    case OF_ACTION_NICIRA_DEC_TTL:
-    case OF_ACTION_ID_NICIRA_DEC_TTL:
-        buf_u32_set(buf + OF_ACTION_EXPERIMENTER_ID_OFFSET,
-                    OF_EXPERIMENTER_ID_NICIRA);
-        buf_u16_set(buf + OF_ACTION_EXPERIMENTER_SUBTYPE_OFFSET, 18);
-        break;
-    case OF_INSTRUCTION_BSN_DISABLE_SRC_MAC_CHECK:
-        buf_u32_set(buf + OF_INSTRUCTION_EXPERIMENTER_ID_OFFSET,
-                    OF_EXPERIMENTER_ID_BSN);
-        buf_u32_set(buf + OF_INSTRUCTION_EXPERIMENTER_SUBTYPE_OFFSET, 0);
-        break;
-    default:
-        break;
-    }
-}
-
-/**
  * Get the object ID of an extended action
  * @param obj The object being referenced
  * @param id Where to store the object ID
@@ -551,27 +489,6 @@
 }
 
 /**
- * Set the length of an OXM object in the wire buffer
- * @param obj The object whose wire buffer is an OXM type
- * @param bytes Value to store in wire buffer
- */
-
-void
-of_oxm_wire_length_set(of_object_t *obj, int bytes)
-{
-    uint32_t type_len;
-    of_wire_buffer_t *wbuf;
-
-    ASSERT(bytes >= 0 && bytes < 256);
-
-    /* Read-modify-write */
-    _GET_OXM_TYPE_LEN(obj, &type_len, wbuf);
-    OF_OXM_LENGTH_SET(type_len, bytes);
-    of_wire_buffer_u32_set(wbuf, 
-           OF_OBJECT_ABSOLUTE_OFFSET(obj, OXM_HDR_OFFSET), type_len);
-}
-
-/**
  * Get the object ID of an OXM object based on the wire buffer type
  * @param obj The object whose wire buffer is an OXM type
  * @param id (out) Where the ID is stored 
@@ -587,80 +504,6 @@
     *id = of_oxm_to_object_id(type_len, obj->version);
 }
 
-/**
- * Set the wire type of an OXM object based on the object ID passed
- * @param obj The object whose wire buffer is an OXM type
- * @param id The object ID mapped to an OXM wire type which is stored
- */
-
-void
-of_oxm_wire_object_id_set(of_object_t *obj, of_object_id_t id)
-{
-    uint32_t type_len;
-    int wire_type;
-    of_wire_buffer_t *wbuf;
-
-    ASSERT(OF_OXM_VALID_ID(id));
-
-    /* Read-modify-write */
-    _GET_OXM_TYPE_LEN(obj, &type_len, wbuf);
-
-    switch (id) {
-    case OF_OXM_BSN_IN_PORTS_128:
-        type_len = 0x00030000 | (type_len & 0xff);
-        break;
-    case OF_OXM_BSN_IN_PORTS_128_MASKED:
-        type_len = 0x00030100 | (type_len & 0xff);
-        break;
-    case OF_OXM_BSN_LAG_ID:
-        type_len = 0x00030200 | (type_len & 0xff);
-        break;
-    case OF_OXM_BSN_LAG_ID_MASKED:
-        type_len = 0x00030300 | (type_len & 0xff);
-        break;
-    case OF_OXM_BSN_VRF:
-        type_len = 0x00030400 | (type_len & 0xff);
-        break;
-    case OF_OXM_BSN_VRF_MASKED:
-        type_len = 0x00030500 | (type_len & 0xff);
-        break;
-    case OF_OXM_BSN_GLOBAL_VRF_ALLOWED:
-        type_len = 0x00030600 | (type_len & 0xff);
-        break;
-    case OF_OXM_BSN_GLOBAL_VRF_ALLOWED_MASKED:
-        type_len = 0x00030700 | (type_len & 0xff);
-        break;
-    case OF_OXM_BSN_L3_INTERFACE_CLASS_ID:
-        type_len = 0x00030800 | (type_len & 0xff);
-        break;
-    case OF_OXM_BSN_L3_INTERFACE_CLASS_ID_MASKED:
-        type_len = 0x00030900 | (type_len & 0xff);
-        break;
-    case OF_OXM_BSN_L3_SRC_CLASS_ID:
-        type_len = 0x00030a00 | (type_len & 0xff);
-        break;
-    case OF_OXM_BSN_L3_SRC_CLASS_ID_MASKED:
-        type_len = 0x00030b00 | (type_len & 0xff);
-        break;
-    case OF_OXM_BSN_L3_DST_CLASS_ID:
-        type_len = 0x00030c00 | (type_len & 0xff);
-        break;
-    case OF_OXM_BSN_L3_DST_CLASS_ID_MASKED:
-        type_len = 0x00030d00 | (type_len & 0xff);
-        break;
-    default:
-        wire_type = of_object_to_wire_type(id, obj->version);
-        ASSERT(wire_type >= 0);
-        type_len = 0x80000000 | (wire_type << 8) | (type_len & 0xff);
-        break;
-    }
-
-    of_wire_buffer_u32_set(wbuf, 
-           OF_OBJECT_ABSOLUTE_OFFSET(obj, OXM_HDR_OFFSET), type_len);
-}
-
-
-
 #define OF_U16_LEN_LENGTH_OFFSET 0
 
 /**
@@ -871,6 +714,8 @@
         switch (subtype) {
         case 1: return OF_BSN_LACP_STATS_REQUEST;
         case 6: return OF_BSN_SWITCH_PIPELINE_STATS_REQUEST;
+        case 8: return OF_BSN_PORT_COUNTER_STATS_REQUEST;
+        case 9: return OF_BSN_VLAN_COUNTER_STATS_REQUEST;
         }
     }
     return OF_OBJECT_INVALID;
@@ -884,6 +729,8 @@
         switch (subtype) {
         case 1: return OF_BSN_LACP_STATS_REPLY;
         case 6: return OF_BSN_SWITCH_PIPELINE_STATS_REPLY;
+        case 8: return OF_BSN_PORT_COUNTER_STATS_REPLY;
+        case 9: return OF_BSN_VLAN_COUNTER_STATS_REPLY;
         }
     }
     return OF_OBJECT_INVALID;
diff --git a/c_gen/translation.py b/c_gen/translation.py
index ef6c11b..fb64fa9 100644
--- a/c_gen/translation.py
+++ b/c_gen/translation.py
@@ -89,7 +89,7 @@
         dict(OFPM_ = "OF_METER_"),
         dict(OFPXMC_ = "OF_OXM_CLASS_"),
         dict(OFPVID_ = "OF_VLAN_TAG_"),
-        dict(OFPGC_ = "OF_GROUP_"),
+        dict(OFPGC_ = "OF_GROUP_MOD_COMMAND_"),
         dict(OFPGT_ = "OF_GROUP_TYPE_"),
         dict(OFPG_ = "OF_GROUP_"),
         dict(OFPET_ = "OF_ERROR_TYPE_"),
diff --git a/c_gen/type_maps.py b/c_gen/type_maps.py
index 14e7048..f285a33 100644
--- a/c_gen/type_maps.py
+++ b/c_gen/type_maps.py
@@ -38,6 +38,7 @@
 from generic_utils import *
 import loxi_utils.loxi_utils as loxi_utils
 import c_gen.loxi_utils_legacy as loxi_utils
+import loxi_globals
 
 invalid_type = "invalid_type"
 invalid_value = "0xeeee"  # Note, as a string
@@ -60,21 +61,25 @@
     of_g.VERSION_1_3:dict()
     }
 
-# HACK shared between actions and action_ids
-of_1_3_action_types = dict()
+instruction_id_types = {
+    of_g.VERSION_1_0:dict(),
+    of_g.VERSION_1_1:dict(),
+    of_g.VERSION_1_2:dict(),
+    of_g.VERSION_1_3:dict()
+    }
 
 action_types = {
     of_g.VERSION_1_0:dict(),
     of_g.VERSION_1_1:dict(),
     of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:of_1_3_action_types
+    of_g.VERSION_1_3:dict(),
     }
 
 action_id_types = {
     of_g.VERSION_1_0:dict(),
     of_g.VERSION_1_1:dict(),
     of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:of_1_3_action_types
+    of_g.VERSION_1_3:dict(),
     }
 
 queue_prop_types = {
@@ -134,6 +139,7 @@
 # All inheritance data for non-messages
 inheritance_data = dict(
     of_instruction = instruction_types,
+    of_instruction_id = instruction_id_types,
     of_action = action_types,
     of_action_id = action_id_types,
     of_oxm = oxm_types,
@@ -149,16 +155,11 @@
     """
     Returns True if cls is a virtual class
     """
-    if cls in inheritance_map:
-        return True
     if cls.find("header") > 0:
         return True
     if loxi_utils.class_is_list(cls):
         return True
-    # TODO get this from the input file when we have virtual class syntax
-    if cls in ["of_flow_mod", "of_stats_request", "of_stats_reply", "of_error_msg", "of_bsn_header", "of_nicira_header", "of_action_bsn", "of_action_nicira", "of_action_id_bsn", "of_action_id_nicira", "of_bsn_stats_request", "of_bsn_stats_reply", "of_experimenter_stats_request", "of_experimenter_stats_reply", "of_instruction_experimenter", "of_instruction_bsn"]:
-        return True
-    return False
+    return loxi_globals.unified.class_by_name(cls).virtual
 
 ################################################################
 #
@@ -182,6 +183,7 @@
         error_msg               = 1,
         experimenter            = 4,
         flow_mod                = 14,
+        group_mod               = 15,
         stats_request           = 18,
         stats_reply             = 19,
         ),
@@ -191,6 +193,7 @@
         error_msg               = 1,
         experimenter            = 4,
         flow_mod                = 14,
+        group_mod               = 15,
         stats_request           = 18,
         stats_reply             = 19,
         ),
@@ -200,6 +203,7 @@
         error_msg               = 1,
         experimenter            = 4,
         flow_mod                = 14,
+        group_mod               = 15,
         stats_request           = 18,  # FIXME Multipart
         stats_reply             = 19,
         )
@@ -269,7 +273,9 @@
         port_desc = 13,
         experimenter = 0xffff,
         bsn_lacp = 0xffff,
-        bsn_switch_pipeline = 0xffff
+        bsn_switch_pipeline = 0xffff,
+        bsn_port_counter = 0xffff,
+        bsn_vlan_counter = 0xffff
         )
     }
 
@@ -353,6 +359,32 @@
         )
     }
 
+group_mod_types = {
+    # version 1.0
+    of_g.VERSION_1_0:dict(),
+
+    # version 1.1
+    of_g.VERSION_1_1:dict(
+        add = 0,
+        modify = 1,
+        delete = 2
+        ),
+
+    # version 1.2
+    of_g.VERSION_1_2:dict(
+        add = 0,
+        modify = 1,
+        delete = 2
+        ),
+
+    # version 1.3
+    of_g.VERSION_1_3:dict(
+        add = 0,
+        modify = 1,
+        delete = 2
+        )
+    }
+
 ##
 # These are the objects whose length is specified by an external
 # reference, specifically another data member in the class.
@@ -458,6 +490,8 @@
     "of_bsn_stats_reply",
     "of_bsn_lacp_stats_reply",
     "of_bsn_switch_pipeline_stats_reply",
+    "of_bsn_port_counter_stats_reply",
+    "of_bsn_vlan_counter_stats_reply",
 ]
 
 stats_request_list = [
@@ -479,6 +513,8 @@
     "of_bsn_stats_request",
     "of_bsn_lacp_stats_request",
     "of_bsn_switch_pipeline_stats_request",
+    "of_bsn_port_counter_stats_request",
+    "of_bsn_vlan_counter_stats_request",
 ]
 
 flow_mod_list = [
@@ -507,6 +543,12 @@
     "of_experimenter_error_msg"
 ]
 
+group_mod_list = [
+    "of_group_add",
+    "of_group_modify",
+    "of_group_delete",
+]
+
 def sub_class_map(base_type, version):
     """
     Returns an iterable object giving the instance nameys and subclass types
diff --git a/java_gen/java_model.py b/java_gen/java_model.py
index 8f5434e..f1c8c12 100644
--- a/java_gen/java_model.py
+++ b/java_gen/java_model.py
@@ -201,7 +201,7 @@
 
         factories = OrderedDict()
 
-        sub_factory_classes = ("OFAction", "OFInstruction", "OFMeterBand", "OFOxm", "OFQueueProp")
+        sub_factory_classes = ("OFAction", "OFInstruction", "OFMeterBand", "OFOxm", "OFQueueProp", "OFErrorMsg", "OFActionId", "OFInstructionId")
         for base_class in sub_factory_classes:
             package = base_class[2:].lower()
             remove_prefix = base_class[2].lower() + base_class[3:]
@@ -210,7 +210,7 @@
             annotated_base_class = base_class + "<?>" if base_class == "OFOxm" else base_class
 
             factories[base_class] = OFFactory(package="%s.%s" % (prefix, package),
-                    name=base_class + "s", members=[], remove_prefix=remove_prefix, base_class=annotated_base_class, sub_factories={}, xid_generator=False)
+                    name=base_class + "s", members=[], remove_prefix=remove_prefix, base_class=annotated_base_class, sub_factories={}, xid_generator= (base_class == "OFErrorMsg"))
 
         factories[""] = OFFactory(
                     package=prefix,
@@ -446,7 +446,7 @@
             else:
                 return ("", "OFStatsReply", None)
         elif self.ir_class.is_subclassof('of_error_msg'):
-            return ("", "OFErrorMsg", None)
+            return ("errormsg", "OFErrorMsg", None)
         elif self.ir_class.is_subclassof('of_flow_mod'):
             return ("", "OFFlowMod", None)
         elif self.ir_class.is_subclassof('of_group_mod'):
@@ -470,6 +470,15 @@
                 return ("action", "OFActionExperimenter", None)
             else:
                 return ("action", "OFAction", None)
+        elif self.ir_class.is_instanceof("of_action_id"):
+            if self.ir_class.is_subclassof('of_action_id_bsn'):
+                return ("actionid", "OFActionIdBsn", None)
+            elif self.ir_class.is_subclassof('of_action_id_nicira'):
+                return ("actionid", "OFActionIdNicira", None)
+            elif self.ir_class.is_subclassof('of_action_id_experimenter'):
+                return ("actionid", "OFActionIdExperimenter", None)
+            else:
+                return ("actionid", "OFActionId", None)
         elif self.ir_class.is_instruction:
             if self.ir_class.is_subclassof('of_instruction_bsn'):
                 return ("instruction", "OFInstructionBsn", None)
@@ -477,6 +486,13 @@
                 return ("instruction", "OFInstructionExperimenter", None)
             else:
                 return ("instruction", "OFInstruction", None)
+        elif self.ir_class.is_instanceof('of_instruction_id'):
+            if self.ir_class.is_subclassof('of_instruction_id_bsn'):
+                return ("instructionid", "OFInstructionIdBsn", None)
+            elif self.ir_class.is_subclassof('of_instruction_id_experimenter'):
+                return ("instructionid", "OFInstructionIdExperimenter", None)
+            else:
+                return ("instructionid", "OFInstructionId", None)
         elif re.match(r'OFBsnVport.+$', self.name):
             return ("", "OFBsnVport", None)
         elif self.name == "OFOxm":
diff --git a/java_gen/java_type.py b/java_gen/java_type.py
index eca00e7..1bed988 100644
--- a/java_gen/java_type.py
+++ b/java_gen/java_type.py
@@ -298,6 +298,12 @@
                 write='ChannelUtils.writeList(bb, $name)',
                 default="ImmutableList.<U32>of()",
                 funnel="FunnelUtils.putList($name, sink)")
+u64_list = JType('List<U64>', 'int[]') \
+        .op(
+                read='ChannelUtils.readList(bb, $length, U64.READER)',
+                write='ChannelUtils.writeList(bb, $name)',
+                default="ImmutableList.<U64>of()",
+                funnel="FunnelUtils.putList($name, sink)")
 u8obj = JType('U8', 'U8') \
         .op(read='U8.of(bb.readByte())', write='bb.writeByte($name.getRaw())', default="U8.ZERO")
 u32obj = JType('U32', 'U32') \
@@ -489,6 +495,7 @@
         'list(of_bucket_t)': buckets_list,
         'list(of_port_desc_t)' : port_desc_list,
         'list(of_packet_queue_t)' : packet_queue_list,
+        'list(of_uint64_t)' : u64_list,
         'list(of_uint32_t)' : u32_list,
         'list(of_uint8_t)' : u8_list,
         'list(of_oxm_t)' : oxm_list,
diff --git a/java_gen/pre-written/pom.xml b/java_gen/pre-written/pom.xml
index 0cb42ac..567f006 100644
--- a/java_gen/pre-written/pom.xml
+++ b/java_gen/pre-written/pom.xml
@@ -10,7 +10,7 @@
 
     <groupId>org.projectfloodlight</groupId>
     <artifactId>openflowj</artifactId>
-    <version>0.3.1-SNAPSHOT</version>
+    <version>0.3.3-SNAPSHOT</version>
     <packaging>jar</packaging>
 
     <name>OpenFlowJ-Loxi</name>
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPAddress.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPAddress.java
index 7c50aed..c96be83 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPAddress.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPAddress.java
@@ -4,7 +4,32 @@
 
     public abstract IPVersion getIpVersion();
 
+    /**
+     * Checks if this IPAddress represents a valid CIDR style netmask, i.e.,
+     * it has a set of leading "1" bits followed by only "0" bits
+     * @return true if this represents a valid CIDR style netmask, false
+     * otherwise
+     */
+    public abstract boolean isCidrMask();
+
+    /**
+     * If this IPAddress represents a valid CIDR style netmask (see
+     * isCidrMask()) returns the length of the prefix (the number of "1" bits).
+     * @return length of CIDR mask if this represents a valid CIDR mask
+     * @throws IllegalStateException if isCidrMask() == false
+     */
+    public abstract int asCidrMaskLength();
+
+    @Override
+    public abstract boolean equals(Object other);
+
+    @Override
+    public abstract int hashCode();
+
     public static IPAddress<?> of(String ip) {
+        if (ip == null) {
+            throw new NullPointerException("String ip must not be null");
+        }
         if (ip.indexOf('.') != -1)
             return IPv4Address.of(ip);
         else if (ip.indexOf(':') != -1)
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPAddressWithMask.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPAddressWithMask.java
index 11ef103..2087ab4 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPAddressWithMask.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPAddressWithMask.java
@@ -10,6 +10,9 @@
     public abstract IPVersion getIpVersion();
 
     public static IPAddressWithMask<?> of(String ip) {
+        if (ip == null) {
+            throw new NullPointerException("String ip must not be null");
+        }
         if (ip.indexOf('.') != -1)
             return IPv4AddressWithMask.of(ip);
         else if (ip.indexOf(':') != -1)
@@ -18,4 +21,21 @@
             throw new IllegalArgumentException("IP Address not well formed: " + ip);
     }
 
+    @Override
+    public String toString() {
+        StringBuilder res = new StringBuilder();
+        res.append(value.toString());
+
+        res.append('/');
+        if (mask.isCidrMask()) {
+            // CIDR notation
+            res.append(mask.asCidrMaskLength());
+        } else {
+            // Full address mask
+            res.append(mask.toString());
+        }
+
+        return res.toString();
+    }
+
 }
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4Address.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4Address.java
index 51d10f3..a75c2ee 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4Address.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4Address.java
@@ -18,6 +18,11 @@
     static final int LENGTH = 4;
     private final int rawValue;
 
+    private static final int NOT_A_CIDR_MASK = -1;
+    private static final int CIDR_MASK_CACHE_UNSET = -2;
+    // Must appear before the static IPv4Address constant assignments
+    private volatile int cidrMaskLengthCache = CIDR_MASK_CACHE_UNSET;
+
     private final static int NONE_VAL = 0x0;
     public final static IPv4Address NONE = new IPv4Address(NONE_VAL);
 
@@ -33,7 +38,41 @@
         return IPVersion.IPv4;
     }
 
+    private int asCidrMaskLengthInternal() {
+        if (cidrMaskLengthCache == CIDR_MASK_CACHE_UNSET) {
+            // No lock required. We only write cidrMaskLengthCache once
+            int maskint = getInt();
+            if (maskint == 0) {
+                cidrMaskLengthCache = 0;
+            } else if (Integer.bitCount((~maskint) + 1) == 1) {
+                // IP represents a true CIDR prefix length
+                cidrMaskLengthCache = Integer.bitCount(maskint);
+            } else {
+                cidrMaskLengthCache = NOT_A_CIDR_MASK;
+            }
+        }
+        return cidrMaskLengthCache;
+    }
+
+    @Override
+    public boolean isCidrMask() {
+        return asCidrMaskLengthInternal() != NOT_A_CIDR_MASK;
+    }
+
+    @Override
+    public int asCidrMaskLength() {
+        if (!isCidrMask()) {
+            throw new IllegalStateException("IP is not a valid CIDR prefix " +
+                    "mask " + toString());
+        } else {
+            return asCidrMaskLengthInternal();
+        }
+    }
+
     public static IPv4Address of(final byte[] address) {
+        if (address == null) {
+            throw new NullPointerException("Address must not be null");
+        }
         if (address.length != LENGTH) {
             throw new IllegalArgumentException(
                     "Invalid byte array length for IPv4Address address: " + address.length);
@@ -52,6 +91,9 @@
     }
 
     public static IPv4Address of(final String string) {
+        if (string == null) {
+            throw new NullPointerException("String must not be null");
+        }
         int start = 0;
         int shift = 24;
 
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4AddressWithMask.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4AddressWithMask.java
index f30fcbb..9b60c6a 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4AddressWithMask.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4AddressWithMask.java
@@ -22,28 +22,19 @@
     }
 
     public static IPv4AddressWithMask of(IPv4Address value, IPv4Address mask) {
+        if (value == null) {
+            throw new NullPointerException("Value must not be null");
+        }
+        if (mask == null) {
+            throw new NullPointerException("Mask must not be null");
+        }
         return new IPv4AddressWithMask(value, mask);
     }
 
-    @Override
-    public String toString() {
-        StringBuilder res = new StringBuilder();
-        res.append(value.toString());
-
-        int maskint = mask.getInt();
-        res.append('/');
-        if (Integer.bitCount((~maskint) + 1) == 1) {
-            // CIDR notation
-            res.append(Integer.bitCount(maskint));
-        } else {
-            // Full address mask
-            res.append(mask.toString());
-        }
-
-        return res.toString();
-    }
-
     public static IPv4AddressWithMask of(final String string) {
+        if (string == null) {
+            throw new NullPointerException("String must not be null");
+        }
         int slashPos;
         String ip = string;
         int maskBits = 32;
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6Address.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6Address.java
index 1aad85b..4e7b856 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6Address.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6Address.java
@@ -4,7 +4,6 @@
 
 import org.jboss.netty.buffer.ChannelBuffer;
 import org.projectfloodlight.openflow.exceptions.OFParseError;
-
 import com.google.common.hash.PrimitiveSink;
 import com.google.common.primitives.Longs;
 
@@ -19,10 +18,16 @@
     private final long raw1;
     private final long raw2;
 
+    private static final int NOT_A_CIDR_MASK = -1;
+    private static final int CIDR_MASK_CACHE_UNSET = -2;
+    // Must appear before the static IPv4Address constant assignments
+    private volatile int cidrMaskLengthCache = CIDR_MASK_CACHE_UNSET;
+
     private final static long NONE_VAL1 = 0x0L;
     private final static long NONE_VAL2 = 0x0L;
     public static final IPv6Address NONE = new IPv6Address(NONE_VAL1, NONE_VAL2);
 
+
     public static final IPv6Address NO_MASK = IPv6Address.of(0xFFFFFFFFFFFFFFFFl, 0xFFFFFFFFFFFFFFFFl);
     public static final IPv6Address FULL_MASK = IPv6Address.of(0x0, 0x0);
 
@@ -36,7 +41,60 @@
         return IPVersion.IPv6;
     }
 
+
+    private int computeCidrMask64(long raw) {
+        long mask = raw;
+        if (raw == 0)
+            return 0;
+        else if (Long.bitCount((~mask) + 1) == 1) {
+            // represent a true CIDR prefix length
+            return Long.bitCount(mask);
+        }
+        else {
+            // Not a true prefix
+            return NOT_A_CIDR_MASK;
+        }
+    }
+
+    private int asCidrMaskLengthInternal() {
+        if (cidrMaskLengthCache == CIDR_MASK_CACHE_UNSET) {
+            // No synchronization needed. Writing cidrMaskLengthCache only once
+            if (raw1 == 0 && raw2 == 0) {
+                cidrMaskLengthCache = 0;
+            } else if (raw1 == -1L) {
+                // top half is all 1 bits
+                int tmpLength = computeCidrMask64(raw2);
+                if (tmpLength != NOT_A_CIDR_MASK)
+                    tmpLength += 64;
+                cidrMaskLengthCache = tmpLength;
+            } else if (raw2 == 0) {
+                cidrMaskLengthCache = computeCidrMask64(raw1);
+            } else {
+                cidrMaskLengthCache = NOT_A_CIDR_MASK;
+            }
+        }
+        return cidrMaskLengthCache;
+    }
+
+    @Override
+    public boolean isCidrMask() {
+        return asCidrMaskLengthInternal() != NOT_A_CIDR_MASK;
+    }
+
+    @Override
+    public int asCidrMaskLength() {
+        if (!isCidrMask()) {
+            throw new IllegalStateException("IP is not a valid CIDR prefix " +
+                    "mask " + toString());
+        } else {
+            return asCidrMaskLengthInternal();
+        }
+    }
+
     public static IPv6Address of(final byte[] address) {
+        if (address == null) {
+            throw new NullPointerException("Address must not be null");
+        }
         if (address.length != LENGTH) {
             throw new IllegalArgumentException(
                     "Invalid byte array length for IPv6 address: " + address.length);
@@ -82,6 +140,9 @@
     private final static Pattern colonPattern = Pattern.compile(":");
 
     public static IPv6Address of(final String string) {
+        if (string == null) {
+            throw new NullPointerException("String must not be null");
+        }
         IPv6Builder builder = new IPv6Builder();
         String[] parts = colonPattern.split(string, -1);
 
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6AddressWithMask.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6AddressWithMask.java
index 6faf0b8..7259c7f 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6AddressWithMask.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6AddressWithMask.java
@@ -16,28 +16,20 @@
     }
 
     public static IPv6AddressWithMask of(IPv6Address value, IPv6Address mask) {
+        if (value == null) {
+            throw new NullPointerException("Value must not be null");
+        }
+        if (mask == null) {
+            throw new NullPointerException("Mask must not be null");
+        }
         return new IPv6AddressWithMask(value, mask);
     }
 
-    @Override
-    public String toString() {
-        StringBuilder res = new StringBuilder();
-        res.append(value.toString());
-        res.append('/');
-
-        BigInteger maskint = new BigInteger(mask.getBytes());
-        if (maskint.not().add(BigInteger.ONE).bitCount() == 1) {
-            // CIDR notation
-            res.append(maskint.bitCount());
-        } else {
-            // Full address mask
-            res.append(mask.toString());
-        }
-
-        return res.toString();
-    }
 
     public static IPv6AddressWithMask of(final String string) {
+        if (string == null) {
+            throw new NullPointerException("String must not be null");
+        }
         int slashPos;
         String ip = string;
         int maskBits = 128;
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/Masked.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/Masked.java
index ea2317a..b5a995d 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/Masked.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/Masked.java
@@ -5,8 +5,13 @@
 
 
 public class Masked<T extends OFValueType<T>> implements OFValueType<Masked<T>> {
-    protected T value;
-    protected T mask;
+    protected final T value;
+
+    /** bitmask of the value. Note: a set (1) bit in this mask means 'match on this value'.
+     *  This the natural mask represenation as in IPv[46] netmasks. It is the inverse of the
+     *  OpenFlow 1.0 'wildcard' meaning.
+     */
+    protected final T mask;
 
     protected Masked(T value, T mask) {
         this.value = value.applyMask(mask);
@@ -38,6 +43,21 @@
         return sb.toString();
     }
 
+    /** Determine whether candidate value is matched by this masked value
+     *  (i.e., does candiate lie in the 'network/range' specified by this masked
+     *  value).
+     *
+     * @param candidate the candidate value to test
+     * @return true iff the candidate lies in the area specified by this masked
+     *         value.
+     */
+    public boolean matches(T candidate) {
+        // candidate lies in the area of this masked value if its
+        // value with the masked bit zero'ed out equals this's value
+        // (e.g., our 'network address' for networks)
+        return candidate.applyMask(this.mask).equals(this.value);
+    }
+
     @Override
     public Masked<T> applyMask(Masked<T> mask) {
         return this;
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U64.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U64.java
index d77d700..f480c47 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U64.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U64.java
@@ -20,6 +20,8 @@
 import java.math.BigInteger;
 
 import org.jboss.netty.buffer.ChannelBuffer;
+import org.projectfloodlight.openflow.exceptions.OFParseError;
+import org.projectfloodlight.openflow.protocol.OFMessageReader;
 import org.projectfloodlight.openflow.protocol.Writeable;
 
 import com.google.common.hash.PrimitiveSink;
@@ -125,4 +127,13 @@
     public void putTo(PrimitiveSink sink) {
         sink.putLong(raw);
     }
+
+    public final static Reader READER = new Reader();
+
+    private static class Reader implements OFMessageReader<U64> {
+        @Override
+        public U64 readFrom(ChannelBuffer bb) throws OFParseError {
+            return U64.ofRaw(bb.readLong());
+        }
+    }
 }
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPAddressTest.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPAddressTest.java
new file mode 100644
index 0000000..25fc943
--- /dev/null
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPAddressTest.java
@@ -0,0 +1,54 @@
+package org.projectfloodlight.openflow.types;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Most tests are in IPv4AddressTest and IPv6AddressTest
+ * Just exception testing here
+ * @author gregor
+ *
+ */
+public class IPAddressTest {
+    @Test
+    public void testOfException() {
+        try {
+            IPAddress.of("Foobar");
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            IPAddressWithMask.of("Foobar");
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            IPAddress.of(null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            IPAddressWithMask.of(null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            IPAddress.of(null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            IPAddressWithMask.of(null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+    }
+
+}
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv4AddressTest.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv4AddressTest.java
index 38d60b3..334ec0d 100644
--- a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv4AddressTest.java
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv4AddressTest.java
@@ -1,8 +1,12 @@
 package org.projectfloodlight.openflow.types;
 
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import org.hamcrest.CoreMatchers;
@@ -38,6 +42,7 @@
             "1.2..3.4",
             "1.2.3.4.",
             "257.11.225.1",
+            "256.11.225.1",
             "-1.2.3.4",
             "1.2.3.4.5",
             "1.x.3.4",
@@ -49,7 +54,10 @@
                             "192.168.130.140/255.255.192.0",
                             "127.0.0.1/8",
                             "8.8.8.8",
-                            "0.0.0.0/0"
+                            "8.8.8.8/32",
+                            "0.0.0.0/0",
+                            "192.168.130.140/255.0.255.0",
+                            "1.2.3.4/0.127.0.255"
     };
 
     boolean[] hasMask = {
@@ -57,6 +65,9 @@
                          true,
                          true,
                          false,
+                         false,
+                         true,
+                         true,
                          true
     };
 
@@ -64,10 +75,110 @@
                              new byte[][] { new byte[] { (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04 }, new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00 } },
                              new byte[][] { new byte[] { (byte)0xC0, (byte)0xA8, (byte)0x82, (byte)0x8C }, new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xC0, (byte)0x00 } },
                              new byte[][] { new byte[] { (byte)0x7F, (byte)0x00, (byte)0x00, (byte)0x01 }, new byte[] { (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00 } },
-                             new byte[][] { new byte[] { (byte)0x08, (byte)0x08, (byte)0x08, (byte)0x08 }, null },
-                             new byte[][] { new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 }, new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 } }
+                             new byte[][] { new byte[] { (byte)0x08, (byte)0x08, (byte)0x08, (byte)0x08 }, new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF } },
+                             new byte[][] { new byte[] { (byte)0x08, (byte)0x08, (byte)0x08, (byte)0x08 }, new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF } },
+                             new byte[][] { new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 }, new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 } },
+                             new byte[][] { new byte[] { (byte)0xC0, (byte)0xA8, (byte)0x82, (byte)0x8C }, new byte[] { (byte)0xFF, (byte)0x00, (byte)0xFF, (byte)0x00 } },
+                             new byte[][] { new byte[] { (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04 }, new byte[] { (byte)0x00, (byte)0x7F, (byte)0x00, (byte)0xFF } }
     };
 
+    int[] ipsWithMaskLengths = {
+                                24,
+                                18,
+                                8,
+                                32,
+                                32,
+                                0,
+                                -1,
+                                -1
+    };
+
+    String[] invalidIpsWithMask = {
+                                   "asdf",
+                                   "1.2.3.4/33",
+                                   "1.2.3.4/34",
+                                   "1.2.3.4/-1",
+                                   "1.2.3.4/256.0.0.0",
+                                   "1.256.3.4/255.255.0.0",
+                                   "1.2.3.4/255.255.0.0.0",
+    };
+
+    @Test
+    public void testMaskedMatchesCidr() {
+        IPv4AddressWithMask slash28 = IPv4AddressWithMask.of("10.0.42.16/28");
+
+        String[] notContained = {"0.0.0.0", "11.0.42.16", "10.0.41.1", "10.0.42.0", "10.0.42.15",
+                                 "10.0.42.32", "255.255.255.255" };
+
+        for(String n: notContained) {
+            assertThat(String.format("slash 28 %s should not contain address %s",
+                                     slash28, n),
+                    slash28.matches(IPv4Address.of(n)), equalTo(false));
+        }
+        for(int i=16; i < 32; i++) {
+            IPv4Address c = IPv4Address.of(String.format("10.0.42.%d", i));
+            assertThat(String.format("slash 28 %s should contain address %s",
+                                     slash28, c),
+                       slash28.matches(c), equalTo(true));
+        }
+    }
+
+    @Test
+    public void testMaskedMatchesArbitrary() {
+        // irregular octect on the 3rd bitmask requires '1'bit to be set
+        // 4 bit unset, all others arbitrary
+        IPv4AddressWithMask slash28 = IPv4AddressWithMask.of("1.2.1.4/255.255.5.255");
+
+        String[] notContained = {"0.0.0.0", "1.2.3.5", "1.2.3.3",
+                                 "1.2.0.4", "1.2.2.4", "1.2.4.4", "1.2.5.4", "1.2.6.4", "1.2.7.4",
+                                 "1.2.8.4", "1.2.12.4", "1.2.13.4"
+                                 };
+        String[] contained = {"1.2.1.4", "1.2.3.4", "1.2.9.4", "1.2.11.4", "1.2.251.4",
+                };
+
+        for(String n: notContained) {
+            assertThat(String.format("slash 28 %s should not contain address %s",
+                                     slash28, n),
+                    slash28.matches(IPv4Address.of(n)), equalTo(false));
+        }
+        for(String c: contained) {
+            IPv4Address addr = IPv4Address.of(c);
+            assertThat(String.format("slash 28 %s should contain address %s",
+                                     slash28, addr),
+                       slash28.matches(addr), equalTo(true));
+        }
+
+    }
+
+
+    @Test
+    public void testConstants() {
+        byte[] zeros = { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };
+        byte[] ones =  { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF };
+        // Make sure class initializtation and static assignment don't get
+        // messed up. Test everything twice for cached values
+        assertTrue(IPv4Address.NONE.isCidrMask());
+        assertEquals(0, IPv4Address.NONE.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv4Address.NONE.getBytes());
+        assertTrue(IPv4Address.NONE.isCidrMask());
+        assertEquals(0, IPv4Address.NONE.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv4Address.NONE.getBytes());
+
+        assertTrue(IPv4Address.NO_MASK.isCidrMask());
+        assertEquals(32, IPv4Address.NO_MASK.asCidrMaskLength());
+        assertArrayEquals(ones, IPv4Address.NO_MASK.getBytes());
+        assertTrue(IPv4Address.NO_MASK.isCidrMask());
+        assertEquals(32, IPv4Address.NO_MASK.asCidrMaskLength());
+        assertArrayEquals(ones, IPv4Address.NO_MASK.getBytes());
+
+        assertTrue(IPv4Address.FULL_MASK.isCidrMask());
+        assertEquals(0, IPv4Address.FULL_MASK.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv4Address.FULL_MASK.getBytes());
+        assertTrue(IPv4Address.FULL_MASK.isCidrMask());
+        assertEquals(0, IPv4Address.FULL_MASK.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv4Address.FULL_MASK.getBytes());
+    }
+
 
     @Test
     public void testOfString() {
@@ -119,18 +230,111 @@
             if (!hasMask[i]) {
                 IPv4Address ip = value.getValue();
                 assertArrayEquals(ipsWithMaskValues[i][0], ip.getBytes());
-            } else if (hasMask[i]) {
-                byte[] ipBytes = new byte[4];
-                System.arraycopy(ipsWithMaskValues[i][0], 0, ipBytes, 0, 4);
-                assertEquals(ipBytes.length, value.getValue().getBytes().length);
-                for (int j = 0; j < ipBytes.length; j++) {
-                    ipBytes[j] &= ipsWithMaskValues[i][1][j];
-                }
-
-                assertArrayEquals(ipBytes, value.getValue().getBytes());
-                assertThat(String.format("Byte comparison for mask of %s (%s)", ipsWithMask[i], value),
-                        value.getMask().getBytes(), CoreMatchers.equalTo(ipsWithMaskValues[i][1]));
             }
+            IPv4Address mask = value.getMask();
+            if (ipsWithMaskLengths[i] == -1) {
+                assertFalse(mask.isCidrMask());
+                try {
+                    mask.asCidrMaskLength();
+                    fail("Expected IllegalStateException not thrown");
+                } catch(IllegalStateException e) {
+                    //expected
+                }
+            } else {
+                assertTrue(mask.isCidrMask());
+                assertEquals(ipsWithMaskLengths[i], mask.asCidrMaskLength());
+            }
+            assertArrayEquals(ipsWithMaskValues[i][1], mask.getBytes());
+            byte[] ipBytes = new byte[4];
+            System.arraycopy(ipsWithMaskValues[i][0], 0, ipBytes, 0, 4);
+            assertEquals(ipBytes.length, value.getValue().getBytes().length);
+            for (int j = 0; j < ipBytes.length; j++) {
+                ipBytes[j] &= ipsWithMaskValues[i][1][j];
+            }
+
+            assertArrayEquals(ipBytes, value.getValue().getBytes());
+            assertThat(String.format("Byte comparison for mask of %s (%s)", ipsWithMask[i], value),
+                    value.getMask().getBytes(), CoreMatchers.equalTo(ipsWithMaskValues[i][1]));
+        }
+    }
+
+    @Test
+    public void testOfMaskedInvalid() throws Exception {
+        for(String invalid : invalidIpsWithMask) {
+            try {
+                IPv4Address.of(invalid);
+                fail("Invalid IP "+invalid+ " should have raised IllegalArgumentException");
+            } catch(IllegalArgumentException e) {
+                // ok
+            }
+        }
+    }
+
+    @Test
+    public void testSuperclass() throws Exception {
+        for(String ipString: testStrings) {
+            IPAddress<?> superIp = IPAddress.of(ipString);
+            assertEquals(IPVersion.IPv4, superIp.getIpVersion());
+            assertEquals(IPv4Address.of(ipString), superIp);
+        }
+
+        for(String ipMaskedString: ipsWithMask) {
+            IPAddressWithMask<?> superIp = IPAddressWithMask.of(ipMaskedString);
+            assertEquals(IPVersion.IPv4, superIp.getIpVersion());
+            assertEquals(IPv4AddressWithMask.of(ipMaskedString), superIp);
+        }
+    }
+
+    @Test
+    public void testOfExceptions() {
+        // We check if the message of a caught NPE is set to a useful message
+        // as a hacky way of verifying that we got an NPE thrown by use rather
+        // than one the JVM created for a null access.
+        try {
+            String s = null;
+            IPv4Address.of(s);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            byte[] b = null;
+            IPv4Address.of(b);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            byte[] b = new byte[3];
+            IPv4Address.of(b);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            byte[] b = new byte[5];
+            IPv4Address.of(b);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            IPv4AddressWithMask.of(null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            IPv4AddressWithMask.of(IPv4Address.of("1.2.3.4"), null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            IPv4AddressWithMask.of(null, IPv4Address.of("255.0.0.0"));
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
         }
     }
 }
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv6AddressTest.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv6AddressTest.java
index 6eb5743..c521292 100644
--- a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv6AddressTest.java
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv6AddressTest.java
@@ -1,9 +1,6 @@
 package org.projectfloodlight.openflow.types;
 
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.*;
 
 import java.net.Inet6Address;
 import java.net.InetAddress;
@@ -31,6 +28,7 @@
     private class WithMaskTaskCase {
         final String input;
         boolean hasMask;
+        int expectedMaskLength = 128;
         byte[] expectedMask = hex.decode("ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff".replaceAll(" ", ""));
 
         public WithMaskTaskCase(String input) {
@@ -45,22 +43,73 @@
             return this;
         }
 
+        public WithMaskTaskCase expectedMaskLength(int expectedLength) {
+            this.expectedMaskLength = expectedLength;
+            return this;
+        }
+
     }
 
     WithMaskTaskCase[] withMasks = new WithMaskTaskCase[] {
             new WithMaskTaskCase("1::1/80")
-                .maskHex("ff ff ff ff ff ff ff ff ff ff 00 00 00 00 00 00"),
+                .maskHex("ff ff ff ff ff ff ff ff ff ff 00 00 00 00 00 00")
+                .expectedMaskLength(80),
 
             new WithMaskTaskCase("ffff:ffee:1::/ff00:ff00:ff00:ff00::")
-                .maskHex("ff 00 ff 00 ff 00 ff 00 00 00 00 00 00 00 00 00"),
+                .maskHex("ff 00 ff 00 ff 00 ff 00 00 00 00 00 00 00 00 00")
+                .expectedMaskLength(-1),
+            new WithMaskTaskCase("1:2:3:4:5:6:7:8/1::ff00:ff00")
+                .maskHex("00 01 00 00 00 00 00 00 00 00 00 00 ff 00 ff 00")
+                .expectedMaskLength(-1),
+            new WithMaskTaskCase("1:2:3:4:5:6:7:8/::ff00:ff00")
+                .maskHex("00 00 00 00 00 00 00 00 00 00 00 00 ff 00 ff 00")
+                .expectedMaskLength(-1),
+            new WithMaskTaskCase("1:2:3:4:5:6:7:8/ffff:ffff:ffff:ffff:ffff::ff00:ff00")
+                .maskHex("ff ff ff ff ff ff ff ff ff ff 00 00 ff 00 ff 00")
+                .expectedMaskLength(-1),
             new WithMaskTaskCase("8:8:8:8:8:8:8:8"),
             new WithMaskTaskCase("8:8:8:8:8:8:8:8"),
             new WithMaskTaskCase("1:2:3:4:5:6:7:8/128"),
             new WithMaskTaskCase("::/0")
                 .maskHex("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00")
+                .expectedMaskLength(0),
     };
 
     @Test
+    public void testConstants() {
+        byte[] zeros = { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+                         (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+                         (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+                         (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };
+        byte[] ones = { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
+                        (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
+                        (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
+                        (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF };
+        // Make sure class initializtation and static assignment don't get
+        // messed up. Test everything twice for cached values
+        assertTrue(IPv6Address.NONE.isCidrMask());
+        assertEquals(0, IPv6Address.NONE.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv6Address.NONE.getBytes());
+        assertTrue(IPv6Address.NONE.isCidrMask());
+        assertEquals(0, IPv6Address.NONE.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv6Address.NONE.getBytes());
+
+        assertTrue(IPv6Address.NO_MASK.isCidrMask());
+        assertEquals(128, IPv6Address.NO_MASK.asCidrMaskLength());
+        assertArrayEquals(ones, IPv6Address.NO_MASK.getBytes());
+        assertTrue(IPv6Address.NO_MASK.isCidrMask());
+        assertEquals(128, IPv6Address.NO_MASK.asCidrMaskLength());
+        assertArrayEquals(ones, IPv6Address.NO_MASK.getBytes());
+
+        assertTrue(IPv6Address.FULL_MASK.isCidrMask());
+        assertEquals(0, IPv6Address.FULL_MASK.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv6Address.FULL_MASK.getBytes());
+        assertTrue(IPv6Address.FULL_MASK.isCidrMask());
+        assertEquals(0, IPv6Address.FULL_MASK.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv6Address.FULL_MASK.getBytes());
+    }
+
+    @Test
     public void testMasked() throws UnknownHostException {
         for(WithMaskTaskCase w: withMasks) {
             IPv6AddressWithMask value = IPv6AddressWithMask.of(w.input);
@@ -70,19 +119,37 @@
 
                 assertArrayEquals(ip.getBytes(), inetAddress.getAddress());
                 assertEquals(w.input.split("/")[0], ip.toString());
-            } else {
-                InetAddress inetAddress = InetAddress.getByName(w.input.substring(0, w.input.indexOf('/')));
-
-                byte[] address = inetAddress.getAddress();
-                assertEquals(address.length, value.getValue().getBytes().length);
-
-                for (int j = 0; j < address.length; j++) {
-                    address[j] &= w.expectedMask[j];
-                }
-
-                assertThat("Address bytes for input " + w.input + ", value=" + value, value.getValue().getBytes(), CoreMatchers.equalTo(address));
-                assertThat("mask check for input " + w.input + ", value=" + value, value.getMask().getBytes(), CoreMatchers.equalTo(w.expectedMask));
             }
+            InetAddress inetAddress = InetAddress.getByName(w.input.split("/")[0]);
+
+            if (w.expectedMaskLength == -1) {
+                assertFalse(value.getMask().isCidrMask());
+                try {
+                    value.getMask().asCidrMaskLength();
+                    fail("Expected IllegalStateException not thrown");
+                } catch(IllegalStateException e) {
+                    //expected
+                }
+            } else {
+                assertTrue(value.getMask().isCidrMask());
+                assertEquals("Input " + w.input, w.expectedMaskLength,
+                             value.getMask().asCidrMaskLength());
+            }
+
+            byte[] address = inetAddress.getAddress();
+            assertEquals(address.length, value.getValue().getBytes().length);
+
+            for (int j = 0; j < address.length; j++) {
+                address[j] &= w.expectedMask[j];
+            }
+
+            assertThat("Address bytes for input " + w.input + ", value=" + value, value.getValue().getBytes(), CoreMatchers.equalTo(address));
+            assertThat("mask check for input " + w.input + ", value=" + value, value.getMask().getBytes(), CoreMatchers.equalTo(w.expectedMask));
+        }
+        for (int i = 0; i <= 128; i++) {
+            String ipString = String.format("8001:2::1/%d", i);
+            IPv6AddressWithMask value = IPv6AddressWithMask.of(ipString);
+            assertEquals("Input " + ipString, i, value.getMask().asCidrMaskLength());
         }
     }
 
@@ -150,4 +217,70 @@
         assertEquals("1::4:5:6:0:8", IPv6Address.of("1:0:0:4:5:6:0:8").toString(true, false));
         assertEquals("1:0:0:4::8", IPv6Address.of("1:0:0:4:0:0:0:8").toString(true, false));
     }
+
+    @Test
+    public void testSuperclass() throws Exception {
+        for(String ipString: testStrings) {
+            IPAddress<?> superIp = IPAddress.of(ipString);
+            assertEquals(IPVersion.IPv6, superIp.getIpVersion());
+            assertEquals(IPv6Address.of(ipString), superIp);
+        }
+
+        for(WithMaskTaskCase w: withMasks) {
+            String ipMaskedString = w.input;
+            IPAddressWithMask<?> superIp = IPAddressWithMask.of(ipMaskedString);
+            assertEquals(IPVersion.IPv6, superIp.getIpVersion());
+            assertEquals(IPv6AddressWithMask.of(ipMaskedString), superIp);
+        }
+    }
+
+    @Test
+    public void testOfExceptions() throws Exception {
+        try {
+            IPv6AddressWithMask.of(null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            String s = null;
+            IPv6Address.of(s);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            byte[] b = null;
+            IPv6Address.of(b);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            byte[] b = new byte[7];
+            IPv6Address.of(b);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            byte[] b = new byte[9];
+            IPv6Address.of(b);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            IPv6AddressWithMask.of(IPv6Address.of("1::"), null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            IPv6AddressWithMask.of(null, IPv6Address.of("255::"));
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+    }
 }
diff --git a/java_gen/templates/_imports.java b/java_gen/templates/_imports.java
index cf7334d..498d2f9 100644
--- a/java_gen/templates/_imports.java
+++ b/java_gen/templates/_imports.java
@@ -7,8 +7,11 @@
 import java.util.Map;
 import org.projectfloodlight.openflow.protocol.*;
 import org.projectfloodlight.openflow.protocol.action.*;
+import org.projectfloodlight.openflow.protocol.actionid.*;
+import org.projectfloodlight.openflow.protocol.errormsg.*;
 import org.projectfloodlight.openflow.protocol.meterband.*;
 import org.projectfloodlight.openflow.protocol.instruction.*;
+import org.projectfloodlight.openflow.protocol.instructionid.*;
 import org.projectfloodlight.openflow.protocol.match.*;
 import org.projectfloodlight.openflow.protocol.oxm.*;
 import org.projectfloodlight.openflow.protocol.queueprop.*;
diff --git a/lang_c.py b/lang_c.py
index 762f82c..7718d48 100644
--- a/lang_c.py
+++ b/lang_c.py
@@ -41,6 +41,7 @@
 import c_gen.c_show_gen as c_show_gen
 import c_gen.c_validator_gen as c_validator_gen
 import c_gen.util
+import c_gen.codegen
 import loxi_utils.loxi_utils as loxi_utils
 import template_utils
 
@@ -173,3 +174,4 @@
     for (name, fn) in targets.items():
         with template_utils.open_output(install_dir, name) as outfile:
             fn(outfile, os.path.basename(name))
+    c_gen.codegen.gen_push_wire_types(install_dir)
diff --git a/lang_python.py b/lang_python.py
index f40cecc..9087b43 100644
--- a/lang_python.py
+++ b/lang_python.py
@@ -50,6 +50,8 @@
             of12: ...           # (code generation incomplete)
                 oxm.py          # OXM classes
             of13: ...           # (code generation incomplete)
+                action_id.py    # Action ID classes
+                instruction_id.py # Instruction ID classes
                 meter_band.py   # Meter band classes
 
 The user will add the pyloxi directory to PYTHONPATH. Then they can
@@ -83,7 +85,7 @@
     1: ["action", "common", "const", "message", "util"],
     2: ["action", "common", "const", "instruction", "message", "util"],
     3: ["action", "common", "const", "instruction", "message", "oxm", "util"],
-    4: ["action", "common", "const", "instruction", "message", "meter_band", "oxm", "util"],
+    4: ["action", "action_id", "common", "const", "instruction", "instruction_id", "message", "meter_band", "oxm", "util"],
 }
 
 def make_gen(name, version):
diff --git a/loxi_ir/ir.py b/loxi_ir/ir.py
index aaa54ca..6a053c6 100644
--- a/loxi_ir/ir.py
+++ b/loxi_ir/ir.py
@@ -187,6 +187,10 @@
     def has_external_alignment(self):
         return self.params.get('length_includes_align') == 'False'
 
+    @property
+    def has_type_members(self):
+        return find(lambda m: isinstance(m, OFTypeMember), self.members) is not None
+
 """ one class unified across openflow versions. Keeps around a map version->versioned_class """
 class OFUnifiedClass(OFClass):
     def __new__(cls, version_classes, *a, **kw):
@@ -423,9 +427,52 @@
         build_touch_classes.remove(name)
         return c
 
+    def build_id_class(orig_name, base_name):
+        name = base_name + '_id' + orig_name[len(base_name):]
+        if name in name_classes:
+            return name_classes[name]
+        orig_fe, _ = name_frontend_classes[orig_name]
+
+        if orig_fe.superclass:
+            superclass_name = base_name + '_id' + orig_fe.superclass[len(base_name):]
+            superclass = build_id_class(orig_fe.superclass, base_name)
+        else:
+            superclass_name = None
+            superclass = None
+
+        fe = frontend_ir.OFClass(
+            name=name,
+            superclass=superclass_name,
+            members=[m for m in orig_fe.members if not isinstance(m, frontend_ir.OFDataMember)],
+            virtual=orig_fe.virtual,
+            params={})
+
+        base_length, is_fixed_length, member_lengths = \
+           ir_offset.calc_lengths(version, fe, name_classes, name_enums)
+        assert fe.virtual or is_fixed_length
+
+        members = []
+        c = OFClass(name=fe.name, superclass=superclass,
+                members=members, virtual=fe.virtual, params=fe.params,
+                is_fixed_length=is_fixed_length, base_length=base_length)
+
+        members.extend( build_member(c, fe_member, member_lengths[fe_member])
+                  for fe_member in fe.members)
+
+        name_classes[name] = c
+        return c
+
+    id_class_roots = ["of_action", "of_instruction"]
+
     for name in sorted(name_frontend_classes.keys()):
         c = build_class(name)
 
+        # Build ID classes for OF 1.3+
+        if version.wire_version >= 4:
+            for root in id_class_roots:
+                if c.is_instanceof(root):
+                    build_id_class(name, root)
+
     protocol = OFProtocol(version=version, classes=tuple(name_classes.values()), enums=tuple(name_enums.values()))
     for e in chain(protocol.classes, protocol.enums):
         e.protocol = protocol
diff --git a/openflow_input/bsn_global_vrf_allowed b/openflow_input/bsn_global_vrf_allowed
index 543c6fd..ee26308 100644
--- a/openflow_input/bsn_global_vrf_allowed
+++ b/openflow_input/bsn_global_vrf_allowed
@@ -41,7 +41,7 @@
 };
 
 struct of_oxm_bsn_global_vrf_allowed_masked : of_oxm {
-    uint32_t type_len == 0x00030701;
+    uint32_t type_len == 0x00030702;
     uint8_t value;
     uint8_t value_mask;
 };
diff --git a/openflow_input/bsn_in_ports b/openflow_input/bsn_in_ports
index 05003b9..b23df63 100644
--- a/openflow_input/bsn_in_ports
+++ b/openflow_input/bsn_in_ports
@@ -50,7 +50,7 @@
  */
 
 struct of_oxm_bsn_in_ports_128 : of_oxm {
-    uint32_t type_len == 0x00030020;
+    uint32_t type_len == 0x00030010;
     of_bitmap_128_t value;
 };
 
diff --git a/openflow_input/bsn_l3_dst_class_id b/openflow_input/bsn_l3_dst_class_id
index 385add7..8d324a1 100644
--- a/openflow_input/bsn_l3_dst_class_id
+++ b/openflow_input/bsn_l3_dst_class_id
@@ -41,7 +41,7 @@
 };
 
 struct of_oxm_bsn_l3_dst_class_id_masked : of_oxm {
-    uint32_t type_len == 0x00030d04;
+    uint32_t type_len == 0x00030d08;
     uint32_t value;
     uint32_t value_mask;
 };
diff --git a/openflow_input/bsn_l3_interface_class_id b/openflow_input/bsn_l3_interface_class_id
index efeb949..80d1e37 100644
--- a/openflow_input/bsn_l3_interface_class_id
+++ b/openflow_input/bsn_l3_interface_class_id
@@ -41,7 +41,7 @@
 };
 
 struct of_oxm_bsn_l3_interface_class_id_masked : of_oxm {
-    uint32_t type_len == 0x00030904;
+    uint32_t type_len == 0x00030908;
     uint32_t value;
     uint32_t value_mask;
 };
diff --git a/openflow_input/bsn_l3_src_class_id b/openflow_input/bsn_l3_src_class_id
index f18aab9..58f60c6 100644
--- a/openflow_input/bsn_l3_src_class_id
+++ b/openflow_input/bsn_l3_src_class_id
@@ -41,7 +41,7 @@
 };
 
 struct of_oxm_bsn_l3_src_class_id_masked : of_oxm {
-    uint32_t type_len == 0x00030b04;
+    uint32_t type_len == 0x00030b08;
     uint32_t value;
     uint32_t value_mask;
 };
diff --git a/openflow_input/bsn_lag_id b/openflow_input/bsn_lag_id
index c96c1d6..63645a3 100644
--- a/openflow_input/bsn_lag_id
+++ b/openflow_input/bsn_lag_id
@@ -41,7 +41,7 @@
 };
 
 struct of_oxm_bsn_lag_id_masked : of_oxm {
-    uint32_t type_len == 0x00030304;
+    uint32_t type_len == 0x00030308;
     uint32_t value;
     uint32_t value_mask;
 };
diff --git a/openflow_input/bsn_port_counter b/openflow_input/bsn_port_counter
new file mode 100644
index 0000000..f3d149f
--- /dev/null
+++ b/openflow_input/bsn_port_counter
@@ -0,0 +1,79 @@
+// Copyright 2013, Big Switch Networks, Inc.
+//
+// LoxiGen is licensed under the Eclipse Public License,
+// version 1.0 (EPL), with the following special exception:
+//
+// LOXI Exception
+//
+// As a special exception to the terms of the EPL, you may
+// distribute libraries generated by LoxiGen (LoxiGen Libraries)
+// under the terms of your choice, provided that copyright and
+// licensing notices generated by LoxiGen are not altered or removed
+// from the LoxiGen Libraries and the notice provided below is (i)
+// included in the LoxiGen Libraries, if distributed in source code
+// form and (ii) included in any documentation for the LoxiGen
+// Libraries, if distributed in binary form.
+//
+// Notice: "Copyright 2013, Big Switch Networks, Inc.
+// This library was generated by the LoxiGen Compiler."
+//
+// You may not use this file except in compliance with the EPL or
+// LOXI Exception. You may obtain a copy of the EPL at:
+//
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS
+// IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// express or implied. See the EPL for the specific language
+// governing permissions and limitations under the EPL.
+
+#version 4
+
+enum ofp_bsn_port_counter(wire_type=uint8_t, complete=False) {
+  OFP_BSN_PORT_COUNTER_RX_BYTES = 0,
+  OFP_BSN_PORT_COUNTER_RX_PACKETS_UNICAST = 1,
+  OFP_BSN_PORT_COUNTER_RX_PACKETS_BROADCAST = 2,
+  OFP_BSN_PORT_COUNTER_RX_PACKETS_MULTICAST = 3,
+  OFP_BSN_PORT_COUNTER_RX_DROPPED = 4,
+  OFP_BSN_PORT_COUNTER_RX_ERRORS = 5,
+  OFP_BSN_PORT_COUNTER_TX_BYTES = 6,
+  OFP_BSN_PORT_COUNTER_TX_PACKETS_UNICAST = 7,
+  OFP_BSN_PORT_COUNTER_TX_PACKETS_BROADCAST = 8,
+  OFP_BSN_PORT_COUNTER_TX_PACKETS_MULTICAST = 9,
+  OFP_BSN_PORT_COUNTER_TX_DROPPED = 10,
+  OFP_BSN_PORT_COUNTER_TX_ERRORS = 11,
+};
+
+struct of_bsn_port_counter_stats_request : of_bsn_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 0xffff;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 8;
+    of_port_no_t port_no;
+};
+
+struct of_bsn_port_counter_stats_entry {
+    uint16_t length;
+    pad(2);
+    of_port_no_t port_no;
+    list(of_uint64_t) values;
+};
+
+struct of_bsn_port_counter_stats_reply : of_bsn_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 0xffff;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 8;
+    list(of_bsn_port_counter_stats_entry_t) entries;
+};
diff --git a/openflow_input/bsn_vlan_counter b/openflow_input/bsn_vlan_counter
new file mode 100644
index 0000000..51dd356
--- /dev/null
+++ b/openflow_input/bsn_vlan_counter
@@ -0,0 +1,71 @@
+// Copyright 2013, Big Switch Networks, Inc.
+//
+// LoxiGen is licensed under the Eclipse Public License,
+// version 1.0 (EPL), with the following special exception:
+//
+// LOXI Exception
+//
+// As a special exception to the terms of the EPL, you may
+// distribute libraries generated by LoxiGen (LoxiGen Libraries)
+// under the terms of your choice, provided that copyright and
+// licensing notices generated by LoxiGen are not altered or removed
+// from the LoxiGen Libraries and the notice provided below is (i)
+// included in the LoxiGen Libraries, if distributed in source code
+// form and (ii) included in any documentation for the LoxiGen
+// Libraries, if distributed in binary form.
+//
+// Notice: "Copyright 2013, Big Switch Networks, Inc.
+// This library was generated by the LoxiGen Compiler."
+//
+// You may not use this file except in compliance with the EPL or
+// LOXI Exception. You may obtain a copy of the EPL at:
+//
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS
+// IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// express or implied. See the EPL for the specific language
+// governing permissions and limitations under the EPL.
+
+#version 4
+
+enum of_bsn_vlan_counter_t(wire_type=uint8_t, complete=False) {
+  OFP_BSN_VLAN_COUNTER_RX_BYTES = 0,
+  OFP_BSN_VLAN_COUNTER_RX_PACKETS = 1,
+  OFP_BSN_VLAN_COUNTER_TX_BYTES = 2,
+  OFP_BSN_VLAN_COUNTER_TX_PACKETS = 3,
+};
+
+struct of_bsn_vlan_counter_stats_request : of_bsn_stats_request {
+    uint8_t version;
+    uint8_t type == 18;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 0xffff;
+    enum ofp_stats_request_flags flags;
+    pad(4);
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 9;
+    uint16_t vlan_vid;
+};
+
+struct of_bsn_vlan_counter_stats_entry {
+    uint16_t length;
+    uint16_t vlan_vid;
+    pad(4);
+    list(of_uint64_t) values;
+};
+
+struct of_bsn_vlan_counter_stats_reply : of_bsn_stats_reply {
+    uint8_t version;
+    uint8_t type == 19;
+    uint16_t length;
+    uint32_t xid;
+    uint16_t stats_type == 0xffff;
+    enum ofp_stats_reply_flags flags;
+    pad(4);
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 9;
+    list(of_bsn_vlan_counter_stats_entry_t) entries;
+};
diff --git a/openflow_input/bsn_vrf b/openflow_input/bsn_vrf
index 3641faa..26959bd 100644
--- a/openflow_input/bsn_vrf
+++ b/openflow_input/bsn_vrf
@@ -41,7 +41,7 @@
 };
 
 struct of_oxm_bsn_vrf_masked : of_oxm {
-    uint32_t type_len == 0x00030504;
+    uint32_t type_len == 0x00030508;
     uint32_t value;
     uint32_t value_mask;
 };
diff --git a/openflow_input/standard-1.3 b/openflow_input/standard-1.3
index 87adcc4..b4add39 100644
--- a/openflow_input/standard-1.3
+++ b/openflow_input/standard-1.3
@@ -567,6 +567,10 @@
     uint32_t xid;
 };
 
+struct of_uint64 {
+    uint64_t value;
+};
+
 // Special structures used for managing scalar list elements
 struct of_uint32 {
     uint32_t value;
diff --git a/py_gen/codegen.py b/py_gen/codegen.py
index 2c4a135..de3fdba 100644
--- a/py_gen/codegen.py
+++ b/py_gen/codegen.py
@@ -40,8 +40,10 @@
 roots = {
     'of_header': 'message',
     'of_action': 'action',
+    'of_action_id': 'action_id',
     'of_oxm': 'oxm',
     'of_instruction': 'instruction',
+    'of_instruction_id': 'instruction_id',
     'of_meter_band': 'meter_band',
 }
 
@@ -74,6 +76,11 @@
                          ofclasses=modules_by_version[version]['action'],
                          version=version)
 
+def generate_action_id(out, name, version):
+    util.render_template(out, 'module.py',
+                         ofclasses=modules_by_version[version]['action_id'],
+                         version=version)
+
 def generate_oxm(out, name, version):
     util.render_template(out, 'module.py',
                          ofclasses=modules_by_version[version]['oxm'],
@@ -94,6 +101,11 @@
                          ofclasses=modules_by_version[version]['instruction'],
                          version=version)
 
+def generate_instruction_id(out, name, version):
+    util.render_template(out, 'module.py',
+                         ofclasses=modules_by_version[version]['instruction_id'],
+                         version=version)
+
 def generate_message(out, name, version):
     util.render_template(out, 'module.py',
                          ofclasses=modules_by_version[version]['message'],
diff --git a/py_gen/templates/module.py b/py_gen/templates/module.py
index 252bf0d..30c45ad 100644
--- a/py_gen/templates/module.py
+++ b/py_gen/templates/module.py
@@ -43,6 +43,8 @@
 import oxm
 :: #endif
 :: if version >= OFVersions.VERSION_1_3:
+import action_id
+import instruction_id
 import meter_band
 :: #endif
 import util
diff --git a/py_gen/templates/util.py b/py_gen/templates/util.py
index 8ad246a..ed2698a 100644
--- a/py_gen/templates/util.py
+++ b/py_gen/templates/util.py
@@ -41,6 +41,8 @@
 import oxm
 :: #endif
 :: if version >= OFVersions.VERSION_1_3:
+import action_id
+import instruction_id
 import meter_band
 :: #endif
 
diff --git a/test_data/of10/packet_in.data b/test_data/of10/packet_in.data
index a8de82c..d5ccf32 100644
--- a/test_data/of10/packet_in.data
+++ b/test_data/of10/packet_in.data
@@ -18,7 +18,7 @@
     data='abc')
 -- c
 obj = of_packet_in_new(OF_VERSION_1_0);
-of_packet_in_buffer_id_set(obj, 2882400001);
+of_packet_in_buffer_id_set(obj, 0xabcdef01);
 {
     of_octets_t data = { .bytes=3, .data=(uint8_t *)"\x61\x62\x63" };
     of_packet_in_data_set(obj, &data);
diff --git a/test_data/of10/packet_out.data b/test_data/of10/packet_out.data
index fdd1c3d..a3c642b 100644
--- a/test_data/of10/packet_out.data
+++ b/test_data/of10/packet_out.data
@@ -25,7 +25,7 @@
     data='abc')
 -- c
 obj = of_packet_out_new(OF_VERSION_1_0);
-of_packet_out_buffer_id_set(obj, 2882400001);
+of_packet_out_buffer_id_set(obj, 0xabcdef01);
 of_packet_out_in_port_set(obj, 65534);
 of_packet_out_xid_set(obj, 305419896);
 {
diff --git a/test_data/of10/port_mod.data b/test_data/of10/port_mod.data
index 53e09ce..754e139 100644
--- a/test_data/of10/port_mod.data
+++ b/test_data/of10/port_mod.data
@@ -18,12 +18,12 @@
     advertise=0xCAFE6789)
 -- c
 obj = of_port_mod_new(OF_VERSION_1_0);
-of_port_mod_advertise_set(obj, 3405670281);
-of_port_mod_config_set(obj, 2427178479);
+of_port_mod_advertise_set(obj, 0xCAFE6789);
+of_port_mod_config_set(obj, 0x90ABCDEF);
 {
     of_mac_addr_t hw_addr = { { 1, 2, 3, 4, 5, 6 } };
     of_port_mod_hw_addr_set(obj, hw_addr);
 }
-of_port_mod_mask_set(obj, 4279369489);
+of_port_mod_mask_set(obj, 0xFF11FF11);
 of_port_mod_port_no_set(obj, 65533);
 of_port_mod_xid_set(obj, 2);
diff --git a/test_data/of13/bsn_port_counter_stats_reply.data b/test_data/of13/bsn_port_counter_stats_reply.data
new file mode 100644
index 0000000..15e98b4
--- /dev/null
+++ b/test_data/of13/bsn_port_counter_stats_reply.data
@@ -0,0 +1,42 @@
+-- binary
+04 13 # version, type
+00 50 # length
+12 34 56 78 # xid
+ff ff # stats_type
+00 00 # flags
+00 00 00 00 # pad
+00 5c 16 c7 # experimenter
+00 00 00 8 # subtype
+# entries[0]
+00 18 # length
+00 00 # pad
+00 00 00 03 # port
+12 34 56 78 9a bc de f0 # values[0]
+11 22 33 44 55 66 77 88 # values[1]
+# entries[0]
+00 20 # length
+00 00 # pad
+00 00 00 04 # port
+12 34 56 78 9a bc de f0 # values[0]
+11 22 33 44 55 66 77 88 # values[1]
+ff ff ff ff ff ff ff ff # values[2]
+-- python
+ofp.message.bsn_port_counter_stats_reply(
+    xid=0x12345678,
+    flags=0,
+    entries=[
+        ofp.bsn_port_counter_stats_entry(
+            port_no=3,
+            values=[
+                ofp.uint64(0x123456789abcdef0),
+                ofp.uint64(0x1122334455667788),
+            ]),
+        ofp.bsn_port_counter_stats_entry(
+            port_no=4,
+            values=[
+                ofp.uint64(0x123456789abcdef0),
+                ofp.uint64(0x1122334455667788),
+                ofp.uint64(0xffffffffffffffff),
+            ])
+    ]
+)