]>
git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/bgp_remove_private_as/test_bgp_remove_private_as.py
4 # test_bgp_remove_private_as.py
6 # Copyright (C) 2022 NVIDIA Corporation
9 # Permission to use, copy, modify, and/or distribute this software
10 # for any purpose with or without fee is hereby granted, provided
11 # that the above copyright notice and this permission notice appear
14 # THE SOFTWARE IS PROVIDED "AS IS" AND NVIDIA DISCLAIMS ALL WARRANTIES
15 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NVIDIA BE LIABLE FOR
17 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
18 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
19 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
20 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
25 test_bgp_remove_private_as.py tests the following conditions:
26 1. "remove-private-AS" strips all private ASNs from the AS-path unless:
27 a. the ASN belongs to the peer
28 b. the ASN is both local + private
29 c. the AS-path is not completely comprised of public ASNs
30 2. "remove-private-AS all" strips all private ASNs from the AS-path unless:
31 a. the ASN belongs to the peer
32 b. the ASN is both local + private
33 3. "remove-private-AS replace-AS" swaps private ASNs with local ASN unless:
34 a. the ASN belongs to the peer
35 b. the AS-path is not completely comprised of public ASNs
36 4. "remove-private-AS all replace-AS" swaps private ASNs with local ASN unless:
37 a. the ASN belongs to the peer
39 All conditions are tested while the local ASN is private.
40 All conditions are tested while the local ASN is public.
41 All conditions are tested against an eBGP peer in a private ASN.
42 All conditions are tested against an eBGP peer in a public ASN.
51 CWD
= os
.path
.dirname(os
.path
.realpath(__file__
))
52 sys
.path
.append(os
.path
.join(CWD
, "../"))
54 # pylint: disable=C0413
55 from lib
import topotest
56 from lib
.topogen
import Topogen
, TopoRouter
, get_topogen
57 from functools
import partial
59 pytestmark
= [pytest
.mark
.bgpd
]
64 We are effectively creating two hub/spoke topologies with r2 and r5 acting
65 as hubs. "remove-private-AS" will be configured on r2/r5 towards r3/r4, and
66 r1 will act as the originator of the test prefixes. AS-Path validation will
70 +-----+ +-----+ +-----+
71 | r1 |----->|r2/r5|---->| r3 |
72 +-----+ +-----+ +-----+
85 for routern
in range(1, 6):
86 tgen
.add_router(f
"r{routern}")
88 #######################
90 #######################
91 switch
= tgen
.add_switch("s1")
92 switch
.add_link(tgen
.gears
["r1"])
93 switch
.add_link(tgen
.gears
["r2"])
95 switch
= tgen
.add_switch("s2")
96 switch
.add_link(tgen
.gears
["r2"])
97 switch
.add_link(tgen
.gears
["r3"])
99 switch
= tgen
.add_switch("s3")
100 switch
.add_link(tgen
.gears
["r2"])
101 switch
.add_link(tgen
.gears
["r4"])
103 #######################
105 #######################
106 switch
= tgen
.add_switch("s4")
107 switch
.add_link(tgen
.gears
["r1"])
108 switch
.add_link(tgen
.gears
["r5"])
110 switch
= tgen
.add_switch("s5")
111 switch
.add_link(tgen
.gears
["r3"])
112 switch
.add_link(tgen
.gears
["r5"])
114 switch
= tgen
.add_switch("s6")
115 switch
.add_link(tgen
.gears
["r4"])
116 switch
.add_link(tgen
.gears
["r5"])
119 def setup_module(mod
):
120 tgen
= Topogen(build_topo
, mod
.__name
__)
121 tgen
.start_topology()
123 router_list
= tgen
.routers()
125 for i
, (rname
, router
) in enumerate(router_list
.items(), 1):
127 TopoRouter
.RD_ZEBRA
, os
.path
.join(CWD
, f
"{rname}/zebra.conf")
129 router
.load_config(TopoRouter
.RD_BGP
, os
.path
.join(CWD
, f
"{rname}/bgpd.conf"))
134 def teardown_module(mod
):
139 def test_bgp_remove_private_as():
142 if tgen
.routers_have_failure():
143 pytest
.skip(tgen
.errors
)
154 # r2/r5 are setup with remove-private-AS configs.
155 tx_routers
= ["r2", "r5"]
157 # We will validate the paths received by r3/r4.
158 rx_routers
= ["r3", "r4"]
160 # Config options for remove-private-AS
163 "remove-private-AS all",
164 "remove-private-AS replace-AS",
165 "remove-private-AS all replace-AS",
168 # Expected as-paths for each test route from the perspective of each
169 # rx_router, accounting for each variation of remove-private-AS.
183 "remove-private-AS": {
185 "100.64.0.0/32": "65002",
186 "100.64.0.1/32": "65002 65003 65003",
187 "100.64.0.2/32": "65002 65001 4200000000 1000 4200000001 2000 4200000002",
188 "100.64.0.3/32": "65002 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
189 "100.64.0.4/32": "65002 65001 1000 2000 2000 3000",
192 "100.64.0.0/32": "5555",
193 "100.64.0.1/32": "5555 65003 65003",
194 "100.64.0.2/32": "5555 65001 4200000000 1000 4200000001 2000 4200000002",
195 "100.64.0.3/32": "5555 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
196 "100.64.0.4/32": "5555 65001 1000 2000 2000 3000",
199 "remove-private-AS all": {
201 "100.64.0.0/32": "65002",
202 "100.64.0.1/32": "65002 65003 65003",
203 "100.64.0.2/32": "65002 1000 2000",
204 "100.64.0.3/32": "65002 65003 1000 2000 65003",
205 "100.64.0.4/32": "65002 1000 2000 2000 3000",
208 "100.64.0.0/32": "5555",
209 "100.64.0.1/32": "5555 65003 65003",
210 "100.64.0.2/32": "5555 1000 2000",
211 "100.64.0.3/32": "5555 65003 1000 2000 65003",
212 "100.64.0.4/32": "5555 1000 2000 2000 3000",
215 "remove-private-AS replace-AS": {
217 "100.64.0.0/32": "65002 65002 65002 65002 65002",
218 "100.64.0.1/32": "65002 65002 65003 65002 65002 65002 65003",
219 "100.64.0.2/32": "65002 65001 4200000000 1000 4200000001 2000 4200000002",
220 "100.64.0.3/32": "65002 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
221 "100.64.0.4/32": "65002 65001 1000 2000 2000 3000",
224 "100.64.0.0/32": "5555 5555 5555 5555 5555",
225 "100.64.0.1/32": "5555 5555 65003 5555 5555 5555 65003",
226 "100.64.0.2/32": "5555 65001 4200000000 1000 4200000001 2000 4200000002",
227 "100.64.0.3/32": "5555 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
228 "100.64.0.4/32": "5555 65001 1000 2000 2000 3000",
231 "remove-private-AS all replace-AS": {
233 "100.64.0.0/32": "65002 65002 65002 65002 65002",
234 "100.64.0.1/32": "65002 65002 65003 65002 65002 65002 65003",
235 "100.64.0.2/32": "65002 65002 65002 1000 65002 2000 65002",
236 "100.64.0.3/32": "65002 65002 65003 65002 1000 65002 2000 65002 65003",
237 "100.64.0.4/32": "65002 65002 1000 2000 2000 3000",
240 "100.64.0.0/32": "5555 5555 5555 5555 5555",
241 "100.64.0.1/32": "5555 5555 65003 5555 5555 5555 65003",
242 "100.64.0.2/32": "5555 5555 5555 1000 5555 2000 5555",
243 "100.64.0.3/32": "5555 5555 65003 5555 1000 5555 2000 5555 65003",
244 "100.64.0.4/32": "5555 5555 1000 2000 2000 3000",
249 "remove-private-AS": {
251 "100.64.0.0/32": "65002",
252 "100.64.0.1/32": "65002",
253 "100.64.0.2/32": "65002 65001 4200000000 1000 4200000001 2000 4200000002",
254 "100.64.0.3/32": "65002 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
255 "100.64.0.4/32": "65002 65001 1000 2000 2000 3000",
258 "100.64.0.0/32": "5555",
259 "100.64.0.1/32": "5555",
260 "100.64.0.2/32": "5555 65001 4200000000 1000 4200000001 2000 4200000002",
261 "100.64.0.3/32": "5555 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
262 "100.64.0.4/32": "5555 65001 1000 2000 2000 3000",
265 "remove-private-AS all": {
267 "100.64.0.0/32": "65002",
268 "100.64.0.1/32": "65002",
269 "100.64.0.2/32": "65002 1000 2000",
270 "100.64.0.3/32": "65002 1000 2000",
271 "100.64.0.4/32": "65002 1000 2000 2000 3000",
274 "100.64.0.0/32": "5555",
275 "100.64.0.1/32": "5555",
276 "100.64.0.2/32": "5555 1000 2000",
277 "100.64.0.3/32": "5555 1000 2000",
278 "100.64.0.4/32": "5555 1000 2000 2000 3000",
281 "remove-private-AS replace-AS": {
283 "100.64.0.0/32": "65002 65002 65002 65002 65002",
284 "100.64.0.1/32": "65002 65002 65002 65002 65002 65002 65002",
285 "100.64.0.2/32": "65002 65001 4200000000 1000 4200000001 2000 4200000002",
286 "100.64.0.3/32": "65002 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
287 "100.64.0.4/32": "65002 65001 1000 2000 2000 3000",
290 "100.64.0.0/32": "5555 5555 5555 5555 5555",
291 "100.64.0.1/32": "5555 5555 5555 5555 5555 5555 5555",
292 "100.64.0.2/32": "5555 65001 4200000000 1000 4200000001 2000 4200000002",
293 "100.64.0.3/32": "5555 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
294 "100.64.0.4/32": "5555 65001 1000 2000 2000 3000",
297 "remove-private-AS all replace-AS": {
299 "100.64.0.0/32": "65002 65002 65002 65002 65002",
300 "100.64.0.1/32": "65002 65002 65002 65002 65002 65002 65002",
301 "100.64.0.2/32": "65002 65002 65002 1000 65002 2000 65002",
302 "100.64.0.3/32": "65002 65002 65002 65002 1000 65002 2000 65002 65002",
303 "100.64.0.4/32": "65002 65002 1000 2000 2000 3000",
306 "100.64.0.0/32": "5555 5555 5555 5555 5555",
307 "100.64.0.1/32": "5555 5555 5555 5555 5555 5555 5555",
308 "100.64.0.2/32": "5555 5555 5555 1000 5555 2000 5555",
309 "100.64.0.3/32": "5555 5555 5555 5555 1000 5555 2000 5555 5555",
310 "100.64.0.4/32": "5555 5555 1000 2000 2000 3000",
316 # Simple lookup of remote peer ip by routers in session (local --> remote).
325 "r1": {"r2": "203.0.113.1", "r5": "203.0.113.3"},
326 "r2": {"r1": "203.0.113.0", "r3": "203.0.113.4", "r4": "203.0.113.8"},
327 "r3": {"r2": "203.0.113.5", "r5": "203.0.113.7"},
328 "r4": {"r2": "203.0.113.9", "r5": "203.0.113.11"},
329 "r5": {"r1": "203.0.113.2", "r3": "203.0.113.6", "r4": "203.0.113.10"},
333 """Return True if all configured peers are Established."""
334 for router
in tx_routers
:
336 tgen
.gears
[router
].vtysh_cmd("show ip bgp summary json")
338 numPeers
= output
["ipv4Unicast"]["totalPeers"]
340 for peer_data
in output
["ipv4Unicast"]["peers"].values():
341 if peer_data
["state"] == "Established":
343 if numConverged
== numPeers
:
347 def __bgp_converged():
348 """Return True if all prefixes have been received from tx_routers."""
349 for router
in rx_routers
:
351 tgen
.gears
[router
].vtysh_cmd("show ip bgp summary json")
353 numPeers
= output
["ipv4Unicast"]["totalPeers"]
355 for peer
in tx_routers
:
356 peer_ip
= peer_to_ip
[router
][peer
]
357 numPrefixes
= output
["ipv4Unicast"]["peers"][peer_ip
]["pfxRcd"]
358 if numPrefixes
== len(prefixes
):
360 if numConverged
== numPeers
:
364 def _get_pfx_path_from_nh(router
, prefix
, nh
):
365 """Return as-path for a specific route + path."""
366 output
= json
.loads(tgen
.gears
[router
].vtysh_cmd(f
"show ip bgp {prefix} json"))
367 for path
in output
[prefix
]:
368 if path
["nexthops"]["ip"] == nh
:
369 return path
["aspath"]["string"]
371 def _routers_up(tx_rtrs
, rx_rtrs
):
372 """Ensure all BGP sessions are up and all routes are installed."""
373 # all sessions go through tx_routers, so ensure all their peers are up
374 test_func
= partial(__bgp_up
)
375 _
, result
= topotest
.run_and_expect(test_func
, True, count
=60, wait
=0.5)
376 assert result
== True, "Not all peers in Established state!"
378 # ensure correct number of routes are installed
379 test_func
= partial(__bgp_converged
)
380 _
, result
= topotest
.run_and_expect(test_func
, True, count
=60, wait
=0.5)
381 assert result
== True, "Not all routes installed in time!"
383 def _change_remove_type(new_type
, op
):
384 """Update config with next remove-private-AS config variant."""
385 no
= "no" if op
== "del" else ""
386 for tr
in tx_routers
:
387 for rr
in rx_routers
:
388 p_ip
= peer_to_ip
[tr
][rr
]
389 tgen
.gears
[tr
].vtysh_multicmd(
393 address-family ipv4 unicast
394 {no} neighbor {p_ip} {new_type}
398 def _validate_paths(remove_type
):
399 """Compare actual AS-Path against expected AS-Path."""
400 for rtr
in rx_routers
:
401 for peer
in tx_routers
:
402 p_ip
= peer_to_ip
[rtr
][peer
]
403 adj_rib_in
= json
.loads(
404 tgen
.gears
[rtr
].vtysh_cmd(
405 f
"show ip bgp neighbor {p_ip} received-routes json"
409 good_path
= expected_paths
[rtr
][remove_type
][peer
][pfx
]
410 real_path
= adj_rib_in
["receivedRoutes"][pfx
]["path"]
412 f
"{rtr} received incorrect AS-Path from {peer} "
413 f
'({p_ip}) for {pfx}. remove_type: "{remove_type}"'
415 assert real_path
== good_path
, msg
417 #######################
419 #######################
421 # make sure all peers come up and exchange routes
422 _routers_up(tx_routers
, rx_routers
)
424 # test each variation of remove-private-AS
425 for rmv_type
in remove_types
:
426 _change_remove_type(rmv_type
, "add")
427 _validate_paths(rmv_type
)
428 # each variation sets a separate peer flag in bgpd. we need to clear
429 # the old flag after each iteration so we only test the flags we expect.
430 _change_remove_type(rmv_type
, "del")
435 if __name__
== "__main__":
436 args
= ["-s"] + sys
.argv
[1:]
437 sys
.exit(pytest
.main(args
))