66import ptf .packet as scapy
77from ptf .mask import Mask
88import 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
1212from tests .common .helpers .assertions import pytest_assert
1313from tests .common .utilities import wait_until
1414from tests .common .utilities import wait_tcp_connection
15+ from tests .common .utilities import is_ipv6_only_topology
1516from bgp_helpers import BGPMON_TEMPLATE_FILE , BGPMON_CONFIG_FILE , BGP_MONITOR_NAME , BGP_MONITOR_PORT
1617
1718pytestmark = [
2223BGP_CONNECT_TIMEOUT = 121
2324MAX_TIME_FOR_BGPMON = 180
2425ZERO_ADDR = r'0.0.0.0/0'
26+ ZERO_ADDR_V6 = r'::/0'
2527logger = 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+
123180def 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
179269def 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+ )
0 commit comments