parse: support OF 1.2
diff --git a/src/python/oftest/parse.py b/src/python/oftest/parse.py
index d75c7cd..385e21d 100644
--- a/src/python/oftest/parse.py
+++ b/src/python/oftest/parse.py
@@ -3,8 +3,6 @@
 """
 
 import sys
-import logging
-import ofp
 try:
     import scapy.all as scapy
 except:
@@ -13,27 +11,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 +38,15 @@
         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.
+    """
+    import ipaddr # TODO remove dependency on ipaddr?
+    return str(ipaddr.IPv6Address(ip_str).packed)
+
 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