]>
Commit | Line | Data |
---|---|---|
3a94d672 | 1 | #!/usr/bin/env python |
acddc0ed | 2 | # SPDX-License-Identifier: ISC |
3a94d672 CS |
3 | |
4 | # Copyright (c) 2022, University of Rome Tor Vergata | |
5 | # Authored by Carmine Scarpitta <carmine.scarpitta@uniroma2.it> | |
6 | # | |
3a94d672 CS |
7 | |
8 | import os | |
9 | import re | |
10 | import sys | |
11 | import json | |
12 | import functools | |
13 | import pytest | |
14 | ||
15 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
16 | sys.path.append(os.path.join(CWD, "../")) | |
17 | ||
18 | # pylint: disable=C0413 | |
19 | # Import topogen and topotest helpers | |
20 | from lib import topotest | |
21 | from lib.topogen import Topogen, TopoRouter, get_topogen | |
22 | from lib.topolog import logger | |
23 | from lib.common_config import required_linux_kernel_version | |
24 | ||
25 | pytestmark = [pytest.mark.bgpd] | |
26 | ||
27 | ||
28 | def build_topo(tgen): | |
29 | tgen.add_router("r1") | |
30 | tgen.add_router("r2") | |
31 | tgen.add_router("ce1") | |
32 | tgen.add_router("ce2") | |
33 | tgen.add_router("ce3") | |
34 | tgen.add_router("ce4") | |
35 | tgen.add_router("ce5") | |
36 | tgen.add_router("ce6") | |
37 | ||
38 | tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0") | |
39 | tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "eth0", "eth1") | |
40 | tgen.add_link(tgen.gears["ce2"], tgen.gears["r2"], "eth0", "eth1") | |
41 | tgen.add_link(tgen.gears["ce3"], tgen.gears["r1"], "eth0", "eth2") | |
42 | tgen.add_link(tgen.gears["ce4"], tgen.gears["r2"], "eth0", "eth2") | |
43 | tgen.add_link(tgen.gears["ce5"], tgen.gears["r1"], "eth0", "eth3") | |
44 | tgen.add_link(tgen.gears["ce6"], tgen.gears["r2"], "eth0", "eth3") | |
45 | ||
46 | ||
47 | def setup_module(mod): | |
48 | result = required_linux_kernel_version("5.14") | |
49 | if result is not True: | |
50 | pytest.skip("Kernel requirements are not met") | |
51 | ||
52 | tgen = Topogen(build_topo, mod.__name__) | |
53 | tgen.start_topology() | |
54 | for rname, router in tgen.routers().items(): | |
55 | router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname)) | |
625fe20b RS |
56 | router.load_config( |
57 | TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) | |
58 | ) | |
59 | router.load_config( | |
60 | TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) | |
61 | ) | |
3a94d672 CS |
62 | |
63 | tgen.gears["r1"].run("sysctl net.vrf.strict_mode=1") | |
64 | tgen.gears["r1"].run("ip link add vrf10 type vrf table 10") | |
65 | tgen.gears["r1"].run("ip link set vrf10 up") | |
66 | tgen.gears["r1"].run("ip link add vrf20 type vrf table 20") | |
67 | tgen.gears["r1"].run("ip link set vrf20 up") | |
68 | tgen.gears["r1"].run("ip link set eth1 master vrf10") | |
69 | tgen.gears["r1"].run("ip link set eth2 master vrf10") | |
70 | tgen.gears["r1"].run("ip link set eth3 master vrf20") | |
71 | ||
72 | tgen.gears["r2"].run("sysctl net.vrf.strict_mode=1") | |
73 | tgen.gears["r2"].run("ip link add vrf10 type vrf table 10") | |
74 | tgen.gears["r2"].run("ip link set vrf10 up") | |
75 | tgen.gears["r2"].run("ip link add vrf20 type vrf table 20") | |
76 | tgen.gears["r2"].run("ip link set vrf20 up") | |
77 | tgen.gears["r2"].run("ip link set eth1 master vrf10") | |
78 | tgen.gears["r2"].run("ip link set eth2 master vrf20") | |
79 | tgen.gears["r2"].run("ip link set eth3 master vrf20") | |
80 | tgen.start_router() | |
81 | ||
82 | ||
83 | def teardown_module(mod): | |
84 | tgen = get_topogen() | |
85 | tgen.stop_topology() | |
86 | ||
87 | ||
88 | def open_json_file(filename): | |
89 | try: | |
90 | with open(filename, "r") as f: | |
91 | return json.load(f) | |
92 | except IOError: | |
93 | assert False, "Could not read file {}".format(filename) | |
94 | ||
95 | ||
96 | def check_ping4(name, dest_addr, expect_connected): | |
97 | def _check(name, dest_addr, match): | |
98 | tgen = get_topogen() | |
99 | output = tgen.gears[name].run("ping {} -c 1 -w 1".format(dest_addr)) | |
100 | logger.info(output) | |
22c10bbd DS |
101 | if match not in output: |
102 | return "ping fail" | |
3a94d672 | 103 | |
625fe20b | 104 | match = ", {} packet loss".format("0%" if expect_connected else "100%") |
3a94d672 CS |
105 | logger.info("[+] check {} {} {}".format(name, dest_addr, match)) |
106 | tgen = get_topogen() | |
107 | func = functools.partial(_check, name, dest_addr, match) | |
108 | success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) | |
109 | assert result is None, "Failed" | |
110 | ||
111 | ||
112 | def check_ping6(name, dest_addr, expect_connected): | |
113 | def _check(name, dest_addr, match): | |
114 | tgen = get_topogen() | |
115 | output = tgen.gears[name].run("ping6 {} -c 1 -w 1".format(dest_addr)) | |
116 | logger.info(output) | |
117 | if match not in output: | |
118 | return "ping fail" | |
119 | ||
120 | match = "{} packet loss".format("0%" if expect_connected else "100%") | |
121 | logger.info("[+] check {} {} {}".format(name, dest_addr, match)) | |
122 | tgen = get_topogen() | |
123 | func = functools.partial(_check, name, dest_addr, match) | |
124 | success, result = topotest.run_and_expect(func, None, count=10, wait=1) | |
125 | assert result is None, "Failed" | |
126 | ||
127 | ||
128 | def check_rib(name, cmd, expected_file): | |
129 | def _check(name, dest_addr, match): | |
130 | logger.info("polling") | |
131 | tgen = get_topogen() | |
132 | router = tgen.gears[name] | |
133 | output = json.loads(router.vtysh_cmd(cmd)) | |
134 | expected = open_json_file("{}/{}".format(CWD, expected_file)) | |
135 | return topotest.json_cmp(output, expected) | |
136 | ||
625fe20b | 137 | logger.info('[+] check {} "{}" {}'.format(name, cmd, expected_file)) |
3a94d672 CS |
138 | tgen = get_topogen() |
139 | func = functools.partial(_check, name, cmd, expected_file) | |
140 | success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) | |
141 | assert result is None, "Failed" | |
142 | ||
143 | ||
144 | def test_rib(): | |
145 | check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib.json") | |
146 | check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib.json") | |
147 | check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10v4_rib.json") | |
148 | check_rib("r1", "show ip route vrf vrf20 json", "r1/vrf20v4_rib.json") | |
149 | check_rib("r2", "show ip route vrf vrf10 json", "r2/vrf10v4_rib.json") | |
150 | check_rib("r2", "show ip route vrf vrf20 json", "r2/vrf20v4_rib.json") | |
151 | check_rib("ce1", "show ip route json", "ce1/ip_rib.json") | |
152 | check_rib("ce2", "show ip route json", "ce2/ip_rib.json") | |
153 | check_rib("ce3", "show ip route json", "ce3/ip_rib.json") | |
154 | check_rib("ce4", "show ip route json", "ce4/ip_rib.json") | |
155 | check_rib("ce5", "show ip route json", "ce5/ip_rib.json") | |
156 | check_rib("ce6", "show ip route json", "ce6/ip_rib.json") | |
157 | ||
158 | check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json") | |
159 | check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json") | |
160 | check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10v6_rib.json") | |
161 | check_rib("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20v6_rib.json") | |
162 | check_rib("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10v6_rib.json") | |
163 | check_rib("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20v6_rib.json") | |
164 | check_rib("ce1", "show ipv6 route json", "ce1/ipv6_rib.json") | |
165 | check_rib("ce2", "show ipv6 route json", "ce2/ipv6_rib.json") | |
166 | check_rib("ce3", "show ipv6 route json", "ce3/ipv6_rib.json") | |
167 | check_rib("ce4", "show ipv6 route json", "ce4/ipv6_rib.json") | |
168 | check_rib("ce5", "show ipv6 route json", "ce5/ipv6_rib.json") | |
169 | check_rib("ce6", "show ipv6 route json", "ce6/ipv6_rib.json") | |
170 | ||
171 | ||
172 | def test_ping(): | |
173 | check_ping4("ce1", "192.168.2.2", True) | |
174 | check_ping4("ce1", "192.168.3.2", True) | |
175 | check_ping4("ce1", "192.168.4.2", False) | |
176 | check_ping4("ce1", "192.168.5.2", False) | |
177 | check_ping4("ce1", "192.168.6.2", False) | |
178 | check_ping4("ce4", "192.168.1.2", False) | |
179 | check_ping4("ce4", "192.168.2.2", False) | |
180 | check_ping4("ce4", "192.168.3.2", False) | |
181 | check_ping4("ce4", "192.168.5.2", True) | |
182 | check_ping4("ce4", "192.168.6.2", True) | |
183 | ||
184 | check_ping6("ce1", "2001:2::2", True) | |
185 | check_ping6("ce1", "2001:3::2", True) | |
186 | check_ping6("ce1", "2001:4::2", False) | |
187 | check_ping6("ce1", "2001:5::2", False) | |
188 | check_ping6("ce1", "2001:6::2", False) | |
189 | check_ping6("ce4", "2001:1::2", False) | |
190 | check_ping6("ce4", "2001:2::2", False) | |
191 | check_ping6("ce4", "2001:3::2", False) | |
192 | check_ping6("ce4", "2001:5::2", True) | |
193 | check_ping6("ce4", "2001:6::2", True) | |
194 | ||
195 | ||
b3c5e11c CS |
196 | def test_bgp_sid_vpn_export_disable(): |
197 | check_ping4("ce1", "192.168.2.2", True) | |
198 | check_ping6("ce1", "2001:2::2", True) | |
199 | get_topogen().gears["r1"].vtysh_cmd( | |
200 | """ | |
201 | configure terminal | |
202 | router bgp 1 vrf vrf10 | |
203 | segment-routing srv6 | |
204 | no sid vpn per-vrf export | |
205 | """ | |
206 | ) | |
625fe20b RS |
207 | check_rib( |
208 | "r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_sid_vpn_export_disabled.json" | |
209 | ) | |
210 | check_rib( | |
211 | "r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_sid_vpn_export_disabled.json" | |
212 | ) | |
213 | check_rib( | |
214 | "r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_sid_vpn_export_disabled.json" | |
215 | ) | |
216 | check_rib( | |
217 | "r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_sid_vpn_export_disabled.json" | |
218 | ) | |
b3c5e11c CS |
219 | check_ping4("ce1", "192.168.2.2", False) |
220 | check_ping6("ce1", "2001:2::2", False) | |
221 | ||
222 | ||
223 | def test_bgp_sid_vpn_export_reenable(): | |
224 | check_ping4("ce1", "192.168.2.2", False) | |
225 | check_ping6("ce1", "2001:2::2", False) | |
226 | get_topogen().gears["r1"].vtysh_cmd( | |
227 | """ | |
228 | configure terminal | |
229 | router bgp 1 vrf vrf10 | |
230 | segment-routing srv6 | |
231 | sid vpn per-vrf export auto | |
232 | """ | |
233 | ) | |
625fe20b RS |
234 | check_rib( |
235 | "r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_sid_vpn_export_reenabled.json" | |
236 | ) | |
237 | check_rib( | |
238 | "r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_sid_vpn_export_reenabled.json" | |
239 | ) | |
240 | check_rib( | |
241 | "r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_sid_vpn_export_reenabled.json" | |
242 | ) | |
243 | check_rib( | |
244 | "r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_sid_vpn_export_reenabled.json" | |
245 | ) | |
b3c5e11c CS |
246 | check_ping4("ce1", "192.168.2.2", True) |
247 | check_ping6("ce1", "2001:2::2", True) | |
248 | ||
249 | ||
3a94d672 CS |
250 | def test_locator_delete(): |
251 | check_ping4("ce1", "192.168.2.2", True) | |
252 | check_ping6("ce1", "2001:2::2", True) | |
253 | get_topogen().gears["r1"].vtysh_cmd( | |
254 | """ | |
255 | configure terminal | |
256 | segment-routing | |
257 | srv6 | |
258 | locators | |
259 | no locator loc1 | |
260 | """ | |
261 | ) | |
262 | check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_deleted.json") | |
263 | check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_deleted.json") | |
264 | check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") | |
265 | check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") | |
266 | check_ping4("ce1", "192.168.2.2", False) | |
267 | check_ping6("ce1", "2001:2::2", False) | |
268 | ||
269 | ||
270 | def test_locator_recreate(): | |
271 | check_ping4("ce1", "192.168.2.2", False) | |
272 | check_ping6("ce1", "2001:2::2", False) | |
273 | get_topogen().gears["r1"].vtysh_cmd( | |
274 | """ | |
275 | configure terminal | |
276 | segment-routing | |
277 | srv6 | |
278 | locators | |
279 | locator loc1 | |
280 | prefix 2001:db8:1:1::/64 | |
281 | """ | |
282 | ) | |
283 | check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_recreated.json") | |
284 | check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_recreated.json") | |
285 | check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") | |
286 | check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") | |
287 | check_ping4("ce1", "192.168.2.2", True) | |
288 | check_ping6("ce1", "2001:2::2", True) | |
289 | ||
290 | ||
291 | def test_bgp_locator_unset(): | |
292 | check_ping4("ce1", "192.168.2.2", True) | |
293 | check_ping6("ce1", "2001:2::2", True) | |
294 | get_topogen().gears["r1"].vtysh_cmd( | |
295 | """ | |
296 | configure terminal | |
297 | router bgp 1 | |
298 | segment-routing srv6 | |
299 | no locator loc1 | |
300 | """ | |
301 | ) | |
302 | check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_deleted.json") | |
303 | check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_deleted.json") | |
304 | check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") | |
305 | check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") | |
306 | check_ping4("ce1", "192.168.2.2", False) | |
307 | check_ping6("ce1", "2001:2::2", False) | |
308 | ||
309 | ||
310 | def test_bgp_locator_reset(): | |
311 | check_ping4("ce1", "192.168.2.2", False) | |
312 | check_ping6("ce1", "2001:2::2", False) | |
313 | get_topogen().gears["r1"].vtysh_cmd( | |
314 | """ | |
315 | configure terminal | |
316 | router bgp 1 | |
317 | segment-routing srv6 | |
318 | locator loc1 | |
319 | """ | |
320 | ) | |
321 | check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_recreated.json") | |
322 | check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_recreated.json") | |
323 | check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") | |
324 | check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") | |
325 | check_ping4("ce1", "192.168.2.2", True) | |
326 | check_ping6("ce1", "2001:2::2", True) | |
327 | ||
328 | ||
329 | def test_bgp_srv6_unset(): | |
330 | check_ping4("ce1", "192.168.2.2", True) | |
331 | check_ping6("ce1", "2001:2::2", True) | |
332 | get_topogen().gears["r1"].vtysh_cmd( | |
333 | """ | |
334 | configure terminal | |
335 | router bgp 1 | |
336 | no segment-routing srv6 | |
337 | """ | |
338 | ) | |
339 | check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_deleted.json") | |
340 | check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_deleted.json") | |
341 | check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") | |
342 | check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") | |
343 | check_ping4("ce1", "192.168.2.2", False) | |
344 | check_ping6("ce1", "2001:2::2", False) | |
345 | ||
346 | ||
347 | def test_bgp_srv6_reset(): | |
348 | check_ping4("ce1", "192.168.2.2", False) | |
349 | check_ping6("ce1", "2001:2::2", False) | |
350 | get_topogen().gears["r1"].vtysh_cmd( | |
351 | """ | |
352 | configure terminal | |
353 | router bgp 1 | |
354 | segment-routing srv6 | |
355 | locator loc1 | |
356 | """ | |
357 | ) | |
358 | check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_recreated.json") | |
359 | check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_recreated.json") | |
360 | check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") | |
361 | check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") | |
362 | check_ping4("ce1", "192.168.2.2", True) | |
363 | check_ping6("ce1", "2001:2::2", True) | |
364 | ||
365 | ||
366 | if __name__ == "__main__": | |
367 | args = ["-s"] + sys.argv[1:] | |
368 | sys.exit(pytest.main(args)) |