Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added SCTP support #250

Merged
merged 1 commit into from
Jul 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/python/oftest/packet.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import scapy.route
import scapy.layers.l2
import scapy.layers.inet
import scapy.layers.sctp
if not config["disable_ipv6"]:
import scapy.route6
import scapy.layers.inet6
Expand All @@ -28,6 +29,7 @@
TCP = scapy.layers.inet.TCP
UDP = scapy.layers.inet.UDP
ICMP = scapy.layers.inet.ICMP
SCTP = scapy.layers.sctp.SCTP

if not config["disable_ipv6"]:
IPv6 = scapy.layers.inet6.IPv6
Expand Down
137 changes: 99 additions & 38 deletions src/python/oftest/testutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,68 @@ def required_wildcards(parent):
else:
return 0

def simple_tcp_packet(pktlen=100,
def simple_sctp_packet(pktlen=100,
eth_dst='00:01:02:03:04:05',
eth_src='00:06:07:08:09:0a',
dl_vlan_enable=False,
vlan_vid=0,
vlan_pcp=0,
dl_vlan_cfi=0,
ip_src='192.168.0.1',
ip_dst='192.168.0.2',
ip_tos=0,
ip_ttl=64,
sctp_sport=1234,
sctp_dport=80,
ip_ihl=None,
ip_options=False
):
"""
Return a simple dataplane SCTP packet

Supports a few parameters:
@param len Length of packet in bytes w/o CRC
@param eth_dst Destinatino MAC
@param eth_src Source MAC
@param dl_vlan_enable True if the packet is with vlan, False otherwise
@param vlan_vid VLAN ID
@param vlan_pcp VLAN priority
@param ip_src IP source
@param ip_dst IP destination
@param ip_tos IP ToS
@param ip_ttl IP TTL
@param sctp_dport SCTP destination port
@param sctp_sport SCTP source port

Generates a simple SCTP request. Users
shouldn't assume anything about this packet other than that
it is a valid ethernet/IP/SCTP frame.
"""

if MINSIZE > pktlen:
pktlen = MINSIZE

# Note Dot1Q.id is really CFI
if (dl_vlan_enable):
pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
scapy.Dot1Q(prio=vlan_pcp, id=dl_vlan_cfi, vlan=vlan_vid)/ \
scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
scapy.SCTP(sport=sctp_sport, dport=sctp_dport)
else:
if not ip_options:
pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
scapy.SCTP(sport=sctp_sport, dport=sctp_dport)
else:
pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl, options=ip_options)/ \
scapy.SCTP(sport=sctp_sport, dport=sctp_dport)

pkt = pkt/("D" * (pktlen - len(pkt)))

return pkt

