Initial set of Fabric switch test cases
Change-Id: I86fd2b67d3b773aa496f5ef61f1e1fdf51fd9925
diff --git a/Fabric/Utilities/src/python/oftest/parse.py b/Fabric/Utilities/src/python/oftest/parse.py
new file mode 100644
index 0000000..1f94c45
--- /dev/null
+++ b/Fabric/Utilities/src/python/oftest/parse.py
@@ -0,0 +1,301 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# 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 License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""
+Utility parsing functions
+"""
+
+import sys
+import socket
+import packet as scapy
+
+def parse_mac(mac_str):
+ """
+ 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: int(val, 16), 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: int(val), ip_str.split("."))
+ val = 0
+ for a in array:
+ val <<= 8
+ 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]
+ except:
+ dot1q = None
+
+ try:
+ ip = ether[scapy.IP]
+ except:
+ ip = None
+
+ try:
+ tcp = ether[scapy.TCP]
+ except:
+ tcp = None
+
+ try:
+ udp = ether[scapy.UDP]
+ except:
+ udp = None
+
+ try:
+ icmp = ether[scapy.ICMP]
+ except:
+ icmp = None
+
+ try:
+ arp = ether[scapy.ARP]
+ except:
+ arp = None
+ return (dot1q, ip, tcp, udp, icmp, arp)
+
+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
+ @return An loxi.of10.match object
+
+ @todo check min length of packet
+ """
+ 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)
+ elif ofp.OFP_VERSION == 5:
+ return packet_to_flow_match_v5(packet)
+ else:
+ raise NotImplementedError()
+
+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)
+ else:
+ ether = packet
+
+ # For now, assume ether IP packet and ignore wildcards
+ try:
+ (dot1q, ip, tcp, udp, icmp, arp) = packet_type_classify(ether)
+ except:
+ raise ValueError("could not classify packet")
+
+ match = ofp.match()
+ match.wildcards = ofp.OFPFW_ALL
+ #@todo Check if packet is other than L2 format
+ match.eth_dst = parse_mac(ether.dst)
+ match.wildcards &= ~ofp.OFPFW_DL_DST
+ match.eth_src = parse_mac(ether.src)
+ match.wildcards &= ~ofp.OFPFW_DL_SRC
+ match.eth_type = ether.type
+ match.wildcards &= ~ofp.OFPFW_DL_TYPE
+
+ if dot1q:
+ match.vlan_vid = dot1q.vlan
+ match.vlan_pcp = dot1q.prio
+ match.eth_type = dot1q.type
+ else:
+ match.vlan_vid = ofp.OFP_VLAN_NONE
+ match.vlan_pcp = 0
+ match.wildcards &= ~ofp.OFPFW_DL_VLAN
+ match.wildcards &= ~ofp.OFPFW_DL_VLAN_PCP
+
+ if ip:
+ match.ipv4_src = parse_ip(ip.src)
+ match.wildcards &= ~ofp.OFPFW_NW_SRC_MASK
+ match.ipv4_dst = parse_ip(ip.dst)
+ match.wildcards &= ~ofp.OFPFW_NW_DST_MASK
+ match.ip_dscp = ip.tos
+ match.wildcards &= ~ofp.OFPFW_NW_TOS
+
+ if tcp:
+ match.ip_proto = 6
+ match.wildcards &= ~ofp.OFPFW_NW_PROTO
+ elif not tcp and udp:
+ tcp = udp
+ match.ip_proto = 17
+ match.wildcards &= ~ofp.OFPFW_NW_PROTO
+
+ if tcp:
+ match.tcp_src = tcp.sport
+ match.wildcards &= ~ofp.OFPFW_TP_SRC
+ match.tcp_dst = tcp.dport
+ match.wildcards &= ~ofp.OFPFW_TP_DST
+
+ if icmp:
+ match.ip_proto = 1
+ match.tcp_src = icmp.type
+ match.tcp_dst = icmp.code
+ match.wildcards &= ~ofp.OFPFW_NW_PROTO
+
+ if arp:
+ match.ip_proto = arp.op
+ match.wildcards &= ~ofp.OFPFW_NW_PROTO
+ match.ipv4_src = parse_ip(arp.psrc)
+ match.wildcards &= ~ofp.OFPFW_NW_SRC_MASK
+ match.ipv4_dst = parse_ip(arp.pdst)
+ 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_v5(packet):
+ """
+ OpenFlow 1.3 implementation of packet_to_flow_match
+ """
+ import loxi.of14 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(ofp.OFPVID_PRESENT|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