pyloxi: unpack of_list_bucket_t
diff --git a/py_gen/oftype.py b/py_gen/oftype.py
index a43a1bb..5d42058 100644
--- a/py_gen/oftype.py
+++ b/py_gen/oftype.py
@@ -127,6 +127,8 @@
         elif self.base == 'of_list_oxm_t':
             # HACK need the match_v3 length field
             return 'oxm.unpack_list(%s.slice(_length-4))' % (reader_expr)
+        elif self.base == 'of_list_bucket_t':
+            return 'common.unpack_list_bucket(%s)' % (reader_expr)
         elif self.base == 'of_port_name_t':
             return self._gen_string_unpack_expr(reader_expr, 16)
         elif self.base == 'of_table_name_t' or self.base == 'of_serial_num_t':
diff --git a/py_gen/templates/common.py b/py_gen/templates/common.py
index 19a0d9b..c2ad61c 100644
--- a/py_gen/templates/common.py
+++ b/py_gen/templates/common.py
@@ -68,6 +68,9 @@
             return None
     return [x for x in loxi.generic_util.unpack_list_tlv16(reader, deserializer) if x != None]
 
+def unpack_list_bucket(reader):
+    return loxi.generic_util.unpack_list_lv16(reader, bucket.unpack)
+
 :: for ofclass in ofclasses:
 :: include('_ofclass.py', ofclass=ofclass, superclass="object")
 
diff --git a/py_gen/tests/of11.py b/py_gen/tests/of11.py
index 8099586..3057619 100644
--- a/py_gen/tests/of11.py
+++ b/py_gen/tests/of11.py
@@ -70,10 +70,8 @@
 
     def test_serialization(self):
         expected_failures = [
-            ofp.common.group_desc_stats_entry,
             ofp.common.table_stats_entry,
             ofp.message.group_desc_stats_reply,
-            ofp.message.group_mod,
             ofp.message.group_stats_reply,
         ]
         for klass in self.klasses:
diff --git a/py_gen/tests/of12.py b/py_gen/tests/of12.py
index 4a6c7c6..c89f5b6 100644
--- a/py_gen/tests/of12.py
+++ b/py_gen/tests/of12.py
@@ -158,10 +158,8 @@
 
     def test_serialization(self):
         expected_failures = [
-            ofp.common.group_desc_stats_entry,
             ofp.common.table_stats_entry,
             ofp.message.group_desc_stats_reply,
-            ofp.message.group_mod,
             ofp.message.group_stats_reply,
         ]
         for klass in self.klasses:
diff --git a/py_gen/tests/of13.py b/py_gen/tests/of13.py
index 3d921d2..45609e4 100644
--- a/py_gen/tests/of13.py
+++ b/py_gen/tests/of13.py
@@ -416,8 +416,66 @@
 
 
     def test_group_mod(self):
-        # TODO
-        pass
+        obj = ofp.message.group_mod(
+            xid=0x12345678,
+            command=ofp.OFPGC_MODIFY,
+            group_type=ofp.OFPGT_FF,
+            group_id=5,
+            buckets=[
+                ofp.bucket(
+                    weight=1,
+                    watch_port=5,
+                    watch_group=0xffffffff,
+                    actions=[
+                        ofp.action.output(port=5, max_len=0),
+                        ofp.action.output(port=6, max_len=0)]),
+                ofp.bucket(
+                    weight=1,
+                    watch_port=6,
+                    watch_group=0xffffffff,
+                    actions=[
+                        ofp.action.output(port=5, max_len=0),
+                        ofp.action.output(port=6, max_len=0)])])
+        buf = ''.join([
+            '\x04', '\x0f', # version, type
+            '\x00\x70', # length
+            '\x12\x34\x56\x78', # xid
+            '\x00\x01', # command
+            '\x03', # group_type
+            '\x00', # pad
+            '\x00\x00\x00\x05', # group_id
+            '\x00\x30', # buckets[0].len
+            '\x00\x01', # buckets[0].weight
+            '\x00\x00\x00\x05', # buckets[0].watch_port
+            '\xff\xff\xff\xff', # buckets[0].watch_group
+            '\x00' * 4, # pad
+            '\x00\x00', # buckets[0].actions[0].type
+            '\x00\x10', # buckets[0].actions[0].len
+            '\x00\x00\x00\x05', # buckets[0].actions[0].port
+            '\x00\x00', # buckets[0].actions[0].max_len
+            '\x00' * 6, # pad
+            '\x00\x00', # buckets[0].actions[1].type
+            '\x00\x10', # buckets[0].actions[1].len
+            '\x00\x00\x00\x06', # buckets[0].actions[1].port
+            '\x00\x00', # buckets[0].actions[1].max_len
+            '\x00' * 6, # pad
+            '\x00\x30', # buckets[1].len
+            '\x00\x01', # buckets[1].weight
+            '\x00\x00\x00\x06', # buckets[1].watch_port
+            '\xff\xff\xff\xff', # buckets[1].watch_group
+            '\x00' * 4, # pad
+            '\x00\x00', # buckets[1].actions[0].type
+            '\x00\x10', # buckets[1].actions[0].len
+            '\x00\x00\x00\x05', # buckets[1].actions[0].port
+            '\x00\x00', # buckets[1].actions[0].max_len
+            '\x00' * 6, # pad
+            '\x00\x00', # buckets[1].actions[1].type
+            '\x00\x10', # buckets[1].actions[1].len
+            '\x00\x00\x00\x06', # buckets[1].actions[1].port
+            '\x00\x00', # buckets[1].actions[1].max_len
+            '\x00' * 6, # pad
+        ])
+        test_serialization(obj, buf)
 
     def test_port_mod(self):
         # TODO
@@ -629,9 +687,7 @@
 
     def test_serialization(self):
         expected_failures = [
-            ofp.common.group_desc_stats_entry,
             ofp.message.group_desc_stats_reply,
-            ofp.message.group_mod,
             ofp.message.group_stats_reply,
             ofp.message.meter_stats_reply,
             ofp.message.meter_features_stats_reply,