Merge branch 'fix-1.2'
diff --git a/src/python/oftest/controller.py b/src/python/oftest/controller.py
index d802717..bee9ab9 100644
--- a/src/python/oftest/controller.py
+++ b/src/python/oftest/controller.py
@@ -608,7 +608,8 @@
                     self.logger.debug("Looking for %s", exp_msg_str)
                     for i in range(len(self.packets)):
                         msg = self.packets[i][0]
-                        self.logger.debug("Checking packets[%d] (%s)", i, exp_msg_str)
+                        msg_str = ofp.ofp_type_map.get(msg.type, "unknown (%d)" % msg.type)
+                        self.logger.debug("Checking packets[%d] %s) against %s", i, msg_str, exp_msg_str)
                         if msg.type == exp_msg:
                             (msg, pkt) = self.packets.pop(i)
                             return (msg, pkt)
diff --git a/src/python/oftest/oft12/packet.py b/src/python/oftest/oft12/packet.py
index 23a3e6e..a7aef94 100644
--- a/src/python/oftest/oft12/packet.py
+++ b/src/python/oftest/oft12/packet.py
@@ -36,12 +36,11 @@
 import socket
 import struct
 import logging
-import oftest.cstruct as ofp
+import ofp
 import unittest
 import binascii
 import string
 import collections #@UnresolvedImport
-import oftest.action as action
 
 ETHERTYPE_IP = 0x0800
 ETHERTYPE_VLAN = 0x8100
diff --git a/src/python/oftest/oft12/testutils.py b/src/python/oftest/oft12/testutils.py
index e508be1..d6dbdf0 100644
--- a/src/python/oftest/oft12/testutils.py
+++ b/src/python/oftest/oft12/testutils.py
@@ -4,12 +4,8 @@
 from cStringIO import StringIO
 #import types
 
-import of12.cstruct as ofp
-import of12.match as oxm_field
-import of12.message as message
-import of12.action as action
-import of12.parse as parse
-import of12.instruction as instruction
+import ofp
+import oftest.parse as parse
 from packet import Packet
 
 try:
@@ -55,7 +51,7 @@
     @param ctrl The controller object for the test
     """
     logger.info("Initializing all table configs")
-    request = message.table_mod()  
+    request = ofp.message.table_mod()  
     request.config = ofp.OFPTC_TABLE_MISS_CONTROLLER
     rv = 0
     for table_id in [0, 1, 2, 3, 4, 5, 6, 7]:
@@ -82,10 +78,9 @@
     @param table_id Table ID
     """
     logger.info("Deleting all flows on table ID: " + str(table_id))
-    msg = message.flow_mod()
+    msg = ofp.message.flow_delete()
     msg.out_port = ofp.OFPP_ANY
     msg.out_group = ofp.OFPG_ANY
-    msg.command = ofp.OFPFC_DELETE
     msg.buffer_id = 0xffffffff
     msg.table_id = table_id
     logger.debug(msg.show())
@@ -100,7 +95,7 @@
     """
     
     logger.info("Deleting all groups")
-    msg = message.group_mod()
+    msg = ofp.message.group_mod()
     msg.group_id = ofp.OFPG_ALL
     msg.command = ofp.OFPGC_DELETE
     logger.debug(msg.show())
@@ -357,7 +352,7 @@
 
 
 def do_barrier(ctrl):
-    b = message.barrier_request()
+    b = ofp.message.barrier_request()
     ctrl.transact(b)
 
 
@@ -371,7 +366,7 @@
     @returns (hwaddr, config, advert) The hwaddress, configuration and
     advertised values
     """
-    request = message.features_request()
+    request = ofp.message.features_request()
     reply, _ = controller.transact(request, timeout=2)
     if reply is None:
         logger.warn("Get feature request failed")
@@ -393,7 +388,7 @@
     configuration value according to config and mask
     """
     logger.info("Setting port " + str(port_no) + " to config " + str(config))
-    request = message.features_request()
+    request = ofp.message.features_request()
     reply, _ = controller.transact(request, timeout=2)
     if reply is None:
         return -1
@@ -403,7 +398,7 @@
             break
     if idx >= len(reply.ports):
         return -1
-    mod = message.port_mod()
+    mod = ofp.message.port_mod()
     mod.port_no = port_no
     mod.hw_addr = reply.ports[idx].hw_addr
     mod.config = config
@@ -491,7 +486,7 @@
     """
     (response, _) = parent.controller.poll(ofp.OFPT_PACKET_IN, 2)
 
-    parent.assertTrue(response is not None, 'Packet in message not received')
+    parent.assertTrue(response is not None, 'Packet in ofp.message not received')
     if str(exp_pkt) != response.data:
         logging.debug("pkt  len " + str(len(str(exp_pkt))) + ": "
                             + str(exp_pkt).encode('hex'))
@@ -567,7 +562,7 @@
     @param byte_count If >= 0, verify byte count
     """
     (response, _) = parent.controller.poll(ofp.OFPT_FLOW_REMOVED, 2)
-    parent.assertTrue(response is not None, 'No flow removed message received')
+    parent.assertTrue(response is not None, 'No flow removed ofp.message received')
 
     if request is None:
         return
@@ -608,24 +603,24 @@
     See flow_match_test for other parameter descriptoins
    
     if egr_queue is set
-             append an out_queue action to egr_queue to the actions_list
+             append an out_queue ofp.action to egr_queue to the actions_list
     else if egr_port is set:  
-             append an output action to egr_port to the actions_list
+             append an output ofp.action to egr_port to the actions_list
     if the instruction_list is empty, 
-        append an APPLY instruction to it
-    Add the action_list to the first write or apply instruction
+        append an APPLY ofp.instruction to it
+    Add the action_list to the first write or apply ofp.instruction
     
-    @param egr_queue if not None, make the output an enqueue action
+    @param egr_queue if not None, make the output an enqueue ofp.action
     @param table_id Table ID for writing a flow_mod
     """
 
     if match_fields is None:
         match_fields = parse.packet_to_flow_match(pkt)
     parent.assertTrue(match_fields is not None, "Flow match from pkt failed")
-    in_port = oxm_field.in_port(ing_port)
-    match_fields.add(in_port) 
-    request = message.flow_mod()
-    request.match_fields = match_fields
+    in_port = ofp.oxm.in_port(ing_port)
+    match_fields.oxm_list.append(in_port) 
+    request = ofp.message.flow_add()
+    request.match= match_fields
     request.buffer_id = 0xffffffff
     request.table_id = table_id
     
@@ -638,21 +633,21 @@
     if instruction_list is None:
         instruction_list = []
     
-    # Set up output/enqueue action if directed
+    # Set up output/enqueue ofp.action if directed
     if egr_queue is not None:
         parent.assertTrue(egr_port is not None, "Egress port not set")
-        act = action.set_queue()
+        act = ofp.action.set_queue()
         act.port = egr_port
         act.queue_id = egr_queue
         action_list.append(act)
     elif egr_port is not None:
-        act = action.output()
+        act = ofp.action.output()
         act.port = egr_port
         action_list.append(act)
         
     inst = None
     if len(instruction_list) == 0: 
-        inst = instruction.instruction_apply_actions()
+        inst = ofp.instruction.apply_actions()
         instruction_list.append(inst)
     else:
         for inst in instruction_list:
@@ -660,26 +655,19 @@
                 inst.type == ofp.OFPIT_APPLY_ACTIONS):
                 break
 
+
     # add all the actions to the last inst
