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