]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / tests / topotests / bgp_peer_type_multipath_relax / test_bgp_peer-type_multipath-relax.py
1 #!/usr/bin/env python
2 # SPDX-License-Identifier: ISC
3
4 #
5 # Part of NetDEF Topology Tests
6 #
7 # Copyright (c) 2021 Arista Networks, Inc.
8 #
9
10 """
11 test_bgp_peer-type_multipath-relax.py:
12
13 Test the effects of the "bgp bestpath peer-type multipath-relax" command
14
15 - enabling the command allows eBGP, iBGP, and confed routes to be multipath
16 - the choice of best path is not affected
17 - disabling the command removes iBGP/confed routes from multipath
18 - enabling the command does not forgive eBGP routes of the requirement
19 (when enabled) that next hops resolve over connected routes
20 - a mixed-type multipath next hop, when published to zebra, does not
21 require resolving next hops over connected routes
22 - with the command enabled, an all-eBGP multipath next hop still requires
23 resolving next hops over connected routes when published to zebra
24
25 Topology used by the test:
26
27 eBGP +------+ iBGP
28 peer1 ---- | r1 | ---- peer3
29 | |
30 peer2 ---- r2 ---- | | ---- peer4
31 iBGP confed +------+ eBGP
32
33 r2 is present in this topology because ExaBGP does not currently support
34 confederations so we use FRR to advertise the required AS_CONFED_SEQUENCE.
35
36 Routes are advertised from different peers to form interesting multipaths.
37
38 peer1 peer2 peer3 peer4 multipath on r1
39
40 203.0.113.0/30 x x x all 3
41 203.0.113.4/30 x x confed-iBGP
42 203.0.113.8/30 x x eBGP-only
43
44 There is also a BGP-advertised route used only for recursively resolving
45 next hops.
46 """
47
48 import functools
49 import json
50 import os
51 import pytest
52 import sys
53
54 CWD = os.path.dirname(os.path.realpath(__file__))
55 sys.path.append(os.path.join(CWD, "../"))
56
57 # pylint: disable=C0413
58 from lib import topotest
59 from lib.topogen import Topogen, TopoRouter, get_topogen
60 from lib.topolog import logger
61
62 pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
63
64
65 def build_topo(tgen):
66 "Build function"
67
68 # Set up routers
69 tgen.add_router("r1") # DUT
70 tgen.add_router("r2")
71
72 # Set up peers
73 for peern in range(1, 5):
74 peer = tgen.add_exabgp_peer(
75 "peer{}".format(peern),
76 ip="10.0.{}.2/24".format(peern),
77 defaultRoute="via 10.0.{}.1".format(peern),
78 )
79 if peern == 2:
80 tgen.add_link(tgen.gears["r2"], peer)
81 else:
82 tgen.add_link(tgen.gears["r1"], peer)
83 tgen.add_link(tgen.gears["r1"], tgen.gears["r2"])
84
85
86 def setup_module(mod):
87 "Sets up the pytest environment"
88 tgen = Topogen(build_topo, mod.__name__)
89 tgen.start_topology()
90
91 # For all registered routers, load the zebra configuration file
92 for rname, router in tgen.routers().items():
93 router.run("/bin/bash {}/setup_vrfs".format(CWD))
94 router.load_config(
95 TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
96 )
97 router.load_config(
98 TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname))
99 )
100 router.load_config(
101 TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
102 )
103
104 # After loading the configurations, this function loads configured daemons.
105 tgen.start_router()
106
107 # Start up exabgp peers
108 peers = tgen.exabgp_peers()
109 for peer in peers:
110 fifo_in = "/var/run/exabgp_{}.in".format(peer)
111 if os.path.exists(fifo_in):
112 os.remove(fifo_in)
113 os.mkfifo(fifo_in, 0o777)
114 logger.info("Starting ExaBGP on peer {}".format(peer))
115 peer_dir = os.path.join(CWD, peer)
116 env_file = os.path.join(CWD, "exabgp.env")
117 peers[peer].start(peer_dir, env_file)
118
119
120 def teardown_module(mod):
121 "Teardown the pytest environment"
122 tgen = get_topogen()
123
124 # This function tears down the whole topology.
125 tgen.stop_topology()
126
127
128 def test_bgp_peer_type_multipath_relax():
129 tgen = get_topogen()
130
131 # Don't run this test if we have any failure.
132 if tgen.routers_have_failure():
133 pytest.skip(tgen.errors)
134
135 def exabgp_cmd(peer, cmd):
136 pipe = open("/run/exabgp_{}.in".format(peer), "w")
137 with pipe:
138 pipe.write(cmd)
139 pipe.close()
140
141 # Prefixes used in the test
142 prefix1 = "203.0.113.0/30"
143 prefix2 = "203.0.113.4/30"
144 prefix3 = "203.0.113.8/30"
145 # Next hops used for iBGP/confed routes
146 resolved_nh1 = "198.51.100.1"
147 resolved_nh2 = "198.51.100.2"
148 # BGP route used for recursive resolution
149 bgp_resolving_prefix = "198.51.100.0/24"
150 # Next hop that will require non-connected recursive resolution
151 ebgp_resolved_nh = "198.51.100.10"
152
153 # Send a non-connected route to resolve others
154 exabgp_cmd(
155 "peer3", "announce route {} next-hop self\n".format(bgp_resolving_prefix)
156 )
157 router = tgen.gears["r1"]
158
159 # It seems that if you write to the exabgp socket too quickly in
160 # succession, requests get lost. So verify prefix1 now instead of
161 # after all the prefixes are advertised.
162 logger.info("Create and verify mixed-type multipaths")
163 exabgp_cmd(
164 "peer1",
165 "announce route {} next-hop {} as-path [ 64499 ]\n".format(
166 prefix1, resolved_nh1
167 ),
168 )
169 exabgp_cmd(
170 "peer2",
171 "announce route {} next-hop {} as-path [ 64499 ]\n".format(
172 prefix1, resolved_nh2
173 ),
174 )
175 exabgp_cmd("peer4", "announce route {} next-hop self\n".format(prefix1))
176 reffile = os.path.join(CWD, "r1/prefix1.json")
177 expected = json.loads(open(reffile).read())
178 test_func = functools.partial(
179 topotest.router_json_cmp,
180 router,
181 "show ip bgp {} json".format(prefix1),
182 expected,
183 )
184 _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
185 assertMsg = "Mixed-type multipath not found"
186 assert res is None, assertMsg
187
188 logger.info("Create and verify eBGP and iBGP+confed multipaths")
189 exabgp_cmd(
190 "peer1",
191 "announce route {} next-hop {} as-path [ 64499 ]\n".format(
192 prefix2, resolved_nh1
193 ),
194 )
195 exabgp_cmd(
196 "peer2",
197 "announce route {} next-hop {} as-path [ 64499 ]\n".format(
198 prefix2, resolved_nh2
199 ),
200 )
201 exabgp_cmd("peer3", "announce route {} next-hop self".format(prefix3))
202 exabgp_cmd("peer4", "announce route {} next-hop self".format(prefix3))
203 reffile = os.path.join(CWD, "r1/multipath.json")
204 expected = json.loads(open(reffile).read())
205 test_func = functools.partial(
206 topotest.router_json_cmp, router, "show ip bgp json", expected
207 )
208 _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
209 assertMsg = "Not all expected multipaths found"
210 assert res is None, assertMsg
211
212 logger.info("Toggle peer-type multipath-relax and verify the changes")
213 router.vtysh_cmd(
214 "conf\n router bgp 64510\n no bgp bestpath peer-type multipath-relax\n"
215 )
216 # This file verifies "multipath" is not set
217 reffile = os.path.join(CWD, "r1/not-multipath.json")
218 expected = json.loads(open(reffile).read())
219 test_func = functools.partial(
220 topotest.router_json_cmp, router, "show ip bgp json", expected
221 )
222 _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
223 assertMsg = "Disabling peer-type multipath-relax did not take effect"
224 assert res is None, assertMsg
225
226 router.vtysh_cmd(
227 "conf\n router bgp 64510\n bgp bestpath peer-type multipath-relax\n"
228 )
229 reffile = os.path.join(CWD, "r1/multipath.json")
230 expected = json.loads(open(reffile).read())
231 test_func = functools.partial(
232 topotest.router_json_cmp, router, "show ip bgp json", expected
233 )
234 _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
235 assertMsg = "Reenabling peer-type multipath-relax did not take effect"
236 assert res is None, assertMsg
237
238 logger.info("Check recursive resolution of eBGP next hops is not affected")
239 # eBGP next hop resolution rejects recursively resolved next hops by
240 # default, even with peer-type multipath-relax
241 exabgp_cmd(
242 "peer4", "announce route {} next-hop {}\n".format(prefix3, ebgp_resolved_nh)
243 )
244 reffile = os.path.join(CWD, "r1/prefix3-no-recursive.json")
245 expected = json.loads(open(reffile).read())
246 test_func = functools.partial(
247 topotest.router_json_cmp,
248 router,
249 "show ip bgp {} json".format(prefix3),
250 expected,
251 )
252 _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
253 assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix3)
254 assert res is None, assertMsg
255
256 exabgp_cmd(
257 "peer4", "announce route {} next-hop {}\n".format(prefix1, ebgp_resolved_nh)
258 )
259 reffile = os.path.join(CWD, "r1/prefix1-no-recursive.json")
260 expected = json.loads(open(reffile).read())
261 test_func = functools.partial(
262 topotest.router_json_cmp,
263 router,
264 "show ip bgp {} json".format(prefix1),
265 expected,
266 )
267 _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
268 assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix1)
269 assert res is None, assertMsg
270
271 # When other config allows recursively resolved eBGP next hops,
272 # such next hops in all-eBGP multipaths should be valid
273 router.vtysh_cmd("conf\n router bgp 64510\n neighbor 10.0.4.2 ebgp-multihop\n")
274 reffile = os.path.join(CWD, "r1/prefix3-recursive.json")
275 expected = json.loads(open(reffile).read())
276 test_func = functools.partial(
277 topotest.router_json_cmp,
278 router,
279 "show ip bgp {} json".format(prefix3),
280 expected,
281 )
282 _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
283 assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix3)
284 assert res is None, assertMsg
285
286 reffile = os.path.join(CWD, "r1/prefix1-recursive.json")
287 expected = json.loads(open(reffile).read())
288 test_func = functools.partial(
289 topotest.router_json_cmp,
290 router,
291 "show ip bgp {} json".format(prefix1),
292 expected,
293 )
294 _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
295 assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix1)
296 assert res is None, assertMsg
297
298 logger.info("Check mixed-type multipath next hop recursive resolution in FIB")
299 # There are now two eBGP-learned routes with a recursively resolved next;
300 # hop; one is all-eBGP multipath, and the other is iBGP/eBGP/
301 # confed-external. The peer-type multipath-relax feature only enables
302 # recursive resolution in FIB if any next hop is iBGP/confed-learned. The
303 # all-eBGP multipath will have only one valid next hop in the FIB.
304 reffile = os.path.join(CWD, "r1/prefix3-ip-route.json")
305 expected = json.loads(open(reffile).read())
306 test_func = functools.partial(
307 topotest.router_json_cmp,
308 router,
309 "show ip route {} json".format(prefix3),
310 expected,
311 )
312 _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
313 assertMsg = "FIB next hops mismatch for all-eBGP multipath"
314 assert res is None, assertMsg
315
316 # check confed-external enables recursively resolved next hops by itself
317 exabgp_cmd(
318 "peer1",
319 "withdraw route {} next-hop {} as-path [ 64499 ]\n".format(
320 prefix1, resolved_nh1
321 ),
322 )
323 reffile = os.path.join(CWD, "r1/prefix1-eBGP-confed.json")
324 expected = json.loads(open(reffile).read())
325 test_func = functools.partial(
326 topotest.router_json_cmp,
327 router,
328 "show ip route {} json".format(prefix1),
329 expected,
330 )
331 _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
332 assertMsg = "FIB next hops mismatch for eBGP+confed-external multipath"
333 assert res is None, assertMsg
334
335 # check iBGP by itself
336 exabgp_cmd(
337 "peer1",
338 "announce route {} next-hop {} as-path [ 64499 ]\n".format(
339 prefix1, resolved_nh1
340 ),
341 )
342 exabgp_cmd(
343 "peer2",
344 "withdraw route {} next-hop {} as-path [ 64499 ]\n".format(
345 prefix1, resolved_nh2
346 ),
347 )
348 reffile = os.path.join(CWD, "r1/prefix1-eBGP-iBGP.json")
349 expected = json.loads(open(reffile).read())
350 test_func = functools.partial(
351 topotest.router_json_cmp,
352 router,
353 "show ip route {} json".format(prefix1),
354 expected,
355 )
356 _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
357 assertMsg = "FIB next hops mismatch for eBGP+iBGP multipath"
358 assert res is None, assertMsg
359
360
361 def test_memory_leak():
362 "Run the memory leak test and report results."
363 tgen = get_topogen()
364 if not tgen.is_memleak_enabled():
365 pytest.skip("Memory leak test/report is disabled")
366
367 tgen.report_memory_leaks()
368
369
370 if __name__ == "__main__":
371 args = ["-s"] + sys.argv[1:]
372 sys.exit(pytest.main(args))