Skip to content

Commit 7eaaa69

Browse files
committed
Test SRv6 dataplane uN behavior (sonic-net#16660)
Test SRv6 dataplane uN and uN +USD behavior
1 parent a5afadf commit 7eaaa69

5 files changed

Lines changed: 204 additions & 6 deletions

File tree

tests/common/plugins/conditional_mark/tests_mark_conditions.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1884,6 +1884,18 @@ srv6/test_srv6_basic_sanity.py:
18841884
conditions:
18851885
- topo_name not in ["ciscovs-7nodes", "ciscovs-5nodes"]
18861886

1887+
srv6/test_srv6_dataplane.py:
1888+
skip:
1889+
reason: "Only target mellanox platform with 202412 image at this time"
1890+
conditions:
1891+
- "asic_type not in ['mellanox', 'broadcom'] or release not in ['202412']"
1892+
1893+
srv6/test_srv6_static_config.py:
1894+
skip:
1895+
reason: "Requires particular image support, skip in PR testing"
1896+
conditions:
1897+
- "release not in ['202412']"
1898+
18871899
#######################################
18881900
##### ssh #####
18891901
#######################################

tests/common/plugins/ptfadapter/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ def check_if_use_minigraph_from_tbinfo(tbinfo):
168168
return False
169169
return True
170170

171-
with PtfTestAdapter(tbinfo['ptf_ip'], ptf_nn_agent_port, 0, list(ifaces_map.keys()), ptfhost) as adapter:
171+
with PtfTestAdapter(tbinfo['ptf_ip'], tbinfo['ptf_ipv6'],
172+
ptf_nn_agent_port, 0, list(ifaces_map.keys()), ptfhost) as adapter:
172173
if not request.config.option.keep_payload:
173174
override_ptf_functions()
174175
node_id = request.module.__name__

tests/common/plugins/ptfadapter/ptfadapter.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ class PtfTestAdapter(BaseTest):
3030
# the number of currently established connections
3131
NN_STAT_CURRENT_CONNECTIONS = 201
3232

33-
def __init__(self, ptf_ip, ptf_nn_port, device_num, ptf_port_set, ptfhost):
33+
def __init__(self, ptf_ip, ptf_ipv6, ptf_nn_port, device_num, ptf_port_set, ptfhost):
3434
""" initialize PtfTestAdapter
3535
:param ptf_ip: PTF host IP
36+
:param ptf_ipv6: PTF host IPv6 address
3637
:param ptf_nn_port: PTF nanomessage agent port
3738
:param device_num: device number
3839
:param ptf_port_set: PTF ports
@@ -43,7 +44,7 @@ def __init__(self, ptf_ip, ptf_nn_port, device_num, ptf_port_set, ptfhost):
4344
self.payload_pattern = ""
4445
self.connected = False
4546
self.ptfhost = ptfhost
46-
self._init_ptf_dataplane(ptf_ip, ptf_nn_port, device_num, ptf_port_set)
47+
self._init_ptf_dataplane(ptf_ip, ptf_ipv6, ptf_nn_port, device_num, ptf_port_set)
4748

4849
def __enter__(self):
4950
""" enter in 'with' block """
@@ -64,17 +65,19 @@ def _check_ptf_nn_agent_availability(self, socket_addr):
6465
finally:
6566
sock.close()
6667

67-
def _init_ptf_dataplane(self, ptf_ip, ptf_nn_port, device_num, ptf_port_set, ptf_config=None):
68+
def _init_ptf_dataplane(self, ptf_ip, ptf_ipv6, ptf_nn_port, device_num, ptf_port_set, ptf_config=None):
6869
"""
6970
initialize ptf framework and establish connection to ptf_nn_agent
7071
running on PTF host
7172
:param ptf_ip: PTF host IP
73+
:param ptf_ipv6: PTF host IPv6 address
7274
:param ptf_nn_port: PTF nanomessage agent port
7375
:param device_num: device number
7476
:param ptf_port_set: PTF ports
7577
:return:
7678
"""
7779
self.ptf_ip = ptf_ip
80+
self.ptf_ipv6 = ptf_ipv6
7881
self.ptf_nn_port = ptf_nn_port
7982
self.device_num = device_num
8083
self.ptf_port_set = ptf_port_set
@@ -137,7 +140,8 @@ def reinit(self, ptf_config=None):
137140
self.ptfhost.command('supervisorctl update')
138141
self.ptfhost.command('supervisorctl restart ptf_nn_agent')
139142

140-
self._init_ptf_dataplane(self.ptf_ip, self.ptf_nn_port, self.device_num, self.ptf_port_set, ptf_config)
143+
self._init_ptf_dataplane(self.ptf_ip, self.ptf_ipv6, self.ptf_nn_port,
144+
self.device_num, self.ptf_port_set, ptf_config)
141145

142146
def update_payload(self, pkt):
143147
"""Update the payload of packet to the default pattern when certain conditions are met.

tests/srv6/srv6_utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,13 @@ def runSendReceive(pkt, src_port, exp_pkt, dst_ports, pkt_expected, ptfadapter):
128128
# Send the packet and poll on destination ports
129129
testutils.send(ptfadapter, src_port, pkt, 1)
130130
logger.debug("Sent packet: " + pkt.summary())
131+
132+
time.sleep(1)
131133
(index, rcv_pkt) = testutils.verify_packet_any_port(ptfadapter, exp_pkt, dst_ports)
132134
received = False
133135
if rcv_pkt:
134136
received = True
135-
pytest_assert(received is True)
137+
pytest_assert(received == pkt_expected)
136138
logger.debug('index=%s, received=%s' % (str(index), str(received)))
137139
if received:
138140
logger.debug("Received packet: " + scapy.Ether(rcv_pkt).summary())

tests/srv6/test_srv6_dataplane.py

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import pytest
2+
import time
3+
import random
4+
import logging
5+
from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest
6+
from scapy.layers.l2 import Ether
7+
8+
from srv6_utils import runSendReceive
9+
from common.helpers.voq_helpers import get_neighbor_info
10+
from ptf.testutils import simple_ipv6_sr_packet
11+
12+
logger = logging.getLogger(__name__)
13+
14+
pytestmark = [
15+
pytest.mark.asic("mellanox", "broadcom"),
16+
pytest.mark.topology("t0", "t1")
17+
]
18+
19+
20+
def get_ptf_src_port_and_dut_port_and_neighbor(dut, tbinfo):
21+
"""Get the PTF port mapping for the duthost or an asic of the duthost"""
22+
dut_mg_facts = dut.get_extended_minigraph_facts(tbinfo)
23+
ports_map = dut_mg_facts["minigraph_ptf_indices"]
24+
if len(ports_map) == 0:
25+
pytest.skip("No PTF ports found for {}".format(dut))
26+
27+
lldp_table = dut.command("show lldp table")['stdout'].split("\n")[3:]
28+
neighbor_table = [line.split() for line in lldp_table]
29+
for entry in neighbor_table:
30+
intf = entry[0]
31+
if intf in ports_map:
32+
return intf, ports_map[intf], entry[1] # local intf, ptf_src_port, neighbor hostname
33+
34+
dut_port, ptf_src_port = random.choice(ports_map)
35+
return dut_port, ptf_src_port, None
36+
37+
38+
@pytest.mark.parametrize("with_srh", [True, False])
39+
def test_srv6_uN_forwarding(duthosts, enum_frontend_dut_hostname, enum_frontend_asic_index,
40+
ptfadapter, tbinfo, nbrhosts, with_srh):
41+
duthost = duthosts[enum_frontend_dut_hostname]
42+
asic_index = enum_frontend_asic_index
43+
44+
if duthost.is_multi_asic:
45+
cli_options = " -n " + duthost.get_namespace_from_asic_id(asic_index)
46+
dut_asic = duthost.asic_instance[asic_index]
47+
dut_mac = dut_asic.get_router_mac()
48+
dut_port, ptf_src_port, neighbor = get_ptf_src_port_and_dut_port_and_neighbor(dut_asic, tbinfo)
49+
else:
50+
cli_options = ''
51+
dut_mac = duthost._get_router_mac()
52+
dut_port, ptf_src_port, neighbor = get_ptf_src_port_and_dut_port_and_neighbor(duthost, tbinfo)
53+
54+
logger.info("Doing test on DUT port {} | PTF port {}".format(dut_port, ptf_src_port))
55+
56+
# get neighbor IP
57+
lines = duthost.command("show ipv6 bgp sum")['stdout'].split("\n")
58+
for line in lines:
59+
if neighbor in line:
60+
neighbor_ip = line.split()[0]
61+
assert neighbor_ip
62+
63+
# use DUT portchannel if applicable
64+
pc_info = duthost.command("show int portchannel")['stdout']
65+
if dut_port in pc_info:
66+
lines = pc_info.split("\n")
67+
for line in lines:
68+
if dut_port in line:
69+
dut_port = line.split()[1]
70+
71+
sonic_db_cli = "sonic-db-cli" + cli_options
72+
73+
# add a locator configuration entry
74+
duthost.command(sonic_db_cli + " CONFIG_DB HSET SRV6_MY_LOCATORS\\|loc1 prefix fcbb:bbbb:1::")
75+
# add a uN sid configuration entry
76+
duthost.command(sonic_db_cli +
77+
" CONFIG_DB HSET SRV6_MY_SIDS\\|loc1\\|fcbb:bbbb:1::/48 action uN decap_dscp_mode pipe")
78+
# add the static route for IPv6 forwarding towards PTF's uSID
79+
duthost.command(sonic_db_cli + " CONFIG_DB HSET STATIC_ROUTE\\|default\\|fcbb:bbbb:2::/48 nexthop {} ifname {}"
80+
.format(neighbor_ip, dut_port))
81+
time.sleep(5)
82+
83+
for i in range(0, 10):
84+
if with_srh:
85+
injected_pkt = simple_ipv6_sr_packet(
86+
eth_dst=dut_mac,
87+
eth_src=ptfadapter.dataplane.get_mac(0, ptf_src_port).decode(),
88+
ipv6_src=ptfadapter.ptf_ipv6,
89+
ipv6_dst="fcbb:bbbb:1:2::",
90+
srh_seg_left=1,
91+
srh_nh=41,
92+
inner_frame=IPv6()/ICMPv6EchoRequest(seq=i)
93+
)
94+
else:
95+
injected_pkt = Ether(dst=dut_mac, src=ptfadapter.dataplane.get_mac(0, ptf_src_port).decode()) \
96+
/ IPv6(src=ptfadapter.ptf_ipv6, dst="fcbb:bbbb:1:2::") \
97+
/ IPv6() / ICMPv6EchoRequest(seq=i)
98+
99+
expected_pkt = injected_pkt.copy()
100+
expected_pkt['Ether'].dst = get_neighbor_info(neighbor_ip, nbrhosts)['mac']
101+
expected_pkt['Ether'].src = dut_mac
102+
expected_pkt['IPv6'].dst = "fcbb:bbbb:2::"
103+
expected_pkt['IPv6'].hlim -= 1
104+
logger.debug("Expected packet #{}: {}".format(i, expected_pkt.summary()))
105+
runSendReceive(injected_pkt, ptf_src_port, expected_pkt, [ptf_src_port], True, ptfadapter)
106+
107+
# delete the SRv6 configuration
108+
duthost.command(sonic_db_cli + " CONFIG_DB DEL SRV6_MY_LOCATORS\\|loc1")
109+
duthost.command(sonic_db_cli + " CONFIG_DB DEL SRV6_MY_SIDS\\|loc1\\|fcbb:bbbb:1::/48")
110+
duthost.command(sonic_db_cli + " CONFIG_DB DEL STATIC_ROUTE\\|default\\|fcbb:bbbb:2::/48")
111+
112+
113+
@pytest.mark.parametrize("with_srh", [True, False])
114+
def test_srv6_uN_decap_pipe_mode(duthosts, enum_frontend_dut_hostname, enum_frontend_asic_index,
115+
ptfadapter, tbinfo, nbrhosts, with_srh):
116+
duthost = duthosts[enum_frontend_dut_hostname]
117+
asic_index = enum_frontend_asic_index
118+
119+
if duthost.is_multi_asic:
120+
cli_options = " -n " + duthost.get_namespace_from_asic_id(asic_index)
121+
dut_asic = duthost.asic_instance[asic_index]
122+
dut_mac = dut_asic.get_router_mac()
123+
dut_port, ptf_src_port, neighbor = get_ptf_src_port_and_dut_port_and_neighbor(dut_asic, tbinfo)
124+
else:
125+
cli_options = ''
126+
dut_mac = duthost._get_router_mac()
127+
dut_port, ptf_src_port, neighbor = get_ptf_src_port_and_dut_port_and_neighbor(duthost, tbinfo)
128+
129+
logger.info("Doing test on DUT port {} | PTF port {}".format(dut_port, ptf_src_port))
130+
131+
# get neighbor IP
132+
lines = duthost.command("show ipv6 bgp sum")['stdout'].split("\n")
133+
for line in lines:
134+
if neighbor in line:
135+
neighbor_ip = line.split()[0]
136+
assert neighbor_ip
137+
138+
# use DUT portchannel if applicable
139+
pc_info = duthost.command("show int portchannel")['stdout']
140+
if dut_port in pc_info:
141+
lines = pc_info.split("\n")
142+
for line in lines:
143+
if dut_port in line:
144+
dut_port = line.split()[1]
145+
146+
sonic_db_cli = "sonic-db-cli" + cli_options
147+
148+
# add a locator configuration entry
149+
duthost.command(sonic_db_cli + " CONFIG_DB HSET SRV6_MY_LOCATORS\\|loc1 prefix fcbb:bbbb:1::")
150+
# add a uN sid configuration entry
151+
duthost.command(sonic_db_cli +
152+
" CONFIG_DB HSET SRV6_MY_SIDS\\|loc1\\|fcbb:bbbb:1::/48 action uN decap_dscp_mode pipe")
153+
154+
time.sleep(5)
155+
156+
for i in range(0, 10):
157+
if with_srh:
158+
injected_pkt = simple_ipv6_sr_packet(
159+
eth_dst=dut_mac,
160+
eth_src=ptfadapter.dataplane.get_mac(0, ptf_src_port).decode(),
161+
ipv6_src=ptfadapter.ptf_ipv6,
162+
ipv6_dst="fcbb:bbbb:1::",
163+
srh_seg_left=1,
164+
srh_nh=41,
165+
inner_frame=IPv6(dst=neighbor_ip, src=ptfadapter.ptf_ipv6)/ICMPv6EchoRequest(seq=i)
166+
)
167+
else:
168+
injected_pkt = Ether(dst=dut_mac, src=ptfadapter.dataplane.get_mac(0, ptf_src_port).decode()) \
169+
/ IPv6(src=ptfadapter.ptf_ipv6, dst="fcbb:bbbb:1::") \
170+
/ IPv6(dst=neighbor_ip, src=ptfadapter.ptf_ipv6) / ICMPv6EchoRequest(seq=i)
171+
172+
expected_pkt = Ether(dst=get_neighbor_info(neighbor_ip, nbrhosts)['mac'], src=dut_mac) / \
173+
IPv6(dst=neighbor_ip, src=ptfadapter.ptf_ipv6)/ICMPv6EchoRequest(seq=i)
174+
logger.debug("Expected packet #{}: {}".format(i, expected_pkt.summary()))
175+
runSendReceive(injected_pkt, ptf_src_port, expected_pkt, [ptf_src_port], True, ptfadapter)
176+
177+
# delete the SRv6 configuration
178+
duthost.command(sonic_db_cli + " CONFIG_DB DEL SRV6_MY_LOCATORS\\|loc1")
179+
duthost.command(sonic_db_cli + " CONFIG_DB DEL SRV6_MY_SIDS\\|loc1\\|fcbb:bbbb:1::/48")

0 commit comments

Comments
 (0)