Merge into master from pull request #156:
Simplifying reader functions (https://github.com/floodlight/loxigen/pull/156)
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_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..09ee486 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
@@ -1076,6 +1139,16 @@
                 of_message_stats_experimenter_id_set(msg, OF_EXPERIMENTER_ID_BSN);
                 of_message_stats_experimenter_subtype_set(msg, 6);
                 break;
+            case OF_BSN_PORT_COUNTER_STATS_REQUEST:
+            case OF_BSN_PORT_COUNTER_STATS_REPLY:
+                of_message_stats_experimenter_id_set(msg, OF_EXPERIMENTER_ID_BSN);
+                of_message_stats_experimenter_subtype_set(msg, 8);
+                break;
+            case OF_BSN_VLAN_COUNTER_STATS_REQUEST:
+            case OF_BSN_VLAN_COUNTER_STATS_REPLY:
+                of_message_stats_experimenter_id_set(msg, OF_EXPERIMENTER_ID_BSN);
+                of_message_stats_experimenter_subtype_set(msg, 9);
+                break;
             default:
                 break;
             }
@@ -1089,6 +1162,10 @@
         /* It's a flow mod obj */
         of_message_flow_mod_command_set(msg, ver, type);
     }
+    if ((type = of_object_to_group_mod_command(id, ver)) >= 0) {
+        /* It's a group mod obj */
+        of_message_group_mod_command_set(msg, type);
+    }
     if (of_object_id_is_extension(id, ver)) {
         uint32_t val32;
 
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_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..befda2f 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
@@ -312,4 +314,23 @@
     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;
+}
+
+static inline void
+of_message_group_mod_command_set(of_message_t msg, uint16_t command) {
+    buf_u16_set(msg + OF_MESSAGE_GROUP_MOD_COMMAND_OFFSET, command);
+}
+
 #endif /* _OF_MESSAGE_H_ */
diff --git a/c_gen/templates/of_type_maps.c b/c_gen/templates/of_type_maps.c
index fa3e05c..2314d67 100644
--- a/c_gen/templates/of_type_maps.c
+++ b/c_gen/templates/of_type_maps.c
@@ -871,6 +871,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 +886,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..e709b70 100644
--- a/c_gen/type_maps.py
+++ b/c_gen/type_maps.py
@@ -60,21 +60,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 +138,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,
@@ -156,7 +161,7 @@
     if loxi_utils.class_is_list(cls):
         return True
     # TODO get this from the input file when we have virtual class syntax
-    if cls in ["of_flow_mod", "of_stats_request", "of_stats_reply", "of_error_msg", "of_bsn_header", "of_nicira_header", "of_action_bsn", "of_action_nicira", "of_action_id_bsn", "of_action_id_nicira", "of_bsn_stats_request", "of_bsn_stats_reply", "of_experimenter_stats_request", "of_experimenter_stats_reply", "of_instruction_experimenter", "of_instruction_bsn"]:
+    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", "of_group_mod"]:
         return True
     return False
 
@@ -182,6 +187,7 @@
         error_msg               = 1,
         experimenter            = 4,
         flow_mod                = 14,
+        group_mod               = 15,
         stats_request           = 18,
         stats_reply             = 19,
         ),
@@ -191,6 +197,7 @@
         error_msg               = 1,
         experimenter            = 4,
         flow_mod                = 14,
+        group_mod               = 15,
         stats_request           = 18,
         stats_reply             = 19,
         ),
@@ -200,6 +207,7 @@
         error_msg               = 1,
         experimenter            = 4,
         flow_mod                = 14,
+        group_mod               = 15,
         stats_request           = 18,  # FIXME Multipart
         stats_reply             = 19,
         )
@@ -269,7 +277,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 +363,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 +494,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 +517,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 +547,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..7aa4857 100644
--- a/java_gen/java_model.py
+++ b/java_gen/java_model.py
@@ -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/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/templates/_imports.java b/java_gen/templates/_imports.java
index cf7334d..af529bc 100644
--- a/java_gen/templates/_imports.java
+++ b/java_gen/templates/_imports.java
@@ -7,8 +7,10 @@
 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.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_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 df1c77d..ebd4f85 100644
--- a/loxi_ir/ir.py
+++ b/loxi_ir/ir.py
@@ -419,9 +419,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_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/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/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),
+            ])
+    ]
+)