def simple_tcp_packet(pktlen=100,
eth_dst='00:01:02:03:04:05',
eth_src='00:06:07:08:09:0a',
dl_vlan_enable=False,
Expand Down Expand Up @@ -100,7 +161,7 @@ def simple_tcp_packet(pktlen=100,
@param ip_ttl IP TTL
@param tcp_dport TCP destination port
@param tcp_sport TCP source port
@param tcp_flags TCP Control flags
@param tcp_flags TCP Control flags

Generates a simple TCP request. Users
shouldn't assume anything about this packet other than that
Expand Down Expand Up @@ -161,7 +222,7 @@ def simple_tcpv6_packet(pktlen=100,
@param ipv6_fl IPv6 flow label
@param tcp_dport TCP destination port
@param tcp_sport TCP source port
@param tcp_flags TCP Control flags
@param tcp_flags TCP Control flags

Generates a simple TCP request. Users shouldn't assume anything about this
packet other than that it is a valid ethernet/IPv6/TCP frame.
Expand Down Expand Up @@ -286,7 +347,7 @@ def simple_udpv6_packet(pktlen=100,

return pkt

def simple_icmp_packet(pktlen=60,
def simple_icmp_packet(pktlen=60,
eth_dst='00:01:02:03:04:05',
eth_src='00:06:07:08:09:0a',
dl_vlan_enable=False,
Expand All @@ -296,7 +357,7 @@ def simple_icmp_packet(pktlen=60,
ip_dst='192.168.0.2',
ip_tos=0,
ip_ttl=64,
ip_id=1,
ip_id=1,
icmp_type=8,
icmp_code=0,
icmp_data=''):
Expand Down Expand Up @@ -452,7 +513,7 @@ def simple_icmpv6_packet(pktlen=100,

return pkt

def simple_arp_packet(pktlen=68,
def simple_arp_packet(pktlen=68,
eth_dst='ff:ff:ff:ff:ff:ff',
eth_src='00:06:07:08:09:0a',
vlan_vid=0,
Expand Down Expand Up @@ -507,7 +568,7 @@ def simple_eth_packet(pktlen=60,

return pkt

def qinq_tcp_packet(pktlen=100,
def qinq_tcp_packet(pktlen=100,
eth_dst='00:01:02:03:04:05',
eth_src='00:06:07:08:09:0a',
dl_vlan_outer=20,
Expand Down Expand Up @@ -693,7 +754,7 @@ def port_config_get(controller, port_no):
for port in ports:
if port.port_no == port_no:
return (port.hw_addr, port.config, port.advertised)

logging.warn("Did not find port number for port config")
return None, None, None

Expand Down Expand Up @@ -738,7 +799,7 @@ def receive_pkt_check(dp, pkt, yes_ports, no_ports, assert_if):
logging.debug("Checking for pkt on port " + str(ofport))
(rcv_port, rcv_pkt, pkt_time) = dp.poll(
port_number=ofport, exp_pkt=exp_pkt_arg)
assert_if.assertTrue(rcv_pkt is not None,
assert_if.assertTrue(rcv_pkt is not None,
"Did not receive pkt on " + str(ofport))
if not oftest.dataplane.match_exp_pkt(pkt, rcv_pkt):
logging.debug("Expected %s" % format_packet(pkt))
Expand All @@ -752,7 +813,7 @@ def receive_pkt_check(dp, pkt, yes_ports, no_ports, assert_if):
logging.debug("Negative check for pkt on port " + str(ofport))
(rcv_port, rcv_pkt, pkt_time) = dp.poll(
port_number=ofport, timeout=0, exp_pkt=exp_pkt_arg)
assert_if.assertTrue(rcv_pkt is None,
assert_if.assertTrue(rcv_pkt is None,
"Unexpected pkt on port " + str(ofport))


Expand Down Expand Up @@ -783,12 +844,12 @@ def receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port):
port_number=check_port, exp_pkt=exp_pkt_arg)

if rcv_pkt is None:
logging.error("ERROR: No packet received from " +
logging.error("ERROR: No packet received from " +
str(check_port))

parent.assertTrue(rcv_pkt is not None,
"Did not receive packet port " + str(check_port))
logging.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
logging.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
str(rcv_port))

if str(exp_pkt) != str(rcv_pkt):
Expand Down Expand Up @@ -826,8 +887,8 @@ def match_verify(parent, req_match, res_match):
'Match failed: vlan_vid: ' + str(req_match.vlan_vid) +
" != " + str(res_match.vlan_vid))
parent.assertEqual(req_match.vlan_pcp, res_match.vlan_pcp,
'Match failed: vlan_pcp: ' +
str(req_match.vlan_pcp) + " != " +
'Match failed: vlan_pcp: ' +
str(req_match.vlan_pcp) + " != " +
str(res_match.vlan_pcp))
parent.assertEqual(req_match.eth_type, res_match.eth_type,
'Match failed: eth_type: ' + str(req_match.eth_type) +
Expand All @@ -852,11 +913,11 @@ def match_verify(parent, req_match, res_match):
and ((req_match.ip_proto == TCP_PROTOCOL)
or (req_match.ip_proto == UDP_PROTOCOL))):
parent.assertEqual(req_match.tcp_src, res_match.tcp_src,
'Match failed: tcp_src: ' +
'Match failed: tcp_src: ' +
str(req_match.tcp_src) +
" != " + str(res_match.tcp_src))
parent.assertEqual(req_match.tcp_dst, res_match.tcp_dst,
'Match failed: tcp_dst: ' +
'Match failed: tcp_dst: ' +
str(req_match.tcp_dst) +
" != " + str(res_match.tcp_dst))

Expand All @@ -874,7 +935,7 @@ def flow_msg_create(parent, pkt, ing_port=None, action_list=None, wildcards=None
"""
Create a flow message

Match on packet with given wildcards.
Match on packet with given wildcards.
See flow_match_test for other parameter descriptoins
@param egr_queue if not None, make the output an enqueue action
@param in_band if True, do not wildcard ingress port
Expand Down Expand Up @@ -941,7 +1002,7 @@ def flow_msg_install(parent, request, clear_table_override=None):
if(clear_table_override != None):
clear_table = clear_table_override

if clear_table:
if clear_table:
logging.debug("Clear flow table")
delete_all_flows(parent.controller)

Expand All @@ -963,21 +1024,21 @@ def flow_match_test_port_pair(parent, ing_port, egr_ports, wildcards=None,

if wildcards is None:
wildcards = required_wildcards(parent)
logging.info("Pkt match test: " + str(ing_port) + " to " +
logging.info("Pkt match test: " + str(ing_port) + " to " +
str(egr_ports))
logging.debug(" WC: " + hex(wildcards) + " vlan: " + str(vlan_vid))
if pkt is None:
pkt = simple_tcp_packet(dl_vlan_enable=(vlan_vid >= 0), vlan_vid=vlan_vid)
if exp_pkt is None:
exp_pkt = pkt

request = flow_msg_create(parent, pkt, ing_port=ing_port,
request = flow_msg_create(parent, pkt, ing_port=ing_port,
wildcards=wildcards, egr_ports=egr_ports,
action_list=action_list)

flow_msg_install(parent, request)

logging.debug("Send packet: " + str(ing_port) + " to " +
logging.debug("Send packet: " + str(ing_port) + " to " +
str(egr_ports))
parent.dataplane.send(ing_port, str(pkt))

Expand Down Expand Up @@ -1037,16 +1098,16 @@ def get_egr_list(parent, of_ports, how_many, exclude_list=[]):

count = 0
egr_ports = []
for egr_idx in range(len(of_ports)):
for egr_idx in range(len(of_ports)):
if of_ports[egr_idx] not in exclude_list:
egr_ports.append(of_ports[egr_idx])
count += 1
if count >= how_many:
return egr_ports
logging.debug("Could not generate enough egress ports for test")
return []
def flow_match_test(parent, port_map, wildcards=None, vlan_vid=-1, pkt=None,

def flow_match_test(parent, port_map, wildcards=None, vlan_vid=-1, pkt=None,
exp_pkt=None, action_list=None,
max_test=0, egr_count=1, ing_port=False):
"""
Expand All @@ -1071,18 +1132,18 @@ def flow_match_test(parent, port_map, wildcards=None, vlan_vid=-1, pkt=None,

if egr_count == -1:
egr_count = test_param_get('egr_count', default=2)

for ing_idx in range(len(of_ports)):
ingress_port = of_ports[ing_idx]
egr_ports = get_egr_list(parent, of_ports, egr_count,
egr_ports = get_egr_list(parent, of_ports, egr_count,
exclude_list=[ingress_port])
if ing_port:
egr_ports.append(ofp.OFPP_IN_PORT)
if len(egr_ports) == 0:
parent.assertTrue(0, "Failed to generate egress port list")

flow_match_test_port_pair(parent, ingress_port, egr_ports,
wildcards=wildcards, vlan_vid=vlan_vid,
flow_match_test_port_pair(parent, ingress_port, egr_ports,
wildcards=wildcards, vlan_vid=vlan_vid,
pkt=pkt, exp_pkt=exp_pkt,
action_list=action_list)
test_count += 1
Expand Down Expand Up @@ -1114,7 +1175,7 @@ def test_param_get(key, default=None):
on the command line, return val (as interpreted by exec). Otherwise
return default value.

WARNING: TEST PARAMETERS MUST BE PYTHON IDENTIFIERS;
WARNING: TEST PARAMETERS MUST BE PYTHON IDENTIFIERS;
eg egr_count, not egr-count.
"""
try:
Expand Down Expand Up @@ -1184,7 +1245,7 @@ def action_generate(parent, field_to_mod, mod_field_vals):

return act

def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
mod_fields=[], tp="tcp", check_test_params=False):
"""
Set up the ingress and expected packet and action list for a test
Expand Down Expand Up @@ -1324,7 +1385,7 @@ def all_stats_get(parent):
"""
Get the aggregate stats for all flows in the table
@param parent Test instance with controller connection and assert
@returns dict with keys flows, packets, bytes, active (flows),
@returns dict with keys flows, packets, bytes, active (flows),
lookups, matched
"""
stat_req = ofp.message.aggregate_stats_request()
Expand All @@ -1339,14 +1400,14 @@ def all_stats_get(parent):
parent.assertTrue(len(reply.entries) == 1, "Did not receive flow stats reply")

for obj in reply.entries:
(rv["flows"], rv["packets"], rv["bytes"]) = (obj.flow_count,
(rv["flows"], rv["packets"], rv["bytes"]) = (obj.flow_count,
obj.packet_count, obj.byte_count)
break

request = ofp.message.table_stats_request()
(reply , pkt) = parent.controller.transact(request)


(rv["active"], rv["lookups"], rv["matched"]) = (0,0,0)
for obj in reply.entries:
rv["active"] += obj.active_count
Expand All @@ -1356,7 +1417,7 @@ def all_stats_get(parent):
return rv

_import_blacklist.add('FILTER')
FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.'
FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.'
for x in range(256)])

def hex_dump_buffer(src, length=16):
Expand All @@ -1376,7 +1437,7 @@ def hex_dump_buffer(src, length=16):
return ''.join(result)

def format_packet(pkt):
return "Packet length %d \n%s" % (len(str(pkt)),
return "Packet length %d \n%s" % (len(str(pkt)),
hex_dump_buffer(str(pkt)))

def inspect_packet(pkt):
Expand Down Expand Up @@ -1857,7 +1918,7 @@ def verify_capability(test, capability):
test.assertIn(capability, ofp.const.ofp_capabilities_map,
"Capability code %d does not exist." % capability)
capability_str = ofp.const.ofp_capabilities_map[capability]

logging.info(("Sending features_request to test if capability "
"%s is supported."), capability_str)
req = ofp.message.features_request()
Expand All @@ -1867,7 +1928,7 @@ def verify_capability(test, capability):
("Unexpected packet type %d received in response to "
"OFPT_FEATURES_REQUEST") % res.type)
logging.info("Received features_reply.")

if (res.capabilities & capability) > 0:
logging.info("Switch capabilities bitmask claims to support %s",
capability_str)
Expand All @@ -1890,7 +1951,7 @@ def verify_configuration_flag(test, flag):
"flag %s does not exist." % flag)
flag_str = ofp.const.ofp_config_flags_map[flag]

logging.info("Sending OFPT_GET_CONFIG_REQUEST.")
logging.info("Sending OFPT_GET_CONFIG_REQUEST.")
req = ofp.message.get_config_request()
rv = test.controller.message_send(req)
test.assertNotEqual(rv, -1, "Not able to send get_config_request.")
Expand Down