-    for act in action_list:
-        logging.debug("Adding action " + act.show())
-        rv = inst.actions.add(act)
-        parent.assertTrue(rv, "Could not add action" + act.show())
-    # NOTE that the inst has already been added to the flow_mod
+    inst.actions += action_list
 
     # add all the instrutions to the flow_mod
-    for i in instruction_list: 
-        logging.debug("Adding instruction " + inst.show())
-        rv = request.instructions.add(i)
-        parent.assertTrue(rv, "Could not add instruction " + i.show())
-
+    request.instructions += instruction_list
  
     logging.debug(request.show())
     return request
 
 def flow_msg_install(parent, request, clear_table=True):
     """
-    Install a flow mod message in the switch
+    Install a flow mod ofp.message in the switch
 
     @param parent Must implement controller, assertEqual, assertTrue
     @param request The request, all set to go
@@ -712,18 +700,18 @@
     @param exp_code Expected error code
     """
     (response, raw) = parent.controller.poll(ofp.OFPT_ERROR, 2)
-    parent.assertTrue(response is not None, 'No error message received')
+    parent.assertTrue(response is not None, 'No error ofp.message received')
 
     if (exp_type is None) or (exp_code is None):
         logging.debug("Parametrs are not sufficient")
         return
 
     parent.assertEqual(exp_type, response.type,
-                       'Error message type mismatch: ' +
+                       'Error ofp.message type mismatch: ' +
                        str(exp_type) + " != " +
                        str(response.type))
     parent.assertEqual(exp_code, response.code,
-                       'Error message code mismatch: ' +
+                       'Error ofp.message code mismatch: ' +
                        str(exp_code) + " != " +
                        str(response.code))
 
@@ -797,7 +785,7 @@
     @param dl_vlan If not -1, and pkt is None, create a pkt w/ VLAN tag
     @param exp_pkt If not None, use this as the expected output pkt; els use pkt
     @param action_list Additional actions to add to flow mod
-    @param check_expire Check for flow expiration message
+    @param check_expire Check for flow expiration ofp.message
     """
     of_ports = port_map.keys()
     of_ports.sort()
@@ -881,7 +869,7 @@
                     exp_pkt = simple_tcp_packet(
                                 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp}])
         else:
-            #subtract action
+            #subtract ofp.action
             if dl_vlan_int >= 0:
                 exp_pkt = simple_tcp_packet(
                             vlan_tags=[{'vid': dl_vlan_int, 'pcp': dl_vlan_pcp_int}])
@@ -919,7 +907,7 @@
         elif exp_msg is ofp.OFPT_ERROR:
             error_verify(parent, exp_msg_type, exp_msg_code)
         else:
-            parent.assertTrue(0, "Rcv: Unexpected Message: " + str(exp_msg))
+            parent.assertTrue(0, "Rcv: Unexpected ofp.message: " + str(exp_msg))
 
         (_, rcv_pkt, _) = parent.dataplane.poll(timeout=1)
         parent.assertFalse(rcv_pkt is not None, "Packet on dataplane")
@@ -958,13 +946,13 @@
     @param match_exp Set whether packet is expected to receive
     @param add_tag_exp If True, expected_packet has an additional vlan tag,
     If not, expected_packet's vlan tag is replaced as specified
-    @param exp_msg Expected message
-    @param exp_msg_type Expected message type associated with the message
-    @param exp_msg_code Expected message code associated with the msg_type
+    @param exp_msg Expected ofp.message
+    @param exp_msg_type Expected ofp.message type associated with the ofp.message
+    @param exp_msg_code Expected ofp.message code associated with the msg_type
     @param pkt If not None, use this packet for ingress
     @param exp_pkt If not None, use this as the expected output pkt
     @param action_list Additional actions to add to flow mod
-    @param check_expire Check for flow expiration message
+    @param check_expire Check for flow expiration ofp.message
     """
     of_ports = port_map.keys()
     of_ports.sort()
@@ -1029,7 +1017,7 @@
 
 def action_generate(parent, field_to_mod, mod_field_vals):
     """
-    Create an action to modify the field indicated in field_to_mod
+    Create an ofp.action to modify the field indicated in field_to_mod
 
     @param parent Must implement, assertTrue
     @param field_to_mod The field to modify as a string name
@@ -1042,41 +1030,41 @@
         return None
 
     if field_to_mod == 'dl_dst':
-        act = action.set_dl_dst()
+        act = ofp.action.set_dl_dst()
         act.dl_addr = parse.parse_mac(mod_field_vals['dl_dst'])
     elif field_to_mod == 'dl_src':
-        act = action.set_dl_src()
+        act = ofp.action.set_dl_src()
         act.dl_addr = parse.parse_mac(mod_field_vals['dl_src'])
     elif field_to_mod == 'vlan_tags':
         if len(mod_field_vals['vlan_tags']):
-            act = action.pop_vlan()
+            act = ofp.action.pop_vlan()
         else:
             pass
 #    elif field_to_mod == 'dl_vlan_enable':
 #        if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
-#            act = action.pop_vlan()
+#            act = ofp.action.pop_vlan()
 #        # Add VLAN tag is handled by dl_vlan field
 #        # Will return None in this case
 #    elif field_to_mod == 'dl_vlan':
-#        act = action.set_vlan_vid()
+#        act = ofp.action.set_vlan_vid()
 #        act.vlan_vid = mod_field_vals['dl_vlan']
 #    elif field_to_mod == 'dl_vlan_pcp':
-#        act = action.set_vlan_pcp()
+#        act = ofp.action.set_vlan_pcp()
 #        act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
     elif field_to_mod == 'ip_src':
-        act = action.set_nw_src()
+        act = ofp.action.set_nw_src()
         act.nw_addr = parse.parse_ip(mod_field_vals['ip_src'])
     elif field_to_mod == 'ip_dst':
-        act = action.set_nw_dst()
+        act = ofp.action.set_nw_dst()
         act.nw_addr = parse.parse_ip(mod_field_vals['ip_dst'])
     elif field_to_mod == 'ip_tos':
-        act = action.set_nw_tos()
+        act = ofp.action.set_nw_tos()
         act.nw_tos = mod_field_vals['ip_tos']
     elif field_to_mod == 'tcp_sport':
-        act = action.set_tp_src()
+        act = ofp.action.set_tp_src()
         act.tp_port = mod_field_vals['tcp_sport']
     elif field_to_mod == 'tcp_dport':
-        act = action.set_tp_dst()
+        act = ofp.action.set_tp_dst()
         act.tp_port = mod_field_vals['tcp_dport']
     else:
         parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
@@ -1086,7 +1074,7 @@
 def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={}, 
                      mod_fields={}, check_test_params=False):
     """
-    Set up the ingress and expected packet and action list for a test
+    Set up the ingress and expected packet and ofp.action list for a test
 
     @param parent Must implement, assertTrue, config hash and logger
     @param start_field_values Field values to use for ingress packet (optional)
@@ -1095,7 +1083,7 @@
     @params check_test_params If True, will check the parameters vid, add_vlan
     and strip_vlan from the command line.
 
-    Returns a triple:  pkt-to-send, expected-pkt, action-list
+    Returns a triple:  pkt-to-send, expected-pkt, ofp.action-list
     """
 
     new_actions = []
@@ -1184,7 +1172,7 @@
 
 def skip_message_emit(parent, s):
     """
-    Print out a 'skipped' message to stderr
+    Print out a 'skipped' ofp.message to stderr
 
     @param s The string to print out to the log file
     @param parent Must implement config and logger objects
@@ -1199,11 +1187,11 @@
         sys.stderr.write("(S)")
 
 def do_echo_request_reply_test(test,controller):
