Added scapy and packet to flow match
diff --git a/src/python/oftest/protocol/parse.py b/src/python/oftest/protocol/parse.py
index 49b5df1..9380762 100644
--- a/src/python/oftest/protocol/parse.py
+++ b/src/python/oftest/protocol/parse.py
@@ -1,9 +1,17 @@
-o
+"""
+OpenFlow message parsing functions
+"""
+
+import sys
from message import *
from error import *
from action import *
from action_list import action_list
-from ofp import *
+from cstruct import *
+try:
+ from scapy.all import *
+except:
+ sys.exit("Need to install scapy for packet parsing")
"""
of_message.py
@@ -86,15 +94,23 @@
return msg_type_to_class_map[hdr.type]()
if hdr.type == OFPT_STATS_REQUEST:
sub_hdr = ofp_stats_request()
- sub_hdr.unpack(binary_string)
- return stats_request_to_class_map[sub_hdr.type]()
+ sub_hdr.unpack(binary_string[OFP_HEADER_BYTES:])
+ try:
+ obj = stats_request_to_class_map[sub_hdr.type]()
+ except KeyError:
+ obj = None
+ return obj
elif hdr.type == OFPT_STATS_REPLY:
sub_hdr = ofp_stats_reply()
- sub_hdr.unpack(binary_string)
- return stats_reply_to_class_map[sub_hdr.type]()
+ sub_hdr.unpack(binary_string[OFP_HEADER_BYTES:])
+ try:
+ obj = stats_reply_to_class_map[sub_hdr.type]()
+ except KeyError:
+ obj = None
+ return obj
elif hdr.type == OFPT_ERROR:
sub_hdr = ofp_error_msg()
- sub_hdr.unpack(binary_string)
+ sub_hdr.unpack(binary_string[OFP_HEADER_BYTES:])
return error_to_class_map[sub_hdr.type]()
else:
print "ERROR parsing packet to object"
@@ -114,12 +130,13 @@
"""
+ #@todo verify that object type is really a message
if raw:
print "raw packet message parsing not supported"
return None
obj = _of_message_to_object(binary_string)
- if obj != None:
+ if obj:
obj.unpack(binary_string)
return obj
@@ -147,15 +164,129 @@
return hdr
-def packet_to_flow(packet, wildcards=None, pkt_format="L2"):
+map_wc_field_to_match_member = {
+ 'OFPFW_DL_VLAN' : 'dl_vlan',
+ 'OFPFW_DL_SRC' : 'dl_src',
+ 'OFPFW_DL_DST' : 'dl_dst',
+ 'OFPFW_DL_TYPE' : 'dl_type',
+ 'OFPFW_NW_PROTO' : 'nw_proto',
+ 'OFPFW_TP_SRC' : 'tp_src',
+ 'OFPFW_TP_DST' : 'tp_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' : 'dl_vlan_pcp',
+ 'OFPFW_NW_TOS' : 'nw_tos'
+}
+
+
+def parse_mac(mac_str):
"""
- Create a flow that matches packet with the given wildcards
+ Parse a MAC address
+
+ Parse a MAC address ':' separated string of hex digits to an
+ array of integer values. '00:d0:05:5d:24:00' => [0, 208, 5, 93, 36, 0]
+ @param mac_str The string to convert
+ @return Array of 6 integer values
+ """
+ return map(lambda val:eval("0x" + val), mac_str.split(":"))
+
+def parse_ip(ip_str):
+ """
+ Parse an IP address
+
+ Parse an IP address '.' separated string of decimal digits to an
+ host ordered integer. '172.24.74.77' =>
+ @param ip_str The string to convert
+ @return Integer value
+ """
+ array = map(lambda val:eval(val),ip_str.split("."))
+ val = 0
+ for a in array:
+ val <<= 8
+ val += a
+ return val
+
+def packet_type_classify(ether):
+ try:
+ dot1q = ether[Dot1Q]
+ except:
+ dot1q = None
+ try:
+ ip = ether[IP]
+ except:
+ ip = None
+ try:
+ tcp = ether[TCP]
+ except:
+ tcp = None
+ try:
+ udp = ether[UDP]
+ except:
+ udp = None
+
+ # @todo arp and icmp are not yet supported
+ icmp = arp = None
+ return (dot1q, ip, tcp, udp, icmp, arp)
+
+def packet_to_flow_match(packet, pkt_format="L2"):
+ """
+ Create a flow match that matches packet with the given wildcards
@param packet The packet to use as a flow template
- @param wildcards Wildcards to place in the flow (ignore those
- fields from the packet)
- @param pkt_format May be one string from: L2, L3, ?
- Fields from unspecified layers are forced to be wildcards
+ @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.
+
+ @todo packet_to_flow: Not yet implemenated; see file packet_to_flow
"""
+ #@todo check min length of packet
+
+ if pkt_format.upper() != "L2":
+ print "ERROR: Only L2 packet supported for packet_to_flow"
+ return None
+
+ ether = scapy.all.Ether(packet)
+ # For now, assume ether IP packet and ignore wildcards
+ (dot1q, ip, tcp, udp, icmp, arp) = packet_type_classify(ether)
+
+ match = ofp_match()
+ match.wildcards = OFPFW_ALL
+ #@todo Check if packet is other than L2 format
+ match.dl_dst = parse_mac(ether.dst)
+ match.wildcards &= ~OFPFW_DL_DST
+ match.dl_src = parse_mac(ether.src)
+ match.wildcards &= ~OFPFW_DL_SRC
+ match.dl_type = ether.type
+ match.wildcards &= ~OFPFW_DL_TYPE
+
+ if dot1q:
+ match.dl_vlan = dot1q.vlan
+ match.wildcards &= ~OFPFW_DL_VLAN
+ match.dl_vlan_pcp = dot1q.prio
+ match.wildcards &= ~OFPFW_DL_VLAN_PCP
+
+ if ip:
+ match.nw_src = parse_ip(ip.src)
+ match.wildcards &= ~OFPFW_NW_SRC_MASK
+ match.nw_dst = parse_ip(ip.dst)
+ match.wildcards &= ~OFPFW_NW_DST_MASK
+ match.nw_tos = ip.tos
+ match.wildcards &= ~OFPFW_NW_TOS
+
+ if tcp:
+ match.tp_src = tcp.sport
+ match.wildcards &= ~OFPFW_TP_SRC
+ match.tp_dst = tcp.dport
+ match.wildcards &= ~OFPFW_TP_DST
+
+ #@todo Implement ICMP and ARP fields
+
+ return match