Skip to content

Commit 1cd419b

Browse files
Adjust test_bgpmon.py to handle running over ipv6 only topologies (#21377) (#22024)
* Adjust test_bgpmon.py to handle running over ipv6 only topologies Co-authored-by: gshemesh2 <[email protected]>
1 parent d394f7d commit 1cd419b

File tree

2 files changed

+172
-71
lines changed

2 files changed

+172
-71
lines changed

tests/bgp/test_bgpmon.py

Lines changed: 171 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
import ptf.packet as scapy
77
from ptf.mask import Mask
88
import json
9-
from tests.common.fixtures.ptfhost_utils import change_mac_addresses # noqa:F401
10-
from tests.common.fixtures.ptfhost_utils import remove_ip_addresses # noqa:F401
11-
from tests.common.helpers.generators import generate_ip_through_default_route
9+
from tests.common.fixtures.ptfhost_utils import change_mac_addresses # noqa F401
10+
from tests.common.fixtures.ptfhost_utils import remove_ip_addresses # noqa F401
11+
from tests.common.helpers.generators import generate_ip_through_default_route, generate_ip_through_default_v6_route
1212
from tests.common.helpers.assertions import pytest_assert
1313
from tests.common.utilities import wait_until
1414
from tests.common.utilities import wait_tcp_connection
15+
from tests.common.utilities import is_ipv6_only_topology
1516
from bgp_helpers import BGPMON_TEMPLATE_FILE, BGPMON_CONFIG_FILE, BGP_MONITOR_NAME, BGP_MONITOR_PORT
1617

1718
pytestmark = [
@@ -22,12 +23,14 @@
2223
BGP_CONNECT_TIMEOUT = 121
2324
MAX_TIME_FOR_BGPMON = 180
2425
ZERO_ADDR = r'0.0.0.0/0'
26+
ZERO_ADDR_V6 = r'::/0'
2527
logger = logging.getLogger(__name__)
2628

2729

28-
def get_default_route_ports(host, tbinfo, default_addr=ZERO_ADDR):
30+
def get_default_route_ports(host, tbinfo, default_addr=ZERO_ADDR, is_ipv6=False):
2931
mg_facts = host.get_extended_minigraph_facts(tbinfo)
30-
route_info = json.loads(host.shell("show ip route {} json".format(default_addr))['stdout'])
32+
ip_cmd = "ipv6" if is_ipv6 else "ip"
33+
route_info = json.loads(host.shell("show {} route {} json".format(ip_cmd, default_addr))['stdout'])
3134
ports = []
3235
for route in route_info[default_addr]:
3336
if route['protocol'] != 'bgp':
@@ -47,14 +50,41 @@ def get_default_route_ports(host, tbinfo, default_addr=ZERO_ADDR):
4750

4851

4952
@pytest.fixture
50-
def common_setup_teardown(dut_with_default_route, tbinfo):
53+
def common_setup_teardown(dut_with_default_route, tbinfo):
5154
duthost = dut_with_default_route
52-
peer_addr = generate_ip_through_default_route(duthost)
53-
pytest_assert(peer_addr, "Failed to generate ip address for test")
54-
peer_addr = str(IPNetwork(peer_addr).ip)
55-
peer_ports = get_default_route_ports(duthost, tbinfo)
55+
is_ipv6_only = is_ipv6_only_topology(tbinfo)
56+
57+
# Generate a unique IPV4 address to be used as the BGP router identifier for the monitor connection
58+
router_id = generate_ip_through_default_route(duthost)
59+
pytest_assert(router_id, "Failed to generate router id")
60+
router_id = str(IPNetwork(router_id).ip)
61+
62+
if is_ipv6_only:
63+
peer_addr = generate_ip_through_default_v6_route(duthost)
64+
pytest_assert(peer_addr, "Failed to generate ipv6 address for test")
65+
peer_addr = str(IPNetwork(peer_addr).ip)
66+
else:
67+
peer_addr = router_id
68+
69+
peer_ports = get_default_route_ports(
70+
duthost,
71+
tbinfo,
72+
default_addr=ZERO_ADDR_V6 if is_ipv6_only else ZERO_ADDR,
73+
is_ipv6=is_ipv6_only
74+
)
5675
mg_facts = duthost.minigraph_facts(host=duthost.hostname)['ansible_facts']
57-
local_addr = mg_facts['minigraph_lo_interfaces'][0]['addr']
76+
77+
local_addr = None
78+
for lo_intf in mg_facts['minigraph_lo_interfaces']:
79+
if is_ipv6_only and ':' in lo_intf['addr']:
80+
local_addr = lo_intf['addr']
81+
break
82+
elif not is_ipv6_only and ':' not in lo_intf['addr']:
83+
local_addr = lo_intf['addr']
84+
break
85+
86+
pytest_assert(local_addr, "Failed to get appropriate loopback address")
87+
5888
# Assign peer addr to an interface on ptf
5989
logger.info("Generated peer address {}".format(peer_addr))
6090
bgpmon_args = {
@@ -67,35 +97,55 @@ def common_setup_teardown(dut_with_default_route, tbinfo):
6797
bgpmon_template = Template(open(BGPMON_TEMPLATE_FILE).read())
6898
duthost.copy(content=bgpmon_template.render(**bgpmon_args),
6999
dest=BGPMON_CONFIG_FILE)
70-
yield local_addr, peer_addr, peer_ports, mg_facts['minigraph_bgp_asn']
100+
yield local_addr, peer_addr, peer_ports, mg_facts['minigraph_bgp_asn'], is_ipv6_only, router_id
71101
# Cleanup bgp monitor
72102
duthost.run_sonic_db_cli_cmd("CONFIG_DB del 'BGP_MONITORS|{}'".format(peer_addr), asic_index='all')
73103
duthost.file(path=BGPMON_CONFIG_FILE, state='absent')
74104

75105

76-
def build_syn_pkt(local_addr, peer_addr):
77-
pkt = testutils.simple_tcp_packet(
78-
pktlen=54,
79-
ip_src=local_addr,
80-
ip_dst=peer_addr,
81-
tcp_dport=BGP_PORT,
82-
tcp_flags="S"
83-
)
84-
exp_packet = Mask(pkt)
85-
exp_packet.set_do_not_care_scapy(scapy.Ether, "dst")
86-
exp_packet.set_do_not_care_scapy(scapy.Ether, "src")
87-
88-
exp_packet.set_do_not_care_scapy(scapy.IP, "version")
89-
exp_packet.set_do_not_care_scapy(scapy.IP, "ihl")
90-
exp_packet.set_do_not_care_scapy(scapy.IP, "tos")
91-
exp_packet.set_do_not_care_scapy(scapy.IP, "len")
92-
exp_packet.set_do_not_care_scapy(scapy.IP, "flags")
93-
exp_packet.set_do_not_care_scapy(scapy.IP, "id")
94-
exp_packet.set_do_not_care_scapy(scapy.IP, "frag")
95-
exp_packet.set_do_not_care_scapy(scapy.IP, "ttl")
96-
exp_packet.set_do_not_care_scapy(scapy.IP, "chksum")
97-
exp_packet.set_do_not_care_scapy(scapy.IP, "options")
106+
def build_syn_pkt(local_addr, peer_addr, is_ipv6=False):
107+
if is_ipv6:
108+
pkt = testutils.simple_tcpv6_packet(
109+
pktlen=74,
110+
ipv6_src=local_addr,
111+
ipv6_dst=peer_addr,
112+
tcp_dport=BGP_PORT,
113+
tcp_flags="S"
114+
)
115+
exp_packet = Mask(pkt)
116+
exp_packet.set_do_not_care_scapy(scapy.Ether, "dst")
117+
exp_packet.set_do_not_care_scapy(scapy.Ether, "src")
118+
119+
exp_packet.set_do_not_care_scapy(scapy.IPv6, "version")
120+
exp_packet.set_do_not_care_scapy(scapy.IPv6, "tc")
121+
exp_packet.set_do_not_care_scapy(scapy.IPv6, "fl")
122+
exp_packet.set_do_not_care_scapy(scapy.IPv6, "plen")
123+
exp_packet.set_do_not_care_scapy(scapy.IPv6, "nh")
124+
exp_packet.set_do_not_care_scapy(scapy.IPv6, "hlim")
125+
else:
126+
pkt = testutils.simple_tcp_packet(
127+
pktlen=54,
128+
ip_src=local_addr,
129+
ip_dst=peer_addr,
130+
tcp_dport=BGP_PORT,
131+
tcp_flags="S"
132+
)
133+
exp_packet = Mask(pkt)
134+
exp_packet.set_do_not_care_scapy(scapy.Ether, "dst")
135+
exp_packet.set_do_not_care_scapy(scapy.Ether, "src")
98136

137+
exp_packet.set_do_not_care_scapy(scapy.IP, "version")
138+
exp_packet.set_do_not_care_scapy(scapy.IP, "ihl")
139+
exp_packet.set_do_not_care_scapy(scapy.IP, "tos")
140+
exp_packet.set_do_not_care_scapy(scapy.IP, "len")
141+
exp_packet.set_do_not_care_scapy(scapy.IP, "id")
142+
exp_packet.set_do_not_care_scapy(scapy.IP, "flags")
143+
exp_packet.set_do_not_care_scapy(scapy.IP, "frag")
144+
exp_packet.set_do_not_care_scapy(scapy.IP, "ttl")
145+
exp_packet.set_do_not_care_scapy(scapy.IP, "chksum")
146+
exp_packet.set_do_not_care_scapy(scapy.IP, "options")
147+
148+
# TCP fields (common for both IPv4 and IPv6)
99149
exp_packet.set_do_not_care_scapy(scapy.TCP, "sport")
100150
exp_packet.set_do_not_care_scapy(scapy.TCP, "seq")
101151
exp_packet.set_do_not_care_scapy(scapy.TCP, "ack")
@@ -120,6 +170,13 @@ def test_resolve_via_default_exist(duthost):
120170
"ipv6 nht resolve-via-default not present in global FRR config")
121171

122172

173+
def configure_ipv6_bgpmon_update_source(duthost, asn, local_addr):
174+
duthost.run_vtysh(
175+
"-c 'configure terminal' -c 'router bgp {}' -c 'neighbor BGPMON update-source {}'".format(asn, local_addr),
176+
asic_index='all'
177+
)
178+
179+
123180
def test_bgpmon(dut_with_default_route, localhost, enum_rand_one_frontend_asic_index,
124181
common_setup_teardown, set_timeout_for_bgpmon, ptfadapter, ptfhost):
125182
"""
@@ -128,77 +185,123 @@ def test_bgpmon(dut_with_default_route, localhost, enum_rand_one_frontend_asic_i
128185
duthost = dut_with_default_route
129186
asichost = duthost.asic_instance(enum_rand_one_frontend_asic_index)
130187

131-
def bgpmon_peer_connected(duthost, bgpmon_peer):
188+
def bgpmon_peer_connected(duthost, bgpmon_peer, is_ipv6):
132189
try:
133190
bgp_summary = json.loads(asichost.run_vtysh("-c 'show bgp summary json'")['stdout'])
134-
return bgp_summary['ipv4Unicast']['peers'][bgpmon_peer]["state"] == "Established"
191+
af_key = 'ipv6Unicast' if is_ipv6 else 'ipv4Unicast'
192+
return bgp_summary[af_key]['peers'][bgpmon_peer]["state"] == "Established"
135193
except Exception:
136194
logger.info('Unable to get bgp status')
137195
return False
138196

139-
local_addr, peer_addr, peer_ports, asn = common_setup_teardown
140-
exp_packet = build_syn_pkt(local_addr, peer_addr)
197+
local_addr, peer_addr, peer_ports, asn, is_ipv6_only, router_id = common_setup_teardown
198+
exp_packet = build_syn_pkt(local_addr, peer_addr, is_ipv6=is_ipv6_only)
141199
# Flush dataplane
142200
ptfadapter.dataplane.flush()
143201
# Load bgp monitor config
144202
logger.info("Configured bgpmon and verifying packet on {}".format(peer_ports))
145203
asichost.write_to_config_db(BGPMON_CONFIG_FILE)
204+
if is_ipv6_only:
205+
configure_ipv6_bgpmon_update_source(duthost, asn, local_addr)
146206
# Verify syn packet on ptf
147-
(rcvd_port_index, rcvd_pkt) = testutils.verify_packet_any_port(test=ptfadapter, pkt=exp_packet,
148-
ports=peer_ports, timeout=BGP_CONNECT_TIMEOUT)
207+
(rcvd_port_index, rcvd_pkt) = testutils.verify_packet_any_port(
208+
test=ptfadapter, pkt=exp_packet, ports=peer_ports, timeout=BGP_CONNECT_TIMEOUT
209+
)
149210
# ip as BGMPMON IP , mac as the neighbor mac(mac for default nexthop that was used for sending syn packet) ,
150211
# add the neighbor entry and the default route for dut loopback
151212
ptf_interface = "eth" + str(peer_ports[rcvd_port_index])
152213
res = ptfhost.shell('cat /sys/class/net/{}/address'.format(ptf_interface))
153214
original_mac = res['stdout']
154215
ptfhost.shell("ifconfig %s hw ether %s" % (ptf_interface, scapy.Ether(rcvd_pkt).dst))
155-
ptfhost.shell("ip add add %s dev %s" % (peer_addr + "/24", ptf_interface))
156-
ptfhost.exabgp(name=BGP_MONITOR_NAME,
157-
state="started",
158-
local_ip=peer_addr,
159-
router_id=peer_addr,
160-
peer_ip=local_addr,
161-
local_asn=asn,
162-
peer_asn=asn,
163-
port=BGP_MONITOR_PORT, passive=True)
164-
ptfhost.shell("ip neigh add %s lladdr %s dev %s" % (local_addr, duthost.facts["router_mac"], ptf_interface))
165-
ptfhost.shell("ip route replace %s dev %s" % (local_addr + "/32", ptf_interface))
216+
217+
ip_cmd = "-6" if is_ipv6_only else ""
218+
prefix_len = "/64" if is_ipv6_only else "/24"
219+
route_prefix_len = "/128" if is_ipv6_only else "/32"
220+
221+
ptfhost.shell("ip %s addr add %s dev %s" % (ip_cmd, peer_addr + prefix_len, ptf_interface))
222+
ptfhost.exabgp(
223+
name=BGP_MONITOR_NAME,
224+
state="started",
225+
local_ip=peer_addr,
226+
router_id=router_id,
227+
peer_ip=local_addr,
228+
local_asn=asn,
229+
peer_asn=asn,
230+
port=BGP_MONITOR_PORT,
231+
passive=True
232+
)
233+
ptfhost.shell(
234+
"ip %s neigh add %s lladdr %s dev %s"
235+
% (ip_cmd, local_addr, duthost.facts["router_mac"], ptf_interface)
236+
)
237+
ptfhost.shell(
238+
"ip %s route replace %s dev %s"
239+
% (ip_cmd, local_addr + route_prefix_len, ptf_interface)
240+
)
166241
try:
167-
pytest_assert(wait_tcp_connection(localhost, ptfhost.mgmt_ip, BGP_MONITOR_PORT, timeout_s=60),
168-
"Failed to start bgp monitor session on PTF")
169-
pytest_assert(wait_until(MAX_TIME_FOR_BGPMON, 5, 0, bgpmon_peer_connected, duthost, peer_addr),
170-
"BGPMon Peer connection not established")
242+
pytest_assert(
243+
wait_tcp_connection(localhost, ptfhost.mgmt_ip, BGP_MONITOR_PORT, timeout_s=60),
244+
"Failed to start bgp monitor session on PTF"
245+
)
246+
pytest_assert(
247+
wait_until(
248+
MAX_TIME_FOR_BGPMON, 5, 0, bgpmon_peer_connected, duthost, peer_addr, is_ipv6_only
249+
),
250+
"BGPMon Peer connection not established"
251+
)
171252
finally:
172253
ptfhost.exabgp(name=BGP_MONITOR_NAME, state="absent")
173-
ptfhost.shell("ip route del %s dev %s" % (local_addr + "/32", ptf_interface))
174-
ptfhost.shell("ip neigh del %s lladdr %s dev %s" % (local_addr, duthost.facts["router_mac"], ptf_interface))
175-
ptfhost.shell("ip add del %s dev %s" % (peer_addr + "/24", ptf_interface))
254+
ptfhost.shell(
255+
"ip %s route del %s dev %s"
256+
% (ip_cmd, local_addr + route_prefix_len, ptf_interface)
257+
)
258+
ptfhost.shell(
259+
"ip %s neigh del %s lladdr %s dev %s"
260+
% (ip_cmd, local_addr, duthost.facts["router_mac"], ptf_interface)
261+
)
262+
ptfhost.shell(
263+
"ip %s addr del %s dev %s"
264+
% (ip_cmd, peer_addr + prefix_len, ptf_interface)
265+
)
176266
ptfhost.shell("ifconfig %s hw ether %s" % (ptf_interface, original_mac))
177267

178268

179269
def test_bgpmon_no_resolve_via_default(dut_with_default_route, enum_rand_one_frontend_asic_index,
180270
common_setup_teardown, ptfadapter):
181271
"""
182-
Verify no syn for BGP is sent when 'ip nht resolve-via-default' is disabled.
272+
Verify no syn for BGP is sent when 'ip nht resolve-via-default' or 'ipv6 nht resolve-via-default' is disabled.
183273
"""
184274
duthost = dut_with_default_route
185275
asichost = duthost.asic_instance(enum_rand_one_frontend_asic_index)
186-
local_addr, peer_addr, peer_ports, asn = common_setup_teardown
187-
exp_packet = build_syn_pkt(local_addr, peer_addr)
276+
local_addr, peer_addr, peer_ports, asn, is_ipv6_only, router_id = common_setup_teardown
277+
exp_packet = build_syn_pkt(local_addr, peer_addr, is_ipv6=is_ipv6_only)
278+
279+
ip_cmd = "ipv6" if is_ipv6_only else "ip"
280+
188281
# Load bgp monitor config
189-
logger.info("Configured bgpmon and verifying no packet on {} when resolve-via-default is disabled"
190-
.format(peer_ports))
282+
logger.info(
283+
"Configured bgpmon and verifying no packet on {} when resolve-via-default is disabled".format(peer_ports)
284+
)
191285
try:
192286
# Disable resolve-via-default
193-
duthost.run_vtysh(" -c \"configure terminal\" -c \"no ip nht resolve-via-default\"", asic_index='all')
287+
duthost.run_vtysh(
288+
" -c \"configure terminal\" -c \"no {} nht resolve-via-default\"".format(ip_cmd),
289+
asic_index='all'
290+
)
194291
# Flush dataplane
195292
ptfadapter.dataplane.flush()
196293
asichost.write_to_config_db(BGPMON_CONFIG_FILE)
197294

198295
# Verify no syn packet is received
199-
pytest_assert(0 == testutils.count_matched_packets_all_ports(test=ptfadapter, exp_packet=exp_packet,
200-
ports=peer_ports, timeout=BGP_CONNECT_TIMEOUT),
201-
"Syn packets is captured when resolve-via-default is disabled")
296+
pytest_assert(
297+
0 == testutils.count_matched_packets_all_ports(
298+
test=ptfadapter, exp_packet=exp_packet, ports=peer_ports, timeout=BGP_CONNECT_TIMEOUT
299+
),
300+
"Syn packets is captured when resolve-via-default is disabled"
301+
)
202302
finally:
203303
# Re-enable resolve-via-default
204-
duthost.run_vtysh("-c \"configure terminal\" -c \"ip nht resolve-via-default\"", asic_index='all')
304+
duthost.run_vtysh(
305+
"-c \"configure terminal\" -c \"{} nht resolve-via-default\"".format(ip_cmd),
306+
asic_index='all'
307+
)

tests/common/plugins/conditional_mark/tests_mark_conditions.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -490,11 +490,9 @@ bgp/test_bgp_update_timer.py::test_bgp_update_timer_single_route:
490490

491491
bgp/test_bgpmon.py:
492492
skip:
493-
reason: "Not supported on T2 topology or topology backend
494-
or Skip for IPv6-only topologies, since there are v6 verison of the test"
493+
reason: "Not supported on T2 topology or topology backend"
495494
conditions:
496495
- "'backend' in topo_name or topo_type in ['t2']"
497-
- "'-v6-' in topo_name"
498496

499497
bgp/test_bgpmon_v6.py::test_bgpmon_no_ipv6_resolve_via_default:
500498
skip:

0 commit comments

Comments
 (0)