-        request = message.echo_request()
+        request = ofp.message.echo_request()
         response, _ = controller.transact(request)
-        test.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
+        test.assertEqual(response.type, ofp.OFPT_ECHO_REPLY,
                          'response is not echo_reply')
-        test.assertEqual(request.header.xid, response.header.xid,
+        test.assertEqual(request.xid, response.xid,
                          'response xid != request xid')
         test.assertEqual(len(response.data), 0, 'response data non-empty')
 
@@ -1399,7 +1387,7 @@
         elif exp_msg == ofp.OFPT_ERROR:
             error_verify(parent, exp_msg_type, exp_msg_code)
         else:
-            parent.assertTrue(0, "Rcv: Unexpected Message: " + str(exp_msg))
+            parent.assertTrue(0, "Rcv: Unexpected ofp.message: " + str(exp_msg))
         (_, rcv_pkt, _) = parent.dataplane.poll(timeout=1)
         parent.assertFalse(rcv_pkt is not None, "Packet on dataplane")
 
@@ -1446,13 +1434,13 @@
     @param match_exp Set whether packet is expected to receive
     @param add_tag_exp If True, expected_packet has an additional MPLS shim,
     If not expected_packet's MPLS shim is replaced as specified
-    @param exp_msg Expected message
-    @param exp_msg_type Expected message type associated with the message
-    @param exp_msg_code Expected message code associated with the msg_type
+    @param exp_msg Expected ofp.message
+    @param exp_msg_type Expected ofp.message type associated with the ofp.message
+    @param exp_msg_code Expected ofp.message code associated with the msg_type
     @param pkt If not None, use this packet for ingress
     @param exp_pkt If not None, use this as the expected output pkt; els use pkt
     @param action_list Additional actions to add to flow mod
-    @param check_expire Check for flow expiration message
+    @param check_expire Check for flow expiration ofp.message
     """
     of_ports = port_map.keys()
     of_ports.sort()
@@ -1501,7 +1489,7 @@
     """ Get the flow_stats from the switch
     Test the response to make sure it's really a flow_stats object
     """
-    request = message.flow_stats_request()
+    request = ofp.message.flow_stats_request()
     request.out_port = ofp.OFPP_ANY
     request.out_group = ofp.OFPG_ANY
     request.table_id = 0xff
@@ -1509,6 +1497,6 @@
         request.match_fields = match_fields
     response, _ = parent.controller.transact(request, timeout=2)
     parent.assertTrue(response is not None, "Did not get response")
-    parent.assertTrue(isinstance(response,message.flow_stats_reply),
+    parent.assertTrue(isinstance(response,ofp.message.flow_stats_reply),
                       "Expected a flow_stats_reply, but didn't get it")
     return response
diff --git a/src/python/oftest/parse.py b/src/python/oftest/parse.py
index d75c7cd..8c98c91 100644
--- a/src/python/oftest/parse.py
+++ b/src/python/oftest/parse.py
@@ -3,8 +3,7 @@
 """
 
 import sys
-import logging
-import ofp
+import socket
 try:
     import scapy.all as scapy
 except:
@@ -13,27 +12,6 @@
     except:
         sys.exit("Need to install scapy for packet parsing")
 
-map_wc_field_to_match_member = {
-    'OFPFW_DL_VLAN'                 : 'vlan_vid',
-    'OFPFW_DL_SRC'                  : 'eth_src',
-    'OFPFW_DL_DST'                  : 'eth_dst',
-    'OFPFW_DL_TYPE'                 : 'eth_type',
-    'OFPFW_NW_PROTO'                : 'ip_proto',
-    'OFPFW_TP_SRC'                  : 'tcp_src',
-    'OFPFW_TP_DST'                  : 'tcp_dst',
-    'OFPFW_NW_SRC_SHIFT'            : 'nw_src_shift',
-    'OFPFW_NW_SRC_BITS'             : 'nw_src_bits',
-    'OFPFW_NW_SRC_MASK'             : 'nw_src_mask',
-    'OFPFW_NW_SRC_ALL'              : 'nw_src_all',
-    'OFPFW_NW_DST_SHIFT'            : 'nw_dst_shift',
-    'OFPFW_NW_DST_BITS'             : 'nw_dst_bits',
-    'OFPFW_NW_DST_MASK'             : 'nw_dst_mask',
-    'OFPFW_NW_DST_ALL'              : 'nw_dst_all',
-    'OFPFW_DL_VLAN_PCP'             : 'vlan_pcp',
-    'OFPFW_NW_TOS'                  : 'ip_dscp'
-}
-
-
 def parse_mac(mac_str):
     """
     Parse a MAC address
@@ -61,6 +39,14 @@
         val += a
     return val
 
+def parse_ipv6(ip_str):
+    """
+    Parse an IPv6 address
+
+    Parse a textual IPv6 address and return a 16 byte binary string.
+    """
+    return socket.inet_pton(socket.AF_INET6, ip_str)
+
 def packet_type_classify(ether):
     try:
         dot1q = ether[scapy.Dot1Q]
@@ -93,26 +79,30 @@
         arp = None
     return (dot1q, ip, tcp, udp, icmp, arp)
 
-def packet_to_flow_match(packet, pkt_format="L2"):
+def packet_to_flow_match(packet):
     """
     Create a flow match that matches packet with the given wildcards
 
     @param packet The packet to use as a flow template
-    @param pkt_format Currently only L2 is supported.  Will indicate the 
-    overall packet type for parsing
-    @return An ofp_match object if successful.  None if format is not
-    recognized.  The wildcards of the match will be cleared for the
-    values extracted from the packet.
+    @return An loxi.of10.match object
 
     @todo check min length of packet
-    @todo Check if packet is other than L2 format
-    @todo Implement ICMP and ARP fields
     """
+    import ofp
+    if ofp.OFP_VERSION == 1:
+        return packet_to_flow_match_v1(packet)
+    elif ofp.OFP_VERSION == 3:
+        return packet_to_flow_match_v3(packet)
+    elif ofp.OFP_VERSION == 4:
+        return packet_to_flow_match_v4(packet)
+    else:
+        raise NotImplementedError()
 
-    #@todo check min length of packet
-    if pkt_format.upper() != "L2":
-        logging.error("Only L2 supported for packet_to_flow")
-        return None
+def packet_to_flow_match_v1(packet):
+    """
+    OpenFlow 1.0 implementation of packet_to_flow_match
+    """
+    import loxi.of10 as ofp
 
     if type(packet) == type(""):
         ether = scapy.Ether(packet)
@@ -123,8 +113,7 @@
     try:
         (dot1q, ip, tcp, udp, icmp, arp) = packet_type_classify(ether)
     except:
-        logging.error("packet_to_flow_match: Classify error")
-        return None
+        raise ValueError("could not classify packet")
 
     match = ofp.match()
     match.wildcards = ofp.OFPFW_ALL
@@ -183,3 +172,111 @@
         match.wildcards &= ~ofp.OFPFW_NW_DST_MASK
 
     return match
+
+def packet_to_flow_match_v3(packet):
+    """
+    OpenFlow 1.2 implementation of packet_to_flow_match
+    """
+    import loxi.of12 as ofp
+    return packet_to_flow_match_oxm(packet, ofp)
+
+def packet_to_flow_match_v4(packet):
+    """
+    OpenFlow 1.3 implementation of packet_to_flow_match
+    """
+    import loxi.of13 as ofp
+    return packet_to_flow_match_oxm(packet, ofp)
+
+def packet_to_flow_match_oxm(packet, ofp):
+    def parse_ether_layer(layer, match):
+        assert(type(layer) == scapy.Ether)
+        match.oxm_list.append(ofp.oxm.eth_dst(parse_mac(layer.dst)))
+        match.oxm_list.append(ofp.oxm.eth_src(parse_mac(layer.src)))
+
+        if type(layer.payload) == scapy.Dot1Q:
+            layer = layer.payload
+            match.oxm_list.append(ofp.oxm.eth_type(layer.type))
+            match.oxm_list.append(ofp.oxm.vlan_vid(layer.vlan))
+            match.oxm_list.append(ofp.oxm.vlan_pcp(layer.prio))
+        else:
+            match.oxm_list.append(ofp.oxm.eth_type(layer.type))
+            match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFP_VLAN_NONE))
+
+        if type(layer.payload) == scapy.IP:
+            parse_ipv4_layer(layer.payload, match)
+        elif type(layer.payload) == scapy.IPv6:
+            parse_ipv6_layer(layer.payload, match)
+        elif type(layer.payload) == scapy.ARP:
+            parse_arp_layer(layer.payload, match)
+        # TODO MPLS
+
+    def parse_ipv4_layer(layer, match):
+        assert(type(layer) == scapy.IP)
+        match.oxm_list.append(ofp.oxm.ip_proto(layer.proto))
+        match.oxm_list.append(ofp.oxm.ip_dscp(layer.tos >> 2))
+        match.oxm_list.append(ofp.oxm.ip_ecn(layer.tos & 3))
+        match.oxm_list.append(ofp.oxm.ipv4_src(parse_ip(layer.src)))
+        match.oxm_list.append(ofp.oxm.ipv4_dst(parse_ip(layer.dst)))
+
+        if type(layer.payload) == scapy.TCP:
+            parse_tcp_layer(layer.payload, match)
+        elif type(layer.payload) == scapy.UDP:
+            parse_udp_layer(layer.payload, match)
+        elif type(layer.payload) == scapy.ICMP:
+            parse_icmpv4_layer(layer.payload, match)
+        # TODO SCTP
+
+    def parse_tcp_layer(layer, match):
+        assert(type(layer) == scapy.TCP)
+        match.oxm_list.append(ofp.oxm.tcp_src(layer.sport))
+        match.oxm_list.append(ofp.oxm.tcp_dst(layer.dport))
+
+    def parse_udp_layer(layer, match):
+        assert(type(layer) == scapy.UDP)
+        match.oxm_list.append(ofp.oxm.udp_src(layer.sport))
+        match.oxm_list.append(ofp.oxm.udp_dst(layer.dport))
+
+    def parse_icmpv4_layer(layer, match):
+        assert(type(layer) == scapy.ICMP)
+        match.oxm_list.append(ofp.oxm.icmpv4_type(layer.type))
+        match.oxm_list.append(ofp.oxm.icmpv4_code(layer.code))
+
+    def parse_arp_layer(layer, match):
+        assert(type(layer) == scapy.ARP)
+        match.oxm_list.append(ofp.oxm.arp_op(layer.op))
+        match.oxm_list.append(ofp.oxm.arp_spa(parse_ip(layer.psrc)))
+        match.oxm_list.append(ofp.oxm.arp_tpa(parse_ip(layer.pdst)))
+        match.oxm_list.append(ofp.oxm.arp_sha(parse_mac(layer.hwsrc)))
+        match.oxm_list.append(ofp.oxm.arp_tha(parse_mac(layer.hwdst)))
+
+    def parse_ipv6_layer(layer, match):
+        assert(type(layer) == scapy.IPv6)
+        # TODO handle chained headers
+        match.oxm_list.append(ofp.oxm.ip_proto(layer.nh))
+        match.oxm_list.append(ofp.oxm.ip_dscp(layer.tc >> 2))
+        match.oxm_list.append(ofp.oxm.ip_ecn(layer.tc & 3))
+        match.oxm_list.append(ofp.oxm.ipv6_src(parse_ipv6(layer.src)))
+        match.oxm_list.append(ofp.oxm.ipv6_dst(parse_ipv6(layer.dst)))
+        match.oxm_list.append(ofp.oxm.ipv6_flabel(layer.fl))
+
+        if type(layer.payload) == scapy.TCP:
+            parse_tcp_layer(layer.payload, match)
+        elif type(layer.payload) == scapy.UDP:
+            parse_udp_layer(layer.payload, match)
+        elif layer.nh == 0x3a:
+            parse_icmpv6_layer(layer.payload, match)
+        # TODO ND
+        # TODO SCTP
+
+    def parse_icmpv6_layer(layer, match):
+        match.oxm_list.append(ofp.oxm.icmpv6_type(layer.type))
+        match.oxm_list.append(ofp.oxm.icmpv6_code(layer.code))
+
+    if type(packet) == type(""):
+        ether = scapy.Ether(packet)
+    else:
+        ether = packet
+
+    match = ofp.match()
+    parse_ether_layer(packet, match)
+    return match
diff --git a/src/python/oftest/test_parse.py b/src/python/oftest/test_parse.py
new file mode 100755
index 0000000..7143350
--- /dev/null
+++ b/src/python/oftest/test_parse.py
@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+import unittest
+import parse
+import scapy.all as scapy
+
+class TestPacketToFlowMatchV3(unittest.TestCase):
+    def test_tcp(self):
+        import loxi.of12 as ofp
+        self.maxDiff = None
+        pkt = scapy.Ether(dst='00:01:02:03:04:05', src='00:06:07:08:09:0a')/ \
+            scapy.IP(src='192.168.0.1', dst='192.168.0.2', tos=2 | (32 << 2), ttl=64)/ \
+            scapy.TCP(sport=1234, dport=80)
+        expected = [
+            ofp.oxm.eth_dst([0x00, 0x01, 0x02, 0x03, 0x04, 0x05]),
+            ofp.oxm.eth_src([0x00, 0x06, 0x07, 0x08, 0x09, 0x0a]),
+            ofp.oxm.eth_type(0x0800),
+            ofp.oxm.vlan_vid(ofp.OFP_VLAN_NONE),
+            ofp.oxm.ip_proto(6),
+            ofp.oxm.ip_dscp(32),
+            ofp.oxm.ip_ecn(2),
+            ofp.oxm.ipv4_src(0xc0a80001),
+            ofp.oxm.ipv4_dst(0xc0a80002),
+            ofp.oxm.tcp_src(1234),
+            ofp.oxm.tcp_dst(80)
+        ]
+        result = parse.packet_to_flow_match_v3(pkt).oxm_list
+        self.assertEquals([x.show() for x in expected], [x.show() for x in result])
+
+    def test_udp(self):
+        import loxi.of12 as ofp
+        self.maxDiff = None
+        pkt = scapy.Ether(dst='00:01:02:03:04:05', src='00:06:07:08:09:0a')/ \
+            scapy.IP(src='192.168.0.1', dst='192.168.0.2', tos=2 | (32 << 2), ttl=64)/ \
+            scapy.UDP(sport=1234, dport=80)
+        expected = [
+            ofp.oxm.eth_dst([0x00, 0x01, 0x02, 0x03, 0x04, 0x05]),
+            ofp.oxm.eth_src([0x00, 0x06, 0x07, 0x08, 0x09, 0x0a]),
+            ofp.oxm.eth_type(0x0800),
+            ofp.oxm.vlan_vid(ofp.OFP_VLAN_NONE),
+            ofp.oxm.ip_proto(17),
+            ofp.oxm.ip_dscp(32),
+            ofp.oxm.ip_ecn(2),
+            ofp.oxm.ipv4_src(0xc0a80001),
+            ofp.oxm.ipv4_dst(0xc0a80002),
+            ofp.oxm.udp_src(1234),
+            ofp.oxm.udp_dst(80)
+        ]
+        result = parse.packet_to_flow_match_v3(pkt).oxm_list
+        self.assertEquals([x.show() for x in expected], [x.show() for x in result])
+
+    def test_icmp(self):
+        import loxi.of12 as ofp
+        self.maxDiff = None
+        pkt = scapy.Ether(dst='00:01:02:03:04:05', src='00:06:07:08:09:0a')/ \
+            scapy.IP(src='192.168.0.1', dst='192.168.0.2', tos=2 | (32 << 2), ttl=64)/ \
+            scapy.ICMP(type=8, code=1)
+        expected = [
+            ofp.oxm.eth_dst([0x00, 0x01, 0x02, 0x03, 0x04, 0x05]),
+            ofp.oxm.eth_src([0x00, 0x06, 0x07, 0x08, 0x09, 0x0a]),
+            ofp.oxm.eth_type(0x0800),
+            ofp.oxm.vlan_vid(ofp.OFP_VLAN_NONE),
+            ofp.oxm.ip_proto(1),
+            ofp.oxm.ip_dscp(32),
+            ofp.oxm.ip_ecn(2),
+            ofp.oxm.ipv4_src(0xc0a80001),
+            ofp.oxm.ipv4_dst(0xc0a80002),
+            ofp.oxm.icmpv4_type(8),
+            ofp.oxm.icmpv4_code(1)
+        ]
+        result = parse.packet_to_flow_match_v3(pkt).oxm_list
+        self.assertEquals([x.show() for x in expected], [x.show() for x in result])
+
+    def test_arp(self):
+        import loxi.of12 as ofp
+        self.maxDiff = None
+        pkt = scapy.Ether(dst='00:01:02:03:04:05', src='00:06:07:08:09:0a')/ \
+            scapy.ARP(hwsrc='00:01:02:03:04:05', hwdst='00:06:07:08:09:0a', \
+                      psrc='192.168.0.1', pdst='192.168.0.2', op=1)
+        expected = [
+            ofp.oxm.eth_dst([0x00, 0x01, 0x02, 0x03, 0x04, 0x05]),
+            ofp.oxm.eth_src([0x00, 0x06, 0x07, 0x08, 0x09, 0x0a]),
+            ofp.oxm.eth_type(0x0806),
+            ofp.oxm.vlan_vid(ofp.OFP_VLAN_NONE),
+            ofp.oxm.arp_op(1),
+            ofp.oxm.arp_spa(0xc0a80001),
+            ofp.oxm.arp_tpa(0xc0a80002),
+            ofp.oxm.arp_sha([0x00, 0x01, 0x02, 0x03, 0x04, 0x05]),
+            ofp.oxm.arp_tha([0x00, 0x06, 0x07, 0x08, 0x09, 0x0a]),
+        ]
+        result = parse.packet_to_flow_match_v3(pkt).oxm_list
+        self.assertEquals([x.show() for x in expected], [x.show() for x in result])
+
+    def test_tcpv6(self):
+        import loxi.of12 as ofp
+        self.maxDiff = None
+        pkt = scapy.Ether(dst='00:01:02:03:04:05', src='00:06:07:08:09:0a')/ \
+            scapy.IPv6(src="::1", dst="::2", nh=6, tc=2 | (32 << 2), fl=7)/ \
+            scapy.TCP(sport=1234, dport=80)
+        expected = [
+            ofp.oxm.eth_dst([0x00, 0x01, 0x02, 0x03, 0x04, 0x05]),
+            ofp.oxm.eth_src([0x00, 0x06, 0x07, 0x08, 0x09, 0x0a]),
+            ofp.oxm.eth_type(0x86dd),
+            ofp.oxm.vlan_vid(ofp.OFP_VLAN_NONE),
+            ofp.oxm.ip_proto(6),
+            ofp.oxm.ip_dscp(32),
+            ofp.oxm.ip_ecn(2),
+            ofp.oxm.ipv6_src("\x00" * 15 + "\x01"),
+            ofp.oxm.ipv6_dst("\x00" * 15 + "\x02"),
+            ofp.oxm.ipv6_flabel(7),
+            ofp.oxm.tcp_src(1234),
+            ofp.oxm.tcp_dst(80)
+        ]
+        result = parse.packet_to_flow_match_v3(pkt).oxm_list
+        self.assertEquals([x.show() for x in expected], [x.show() for x in result])
+
+    def test_icmpv6(self):
+        import loxi.of12 as ofp
+        self.maxDiff = None
+        pkt = scapy.Ether(dst='00:01:02:03:04:05', src='00:06:07:08:09:0a')/ \
+            scapy.IPv6(src="::1", dst="::2", tc=2 | (32 << 2), fl=7)/ \
+            scapy.ICMPv6EchoRequest()
+        expected = [
+            ofp.oxm.eth_dst([0x00, 0x01, 0x02, 0x03, 0x04, 0x05]),
+            ofp.oxm.eth_src([0x00, 0x06, 0x07, 0x08, 0x09, 0x0a]),
+            ofp.oxm.eth_type(0x86dd),
+            ofp.oxm.vlan_vid(ofp.OFP_VLAN_NONE),
+            ofp.oxm.ip_proto(0x3a),
+            ofp.oxm.ip_dscp(32),
+            ofp.oxm.ip_ecn(2),
+            ofp.oxm.ipv6_src("\x00" * 15 + "\x01"),
+            ofp.oxm.ipv6_dst("\x00" * 15 + "\x02"),
+            ofp.oxm.ipv6_flabel(7),
+            ofp.oxm.icmpv6_type(128),
+            ofp.oxm.icmpv6_code(0)
+        ]
+        result = parse.packet_to_flow_match_v3(pkt).oxm_list
+        self.assertEquals([x.show() for x in expected], [x.show() for x in result])
+
+    def test_vlan(self):
+        import loxi.of12 as ofp
+        self.maxDiff = None
+        pkt = scapy.Ether(dst='00:01:02:03:04:05', src='00:06:07:08:09:0a')/ \
+            scapy.Dot1Q(vlan=50, prio=5)/ \
+            scapy.IP(src='192.168.0.1', dst='192.168.0.2', tos=2 | (32 << 2), ttl=64)/ \
+            scapy.TCP(sport=1234, dport=80)
+        expected = [
+            ofp.oxm.eth_dst([0x00, 0x01, 0x02, 0x03, 0x04, 0x05]),
+            ofp.oxm.eth_src([0x00, 0x06, 0x07, 0x08, 0x09, 0x0a]),
+            ofp.oxm.eth_type(0x0800),
+            ofp.oxm.vlan_vid(50),
+            ofp.oxm.vlan_pcp(5),
+            ofp.oxm.ip_proto(6),
+            ofp.oxm.ip_dscp(32),
+            ofp.oxm.ip_ecn(2),
+            ofp.oxm.ipv4_src(0xc0a80001),
+            ofp.oxm.ipv4_dst(0xc0a80002),
+            ofp.oxm.tcp_src(1234),
+            ofp.oxm.tcp_dst(80)
+        ]
+        result = parse.packet_to_flow_match_v3(pkt).oxm_list
+        self.assertEquals([x.show() for x in expected], [x.show() for x in result])
+
+    def test_unknown_ethertype(self):
+        import loxi.of12 as ofp
+        self.maxDiff = None
+        pkt = scapy.Ether(dst='00:01:02:03:04:05', src='00:06:07:08:09:0a', type=0x0801)/ \
+            ('\x11' * 20)
+        expected = [
+            ofp.oxm.eth_dst([0x00, 0x01, 0x02, 0x03, 0x04, 0x05]),
+            ofp.oxm.eth_src([0x00, 0x06, 0x07, 0x08, 0x09, 0x0a]),
+            ofp.oxm.eth_type(0x0801),
+            ofp.oxm.vlan_vid(ofp.OFP_VLAN_NONE),
+        ]
+        result = parse.packet_to_flow_match_v3(pkt).oxm_list
+        self.assertEquals([x.show() for x in expected], [x.show() for x in result])
+
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)
diff --git a/tests-1.2/basic.py b/tests-1.2/basic.py
index 76a5a16..fe45502 100644
--- a/tests-1.2/basic.py
+++ b/tests-1.2/basic.py
@@ -28,9 +28,9 @@
         request = ofp.message.echo_request()
         request.data = 'OpenFlow Will Rule The World'
         response, _ = self.controller.transact(request)
-        self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
+        self.assertEqual(response.type, ofp.OFPT_ECHO_REPLY,
                          'response is not echo_reply')
-        self.assertEqual(request.header.xid, response.header.xid,
+        self.assertEqual(request.xid, response.xid,
                          'response xid != request xid')
         self.assertEqual(request.data, response.data,
                          'response data does not match request')
@@ -45,9 +45,8 @@
         request = ofp.message.features_request()
         response,_ = self.controller.transact(request)
         self.assertTrue(response,"Got no features_reply to features_request")
-        self.assertEqual(response.header.type, ofp.OFPT_FEATURES_REPLY,
-                         'response is not echo_reply')
-        self.assertTrue(len(response) >= 32, "features_reply too short: %d < 32 " % len(response))
+        self.assertEqual(response.type, ofp.OFPT_FEATURES_REPLY,
+                         'response is not features_reply')
        
 class PacketIn(base_tests.SimpleDataPlane):
     """
@@ -69,7 +68,7 @@
             pkt = testutils.simple_tcp_packet()
             self.dataplane.send(of_port, str(pkt))
             #@todo Check for unexpected messages?
-            (response, _) = self.controller.poll(ofp.OFPT_PACKET_IN, 2)
+            (response, _) = self.controller.poll(ofp.OFPT_PACKET_IN)
 
             self.assertTrue(response is not None, 
                             'Packet in message not received on port ' + 
@@ -107,10 +106,11 @@
         for dp_port in of_ports:
             msg = ofp.message.packet_out()
             msg.in_port = ofp.OFPP_CONTROLLER
+            msg.buffer_id = 0xffffffff
             msg.data = str(outpkt)
             act = ofp.action.output()
             act.port = dp_port
-            self.assertTrue(msg.actions.add(act), 'Could not add action to msg')
+            msg.actions.append(act)
 
             logging.info("PacketOut to: " + str(dp_port))
             rv = self.controller.message_send(msg)
@@ -137,7 +137,7 @@
     def runTest(self):
         logging.info("Running StatsGet")
         logging.info("Inserting trial flow")
-        request = ofp.message.flow_mod()
+        request = ofp.message.flow_add()
         request.buffer_id = 0xffffffff
         for i in range(1,5):
             request.priority = i*1000
@@ -154,7 +154,7 @@
         response, _ = self.controller.transact(request, timeout=2)
         self.assertTrue(response is not None, "Did not get response")
         self.assertTrue(isinstance(response,ofp.message.flow_stats_reply),"Not a flow_stats_reply")
-        self.assertEqual(len(response.stats),0)
+        self.assertEqual(len(response.entries),0)
         logging.debug(response.show())
         
 
@@ -168,7 +168,7 @@
     def runTest(self):
         logging.info("Running StatsGet")
         logging.info("Inserting trial flow")
-        request = ofp.message.flow_mod()
+        request = ofp.message.flow_add()
         request.buffer_id = 0xffffffff
         rv = self.controller.message_send(request)
         self.assertTrue(rv != -1, "Failed to insert test flow")
@@ -201,7 +201,7 @@
 
     def runTest(self):
         logging.info("Running " + str(self))
-        request = ofp.message.flow_mod()
+        request = ofp.message.flow_add()
         request.buffer_id = 0xffffffff
         rv = self.controller.message_send(request)
         self.assertTrue(rv != -1, "Error installing flow mod")
diff --git a/tests-1.2/flow_mods.py b/tests-1.2/flow_mods.py
index 1fe05ee..27ebb9e 100644
--- a/tests-1.2/flow_mods.py
+++ b/tests-1.2/flow_mods.py
@@ -39,11 +39,16 @@
         testutils.do_barrier(self.controller)
         self.assertEqual(rv, 0, "Failed to insert 2nd flow_mod")
         flow_stats = testutils.flow_stats_get(self)
-        self.assertEqual(len(flow_stats.stats),1, 
+        self.assertEqual(len(flow_stats.entries),1,
                          "Expected only one flow_mod")
-        stat = flow_stats.stats[0]
+        stat = flow_stats.entries[0]
+
+        def canonicalize_match(match):
+            match.oxm_list.sort(key=lambda x: x.type_len)
+
+        canonicalize_match(stat.match)
+        canonicalize_match(fm_new.match)
         self.assertEqual(stat.match, fm_new.match)
-        self.assertEqual(stat.match_fields, fm_new.match_fields)
         self.assertEqual(stat.instructions, fm_new.instructions)
         # @todo consider adding more tests here
         
diff --git a/tests-1.2/groups.py b/tests-1.2/groups.py
index 76948db..0b417e2 100644
--- a/tests-1.2/groups.py
+++ b/tests-1.2/groups.py
@@ -4,9 +4,10 @@
 import logging
 
 from oftest import config
-import of12 as ofp
+import ofp
 import oftest.oft12.testutils as testutils
 import oftest.base_tests as base_tests
+import oftest.parse
 
 def create_group_desc_stats_req():
     # XXX Zoltan: hack, remove if message module is fixed
@@ -28,10 +29,10 @@
                group_id = 0, buckets = []):
     m = ofp.message.group_mod()
     m.command = command
-    m.type = type
+    m.group_type = type
     m.group_id = group_id
     for b in buckets:
-        m.buckets.add(b)
+        m.buckets.append(b)
 
     return m
 
@@ -39,12 +40,12 @@
 
 # XXX Zoltan: watch_port/_group off ?
 def create_bucket(weight = 0, watch_port = 0, watch_group = 0, actions=[]):
-    b = ofp.bucket.bucket()
+    b = ofp.bucket()
     b.weight = weight
     b.watch_port = watch_port
     b.watch_group = watch_group
     for a in actions:
-        b.actions.add(a)
+        b.actions.append(a)
 
     return b
 
@@ -62,36 +63,35 @@
         return act
     if a == ofp.OFPAT_SET_FIELD:
         port = kwargs.get('tcp_sport', 0)
-        field_2b_set = ofp.match.tcp_src(port)
+        field_2b_set = ofp.oxm.tcp_src(port)
         act = ofp.action.set_field()
-        act.field = field_2b_set
+        act.field = field_2b_set.pack() + '\x00' * 6 # HACK
         return act;
 
 
 
 def create_flow_msg(packet = None, in_port = None, match = None, apply_action_list = []):
 
-    apply_inst = ofp.instruction.instruction_apply_actions()
+    apply_inst = ofp.instruction.apply_actions()
 
     if apply_action_list is not None:
         for act in apply_action_list:
-            apply_inst.actions.add(act)
+            apply_inst.actions.append(act)
 
-    request = ofp.message.flow_mod()
-    request.match.type = ofp.OFPMT_OXM
+    request = ofp.message.flow_add()
 
     if match is None:
-        match = ofp.parse.packet_to_flow_match(packet)
+        match = oftest.parse.packet_to_flow_match(packet)
     
-    request.match_fields = match
+    request.match = match
     
     if in_port != None:
-        match_port = testutils.oxm_field.in_port(in_port)
-        request.match_fields.tlvs.append(match_port)
+        match_port = ofp.oxm.in_port(in_port)
+        request.match.oxm_list.append(match_port)
     request.buffer_id = 0xffffffff
     request.priority = 1000
     
-    request.instructions.add(apply_inst)
+    request.instructions.append(apply_inst)
 
     return request
 
@@ -130,11 +130,11 @@
         self.assertTrue(response is not None, 
                         'Did not receive an error message')
 
-        self.assertEqual(response.header.type, ofp.OFPT_ERROR,
+        self.assertEqual(response.type, ofp.OFPT_ERROR,
                          'Did not receive an error message')
 
         if type != 0:
-            self.assertEqual(response.type, type,
+            self.assertEqual(response.err_type, type,
                              'Did not receive a ' + str(type) + ' type error message')
 
         if code != 0:
@@ -1024,16 +1024,14 @@
         self.send_ctrl_exp_reply(group_stats_req,
                                  ofp.OFPT_STATS_REPLY, 'group stat')
 
-        exp_len = ofp.OFP_HEADER_BYTES + \
-                  ofp.OFP_STATS_REPLY_BYTES + \
-                  ofp.OFP_GROUP_STATS_BYTES + \
-                  ofp.OFP_BUCKET_COUNTER_BYTES * 2
+        self.assertEqual(len(response.entries), 1, 'Incorrect number of groups')
+        self.assertEqual(len(response.entries[0].bucket_stats), 2, 'Incorrect number of groups')
+        self.assertEqual(response.entries[0].packet_count, 3, 'Incorrect group packet count')
+        self.assertEqual(response.entries[0].byte_count, 300, 'Incorrect group byte count')
+        for bucket_stat in response.entries[0].bucket_stats:
+            self.assertEqual(bucket_stat.packet_count, 3, 'Incorrect bucket packet count')
+            self.assertEqual(bucket_stat.byte_count, 300, 'Incorrect bucket byte count')
 
-        self.assertEqual(len(response), exp_len,
-                         'Received packet length does not equal expected length')
-        # XXX Zoltan: oftest group_stats_req handling needs to be fixed
-        #             right now only the expected message length is checked
-        #             responses should be checked in Wireshark
 
 
 
@@ -1103,18 +1101,28 @@
         self.send_ctrl_exp_reply(group_stats_req,
                                  ofp.OFPT_STATS_REPLY, 'group stat')
 
-        exp_len = ofp.OFP_HEADER_BYTES + \
-                  ofp.OFP_STATS_REPLY_BYTES + \
-                  ofp.OFP_GROUP_STATS_BYTES + \
-                  ofp.OFP_BUCKET_COUNTER_BYTES * 2 + \
-                  ofp.OFP_GROUP_STATS_BYTES + \
-                  ofp.OFP_BUCKET_COUNTER_BYTES * 2
+        self.assertEqual(len(response.entries), 2)
+        group10, group20 = sorted(response.entries, key=lambda x: x.group_id)
 
-        self.assertEqual(len(response), exp_len,
-                         'Received packet length does not equal expected length')
-        # XXX Zoltan: oftest group_stats_req handling needs to be fixed
-        #             right now only the expected message length is checked
-        #             responses should be checked in Wireshark
+        # Check stats for group 10
+        self.assertEqual(group10.group_id, 10)
+        self.assertEqual(group10.ref_count, 1)
+        self.assertEqual(group10.packet_count, 2)
+        self.assertEqual(group10.byte_count, 200)
+        self.assertEqual(len(group10.bucket_stats), 2)
+        for bucket_stat in group10.bucket_stats:
+            self.assertEqual(bucket_stat.packet_count, 2)
+            self.assertEqual(bucket_stat.byte_count, 200)
+
+        # Check stats for group 20
+        self.assertEqual(group20.group_id, 20)
+        self.assertEqual(group20.ref_count, 1)
+        self.assertEqual(group20.packet_count, 3)
+        self.assertEqual(group20.byte_count, 300)
+        self.assertEqual(len(group20.bucket_stats), 2)
+        for bucket_stat in group20.bucket_stats:
+            self.assertEqual(bucket_stat.packet_count, 3)
+            self.assertEqual(bucket_stat.byte_count, 300)
 
 
 
@@ -1151,16 +1159,13 @@
         self.send_ctrl_exp_reply(group_desc_stats_req,
                                  ofp.OFPT_STATS_REPLY, 'group desc stat')
 
-        exp_len = ofp.OFP_HEADER_BYTES + \
-                  ofp.OFP_STATS_REPLY_BYTES + \
-                  ofp.OFP_GROUP_DESC_STATS_BYTES + \
-                  len(b1) + len(b2) + len(b3)
-
-        self.assertEqual(len(response), exp_len,
-                         'Received packet length does not equal expected length')
-        # XXX Zoltan: oftest group_stats_req handling needs to be fixed
-        #             right now only the expected message length is checked
-        #             responses should be checked in Wireshark
+        self.assertEquals(len(response.entries), 1)
+        group = response.entries[0]
+        self.assertEquals(group.group_id, 10)
+        self.assertEquals(len(group.buckets), 3)
+        self.assertEquals(group.buckets[0], b1)
+        self.assertEquals(group.buckets[1], b2)
+        self.assertEquals(group.buckets[2], b3)
 
 
 #@todo: A flow added with group action should increase the ref counter of the ref. group
@@ -1246,7 +1251,7 @@
         self.send_ctrl_exp_reply(aggr_stat_req,
                                  ofp.OFPT_STATS_REPLY, 'aggr stat')
 
-        self.assertEqual(response.stats[0].flow_count, 2,
+        self.assertEqual(response.flow_count, 2,
                          'Did not match expected flow count')
 
 class GroupFlowSelectAll(GroupTest):
@@ -1315,7 +1320,7 @@
         self.send_ctrl_exp_reply(aggr_stat_req,
                                  ofp.OFPT_STATS_REPLY, 'group desc stat')
 
-        self.assertEqual(response.stats[0].flow_count, 4,
+        self.assertEqual(response.flow_count, 4,
                          'Did not match expected flow count')
 
 
diff --git a/tests-1.2/ipv6.py b/tests-1.2/ipv6.py
index c871a6b..5a45e9d 100644
--- a/tests-1.2/ipv6.py
+++ b/tests-1.2/ipv6.py
@@ -19,6 +19,7 @@
 import ofp
 import oftest.oft12.testutils as testutils
 import oftest.base_tests as base_tests
+import oftest.parse
 
 TEST_VID_DEFAULT = 2
 
@@ -48,22 +49,21 @@
 
         # Add entry match 
 
-        request = ofp.message.flow_mod()
-        request.match.type = ofp.OFPMT_OXM
-        port = ofp.match.in_port(of_ports[0])
-        eth_type = ofp.match.eth_type(IPV6_ETHERTYPE)
-        eth_dst = ofp.match.eth_dst(ofp.parse.parse_mac("00:01:02:03:04:05"))
-        ipv6_src = ofp.match.ipv6_src(ipaddr.IPv6Address('fe80::2420:52ff:fe8f:5189'))
+        request = ofp.message.flow_add()
+        port = ofp.oxm.in_port(of_ports[0])
+        eth_type = ofp.oxm.eth_type(IPV6_ETHERTYPE)
+        eth_dst = ofp.oxm.eth_dst(oftest.parse.parse_mac("00:01:02:03:04:05"))
+        ipv6_src = ofp.oxm.ipv6_src(oftest.parse.parse_ipv6('fe80::2420:52ff:fe8f:5189'))
         
-        request.match_fields.tlvs.append(port)
-        request.match_fields.tlvs.append(eth_type)
-        request.match_fields.tlvs.append(eth_dst)
-        request.match_fields.tlvs.append(ipv6_src)
+        request.match.oxm_list.append(port)
+        request.match.oxm_list.append(eth_type)
+        request.match.oxm_list.append(eth_dst)
+        request.match.oxm_list.append(ipv6_src)
         act = ofp.action.output()
         act.port = of_ports[3]
-        inst = ofp.instruction.instruction_apply_actions()
-        inst.actions.add(act)
-        request.instructions.add(inst)
+        inst = ofp.instruction.apply_actions()
+        inst.actions.append(act)
+        request.instructions.append(inst)
         request.buffer_id = 0xffffffff
         
         request.priority = 1000
@@ -103,25 +103,24 @@
 
         # Add entry match 
 
-        request = ofp.message.flow_mod()
-        request.match.type = ofp.OFPMT_OXM
-        port = ofp.match.in_port(of_ports[0])
-        eth_type = ofp.match.eth_type(IPV6_ETHERTYPE)
-        ipv6_src = ofp.match.ipv6_src(ipaddr.IPv6Address('fe80::2420:52ff:fe8f:5189'))
-        ip_proto = ofp.match.ip_proto(ICMPV6_PROTOCOL)
-        icmpv6_type = ofp.match.icmpv6_type(128)
+        request = ofp.message.flow_add()
+        port = ofp.oxm.in_port(of_ports[0])
+        eth_type = ofp.oxm.eth_type(IPV6_ETHERTYPE)
+        ipv6_src = ofp.oxm.ipv6_src(oftest.parse.parse_ipv6('fe80::2420:52ff:fe8f:5189'))
+        ip_proto = ofp.oxm.ip_proto(ICMPV6_PROTOCOL)
+        icmpv6_type = ofp.oxm.icmpv6_type(128)
         
-        request.match_fields.tlvs.append(port)
-        request.match_fields.tlvs.append(eth_type)
-        request.match_fields.tlvs.append(ipv6_src)
-        request.match_fields.tlvs.append(ip_proto)
-        request.match_fields.tlvs.append(icmpv6_type)
+        request.match.oxm_list.append(port)
+        request.match.oxm_list.append(eth_type)
+        request.match.oxm_list.append(ipv6_src)
+        request.match.oxm_list.append(ip_proto)
+        request.match.oxm_list.append(icmpv6_type)
         
         act = ofp.action.output()
         act.port = of_ports[3]
-        inst = ofp.instruction.instruction_apply_actions()
-        inst.actions.add(act)
-        request.instructions.add(inst)
+        inst = ofp.instruction.apply_actions()
+        inst.actions.append(act)
+        request.instructions.append(inst)
         request.buffer_id = 0xffffffff
         
         request.priority = 1000
@@ -159,29 +158,28 @@
 
         # Add entry match 
 
-        request = ofp.message.flow_mod()
-        request.match.type = ofp.OFPMT_OXM
-        port = ofp.match.in_port(of_ports[0])
-        eth_type = ofp.match.eth_type(IPV6_ETHERTYPE)
-        ipv6_src = ofp.match.ipv6_src(ipaddr.IPv6Address('fe80::2420:52ff:fe8f:5189'))
+        request = ofp.message.flow_add()
+        port = ofp.oxm.in_port(of_ports[0])
+        eth_type = ofp.oxm.eth_type(IPV6_ETHERTYPE)
+        ipv6_src = ofp.oxm.ipv6_src(oftest.parse.parse_ipv6('fe80::2420:52ff:fe8f:5189'))
         
-        request.match_fields.tlvs.append(port)
-        request.match_fields.tlvs.append(eth_type)
-        request.match_fields.tlvs.append(ipv6_src)
+        request.match.oxm_list.append(port)
+        request.match.oxm_list.append(eth_type)
+        request.match.oxm_list.append(ipv6_src)
         
-        field_2b_set = ofp.match.ipv6_dst(ipaddr.IPv6Address('fe80::2420:52ff:fe8f:DDDD'))
+        field_2b_set = ofp.oxm.ipv6_dst(oftest.parse.parse_ipv6('fe80::2420:52ff:fe8f:DDDD'))
         act_setfield = ofp.action.set_field()
-        act_setfield.field = field_2b_set
+        act_setfield.field = field_2b_set.pack() # HACK
         
 #       TODO: insert action set field properly
         act_out = ofp.action.output()
         act_out.port = of_ports[3]
         
         
-        inst = ofp.instruction.instruction_apply_actions()
-        inst.actions.add(act_setfield)
-        inst.actions.add(act_out)
-        request.instructions.add(inst)
+        inst = ofp.instruction.apply_actions()
+        inst.actions.append(act_setfield)
+        inst.actions.append(act_out)
+        request.instructions.append(inst)
         request.buffer_id = 0xffffffff
         
         request.priority = 1000
@@ -224,26 +222,26 @@
 
         # Add entry match 
 
-        request = ofp.message.flow_mod()
+        request = ofp.message.flow_add()
         request.match.type = ofp.OFPMT_OXM
-        port = ofp.match.in_port(of_ports[0])
-        eth_type = ofp.match.eth_type(IPV6_ETHERTYPE)
-        ipv6_src = ofp.match.ipv6_src(ipaddr.IPv6Address('fe80::2420:52ff:fe8f:5189'))        
-        ip_proto = ofp.match.ip_proto(TCP_PROTOCOL)
-        tcp_port = ofp.match.tcp_src(80)
+        port = ofp.oxm.in_port(of_ports[0])
+        eth_type = ofp.oxm.eth_type(IPV6_ETHERTYPE)
+        ipv6_src = ofp.oxm.ipv6_src(oftest.parse.parse_ipv6('fe80::2420:52ff:fe8f:5189'))
+        ip_proto = ofp.oxm.ip_proto(TCP_PROTOCOL)
+        tcp_port = ofp.oxm.tcp_src(80)
         
         
-        request.match_fields.tlvs.append(port)
-        request.match_fields.tlvs.append(eth_type)
-        request.match_fields.tlvs.append(ipv6_src)
-        request.match_fields.tlvs.append(ip_proto)
-        request.match_fields.tlvs.append(tcp_port)
+        request.match.oxm_list.append(port)
+        request.match.oxm_list.append(eth_type)
+        request.match.oxm_list.append(ipv6_src)
+        request.match.oxm_list.append(ip_proto)
+        request.match.oxm_list.append(tcp_port)
         
         act = ofp.action.output()
         act.port = of_ports[3]
-        inst = ofp.instruction.instruction_apply_actions()
-        inst.actions.add(act)
-        request.instructions.add(inst)
+        inst = ofp.instruction.apply_actions()
+        inst.actions.append(act)
+        request.instructions.append(inst)
         request.buffer_id = 0xffffffff
         
         request.priority = 1000