From e2bbf175a2184bd76f6c54ccf8456babeb1a46fc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 9 Apr 2024 15:16:35 +0200 Subject: Adding upstream version 9.1. Signed-off-by: Daniel Baumann --- tests/topotests/.gitignore | 4 + tests/topotests/Dockerfile | 87 + tests/topotests/README.md | 1 + .../topotests/all_protocol_startup/r1/babeld.conf | 4 + tests/topotests/all_protocol_startup/r1/bgpd.conf | 60 + tests/topotests/all_protocol_startup/r1/ip_nht.ref | 74 + .../all_protocol_startup/r1/ipv4_routes.ref | 32 + .../topotests/all_protocol_startup/r1/ipv6_nht.ref | 15 + .../all_protocol_startup/r1/ipv6_routes.ref | 29 + tests/topotests/all_protocol_startup/r1/isisd.conf | 21 + tests/topotests/all_protocol_startup/r1/ldpd.conf | 25 + tests/topotests/all_protocol_startup/r1/nhrpd.conf | 1 + .../topotests/all_protocol_startup/r1/ospf6d.conf | 20 + .../all_protocol_startup/r1/ospf6d.conf-pre-v4 | 16 + tests/topotests/all_protocol_startup/r1/ospfd.conf | 25 + tests/topotests/all_protocol_startup/r1/pbrd.conf | 10 + .../all_protocol_startup/r1/rip_status.ref | 15 + tests/topotests/all_protocol_startup/r1/ripd.conf | 15 + .../all_protocol_startup/r1/ripng_status.ref | 14 + .../topotests/all_protocol_startup/r1/ripngd.conf | 14 + .../r1/show_bgp_ipv4-post4.1.ref | 9 + .../r1/show_bgp_ipv4-post5.0.ref | 9 + .../r1/show_bgp_ipv4-post6.1.ref | 10 + .../all_protocol_startup/r1/show_bgp_ipv4.ref | 7 + .../r1/show_bgp_ipv6-post4.1.ref | 9 + .../all_protocol_startup/r1/show_bgp_ipv6.ref | 7 + .../r1/show_bgp_ipv6_post6.1.ref | 10 + .../r1/show_bgp_ipv6_summary.ref | 8 + .../r1/show_ip_bgp_summary.ref | 10 + .../r1/show_ip_ospf_interface.ref | 26 + .../r1/show_ipv6_ospf6_interface | 0 .../r1/show_ipv6_ospf6_interface.ref | 46 + .../r1/show_isis_interface_detail.ref | 28 + .../r1/show_mpls_ldp_interface.ref | 3 + .../all_protocol_startup/r1/show_route_map.ref | 72 + tests/topotests/all_protocol_startup/r1/zebra.conf | 120 + .../test_all_protocol_startup.dot | 61 + .../test_all_protocol_startup.pdf | Bin 0 -> 21760 bytes .../test_all_protocol_startup.py | 1724 ++++++ tests/topotests/analyze.py | 433 ++ tests/topotests/babel_topo1/r1/babeld.conf | 20 + .../babel_topo1/r1/show_ip_route.json_ref | 80 + tests/topotests/babel_topo1/r1/zebra.conf | 22 + tests/topotests/babel_topo1/r2/babeld.conf | 17 + .../babel_topo1/r2/show_ip_route.json_ref | 80 + tests/topotests/babel_topo1/r2/zebra.conf | 23 + tests/topotests/babel_topo1/r3/babeld.conf | 16 + .../babel_topo1/r3/show_ip_route.json_ref | 81 + tests/topotests/babel_topo1/r3/zebra.conf | 25 + tests/topotests/babel_topo1/test_babel_topo1.py | 168 + tests/topotests/bfd_bgp_cbit_topo3/__init__.py | 0 tests/topotests/bfd_bgp_cbit_topo3/r1/bfdd.conf | 5 + .../r1/bgp_ipv6_routes_down.json | 98 + tests/topotests/bfd_bgp_cbit_topo3/r1/bgpd.conf | 23 + .../bfd_bgp_cbit_topo3/r1/ipv6_routes.json | 80 + tests/topotests/bfd_bgp_cbit_topo3/r1/peers.json | 17 + .../bfd_bgp_cbit_topo3/r1/peers_down.json | 15 + tests/topotests/bfd_bgp_cbit_topo3/r1/zebra.conf | 8 + tests/topotests/bfd_bgp_cbit_topo3/r2/zebra.conf | 9 + tests/topotests/bfd_bgp_cbit_topo3/r3/bfdd.conf | 5 + .../r3/bgp_ipv6_routes_down.json | 52 + tests/topotests/bfd_bgp_cbit_topo3/r3/bgpd.conf | 29 + .../bfd_bgp_cbit_topo3/r3/ipv6_routes.json | 80 + tests/topotests/bfd_bgp_cbit_topo3/r3/peers.json | 17 + .../bfd_bgp_cbit_topo3/r3/peers_down.json | 15 + tests/topotests/bfd_bgp_cbit_topo3/r3/zebra.conf | 7 + .../bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.dot | 58 + .../bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.py | 234 + tests/topotests/bfd_isis_topo1/__init__.py | 0 tests/topotests/bfd_isis_topo1/rt1/bfdd.conf | 19 + tests/topotests/bfd_isis_topo1/rt1/isisd.conf | 36 + .../bfd_isis_topo1/rt1/step1/show_ip_route.ref | 74 + .../bfd_isis_topo1/rt1/step1/show_ipv6_route.ref | 70 + .../bfd_isis_topo1/rt1/step2/show_bfd_peers.ref | 16 + .../rt1/step3/show_bfd_peers_healthy.ref | 16 + .../rt1/step3/show_bfd_peers_rt2_down.ref | 9 + .../rt1/step3/show_bfd_peers_rt3_down.ref | 9 + .../rt1/step3/show_ip_route_healthy.ref | 74 + .../rt1/step3/show_ip_route_rt2_down.ref | 74 + .../rt1/step3/show_ip_route_rt3_down.ref | 74 + .../rt1/step3/show_ipv6_route_healthy.ref | 70 + .../rt1/step3/show_ipv6_route_rt2_down.ref | 70 + .../rt1/step3/show_ipv6_route_rt3_down.ref | 70 + tests/topotests/bfd_isis_topo1/rt1/zebra.conf | 25 + tests/topotests/bfd_isis_topo1/rt2/bfdd.conf | 13 + tests/topotests/bfd_isis_topo1/rt2/isisd.conf | 31 + .../bfd_isis_topo1/rt2/step2/show_bfd_peers.ref | 9 + tests/topotests/bfd_isis_topo1/rt2/zebra.conf | 22 + tests/topotests/bfd_isis_topo1/rt3/bfdd.conf | 13 + tests/topotests/bfd_isis_topo1/rt3/isisd.conf | 33 + .../bfd_isis_topo1/rt3/step2/show_bfd_peers.ref | 9 + tests/topotests/bfd_isis_topo1/rt3/zebra.conf | 22 + tests/topotests/bfd_isis_topo1/rt4/bfdd.conf | 5 + tests/topotests/bfd_isis_topo1/rt4/isisd.conf | 30 + tests/topotests/bfd_isis_topo1/rt4/zebra.conf | 22 + tests/topotests/bfd_isis_topo1/rt5/bfdd.conf | 5 + tests/topotests/bfd_isis_topo1/rt5/isisd.conf | 30 + tests/topotests/bfd_isis_topo1/rt5/zebra.conf | 22 + .../bfd_isis_topo1/test_bfd_isis_topo1.py | 255 + tests/topotests/bfd_ospf_topo1/__init__.py | 0 tests/topotests/bfd_ospf_topo1/rt1/bfdd.conf | 9 + tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf | 25 + tests/topotests/bfd_ospf_topo1/rt1/ospfd.conf | 31 + .../bfd_ospf_topo1/rt1/step1/show_ip_route.ref | 74 + .../bfd_ospf_topo1/rt1/step1/show_ipv6_route.ref | 70 + .../bfd_ospf_topo1/rt1/step2/show_bfd_peers.ref | 26 + .../rt1/step3/show_bfd_peers_healthy.ref | 28 + .../rt1/step3/show_bfd_peers_rt2_down.ref | 15 + .../rt1/step3/show_bfd_peers_rt3_down.ref | 15 + .../rt1/step3/show_ip_route_healthy.ref | 74 + .../rt1/step3/show_ip_route_rt2_down.ref | 74 + .../rt1/step3/show_ip_route_rt3_down.ref | 74 + .../rt1/step3/show_ipv6_route_healthy.ref | 70 + .../rt1/step3/show_ipv6_route_rt2_down.ref | 70 + .../rt1/step3/show_ipv6_route_rt3_down.ref | 70 + tests/topotests/bfd_ospf_topo1/rt1/zebra.conf | 25 + tests/topotests/bfd_ospf_topo1/rt2/bfdd.conf | 7 + tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf | 23 + tests/topotests/bfd_ospf_topo1/rt2/ospfd.conf | 29 + .../bfd_ospf_topo1/rt2/step2/show_bfd_peers.ref | 14 + tests/topotests/bfd_ospf_topo1/rt2/zebra.conf | 22 + tests/topotests/bfd_ospf_topo1/rt3/bfdd.conf | 7 + tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf | 23 + tests/topotests/bfd_ospf_topo1/rt3/ospfd.conf | 29 + .../bfd_ospf_topo1/rt3/step2/show_bfd_peers.ref | 14 + tests/topotests/bfd_ospf_topo1/rt3/zebra.conf | 22 + tests/topotests/bfd_ospf_topo1/rt4/bfdd.conf | 5 + tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf | 22 + tests/topotests/bfd_ospf_topo1/rt4/ospfd.conf | 28 + tests/topotests/bfd_ospf_topo1/rt4/zebra.conf | 22 + tests/topotests/bfd_ospf_topo1/rt5/bfdd.conf | 5 + tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf | 22 + tests/topotests/bfd_ospf_topo1/rt5/ospfd.conf | 28 + tests/topotests/bfd_ospf_topo1/rt5/zebra.conf | 22 + .../bfd_ospf_topo1/test_bfd_ospf_topo1.py | 260 + tests/topotests/bfd_profiles_topo1/__init__.py | 0 .../bfd_profiles_topo1/r1/bfd-peers-initial.json | 38 + tests/topotests/bfd_profiles_topo1/r1/bfdd.conf | 15 + tests/topotests/bfd_profiles_topo1/r1/ospfd.conf | 11 + tests/topotests/bfd_profiles_topo1/r1/zebra.conf | 9 + .../bfd_profiles_topo1/r2/bfd-peers-initial.json | 40 + tests/topotests/bfd_profiles_topo1/r2/bfdd.conf | 19 + tests/topotests/bfd_profiles_topo1/r2/bgpd.conf | 21 + tests/topotests/bfd_profiles_topo1/r2/zebra.conf | 10 + .../bfd_profiles_topo1/r3/bfd-peers-initial.json | 40 + tests/topotests/bfd_profiles_topo1/r3/bfdd.conf | 10 + tests/topotests/bfd_profiles_topo1/r3/bgpd.conf | 14 + tests/topotests/bfd_profiles_topo1/r3/isisd.conf | 17 + tests/topotests/bfd_profiles_topo1/r3/zebra.conf | 12 + .../bfd_profiles_topo1/r4/bfd-peers-initial.json | 41 + tests/topotests/bfd_profiles_topo1/r4/bfdd.conf | 10 + tests/topotests/bfd_profiles_topo1/r4/bgpd.conf | 18 + tests/topotests/bfd_profiles_topo1/r4/isisd.conf | 17 + tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf | 10 + tests/topotests/bfd_profiles_topo1/r4/zebra.conf | 12 + .../bfd_profiles_topo1/r5/bfd-peers-initial.json | 21 + tests/topotests/bfd_profiles_topo1/r5/bfdd.conf | 11 + tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf | 10 + tests/topotests/bfd_profiles_topo1/r5/zebra.conf | 6 + .../bfd_profiles_topo1/r6/bfd-peers-initial.json | 20 + tests/topotests/bfd_profiles_topo1/r6/bfdd.conf | 11 + tests/topotests/bfd_profiles_topo1/r6/ospfd.conf | 10 + tests/topotests/bfd_profiles_topo1/r6/zebra.conf | 6 + .../bfd_profiles_topo1/test_bfd_profiles_topo1.dot | 97 + .../bfd_profiles_topo1/test_bfd_profiles_topo1.png | Bin 0 -> 43508 bytes .../bfd_profiles_topo1/test_bfd_profiles_topo1.py | 160 + tests/topotests/bfd_topo1/__init__.py | 0 tests/topotests/bfd_topo1/r1/bfdd.conf | 11 + tests/topotests/bfd_topo1/r1/bgp_prefixes.json | 52 + tests/topotests/bfd_topo1/r1/bgp_summary.json | 11 + tests/topotests/bfd_topo1/r1/bgpd.conf | 10 + tests/topotests/bfd_topo1/r1/peers.json | 8 + tests/topotests/bfd_topo1/r1/zebra.conf | 3 + tests/topotests/bfd_topo1/r2/bfdd.conf | 17 + tests/topotests/bfd_topo1/r2/bgp_prefixes.json | 52 + tests/topotests/bfd_topo1/r2/bgp_summary.json | 19 + tests/topotests/bfd_topo1/r2/bgpd.conf | 16 + tests/topotests/bfd_topo1/r2/peers.json | 17 + tests/topotests/bfd_topo1/r2/zebra.conf | 9 + tests/topotests/bfd_topo1/r3/bfdd.conf | 12 + tests/topotests/bfd_topo1/r3/bgp_prefixes.json | 52 + tests/topotests/bfd_topo1/r3/bgp_summary.json | 11 + tests/topotests/bfd_topo1/r3/bgpd.conf | 10 + tests/topotests/bfd_topo1/r3/peers.json | 6 + tests/topotests/bfd_topo1/r3/zebra.conf | 3 + tests/topotests/bfd_topo1/r4/bfdd.conf | 12 + tests/topotests/bfd_topo1/r4/bgp_prefixes.json | 52 + tests/topotests/bfd_topo1/r4/bgp_summary.json | 11 + tests/topotests/bfd_topo1/r4/bgpd.conf | 10 + tests/topotests/bfd_topo1/r4/peers.json | 6 + tests/topotests/bfd_topo1/r4/zebra.conf | 3 + tests/topotests/bfd_topo1/test_bfd_topo1.dot | 73 + tests/topotests/bfd_topo1/test_bfd_topo1.jpg | Bin 0 -> 25713 bytes tests/topotests/bfd_topo1/test_bfd_topo1.py | 213 + tests/topotests/bfd_topo2/__init__.py | 0 tests/topotests/bfd_topo2/r1/bfdd.conf | 10 + tests/topotests/bfd_topo2/r1/bgpd.conf | 15 + tests/topotests/bfd_topo2/r1/ipv4_routes.json | 59 + tests/topotests/bfd_topo2/r1/ipv6_routes.json | 53 + tests/topotests/bfd_topo2/r1/peers.json | 31 + tests/topotests/bfd_topo2/r1/zebra.conf | 6 + tests/topotests/bfd_topo2/r2/bfdd.conf | 5 + tests/topotests/bfd_topo2/r2/bgpd.conf | 18 + tests/topotests/bfd_topo2/r2/ipv4_routes.json | 92 + tests/topotests/bfd_topo2/r2/ipv6_routes.json | 53 + tests/topotests/bfd_topo2/r2/ospf6d.conf | 11 + tests/topotests/bfd_topo2/r2/ospfd.conf | 11 + tests/topotests/bfd_topo2/r2/peers.json | 45 + tests/topotests/bfd_topo2/r2/zebra.conf | 15 + tests/topotests/bfd_topo2/r3/bfdd.conf | 5 + tests/topotests/bfd_topo2/r3/ipv4_routes.json | 93 + tests/topotests/bfd_topo2/r3/ipv6_routes.json | 2 + tests/topotests/bfd_topo2/r3/ospfd.conf | 10 + tests/topotests/bfd_topo2/r3/peers.json | 17 + tests/topotests/bfd_topo2/r3/zebra.conf | 6 + tests/topotests/bfd_topo2/r4/bfdd.conf | 10 + tests/topotests/bfd_topo2/r4/ipv4_routes.json | 21 + tests/topotests/bfd_topo2/r4/ipv6_routes.json | 53 + tests/topotests/bfd_topo2/r4/ospf6d.conf | 10 + tests/topotests/bfd_topo2/r4/peers.json | 31 + tests/topotests/bfd_topo2/r4/zebra.conf | 6 + tests/topotests/bfd_topo2/test_bfd_topo2.dot | 73 + tests/topotests/bfd_topo2/test_bfd_topo2.jpg | Bin 0 -> 24206 bytes tests/topotests/bfd_topo2/test_bfd_topo2.py | 151 + tests/topotests/bfd_topo3/__init__.py | 0 tests/topotests/bfd_topo3/r1/bfd-peers.json | 68 + tests/topotests/bfd_topo3/r1/bfdd.conf | 17 + tests/topotests/bfd_topo3/r1/bgpd.conf | 23 + tests/topotests/bfd_topo3/r1/zebra.conf | 10 + tests/topotests/bfd_topo3/r2/bfd-peers.json | 46 + tests/topotests/bfd_topo3/r2/bfdd.conf | 15 + tests/topotests/bfd_topo3/r2/bgpd.conf | 17 + tests/topotests/bfd_topo3/r2/zebra.conf | 14 + tests/topotests/bfd_topo3/r3/bfd-peers.json | 68 + tests/topotests/bfd_topo3/r3/bfd-static-down.json | 12 + tests/topotests/bfd_topo3/r3/bfd-static.json | 13 + tests/topotests/bfd_topo3/r3/bfdd.conf | 11 + tests/topotests/bfd_topo3/r3/bgpd.conf | 22 + tests/topotests/bfd_topo3/r3/staticd.conf | 1 + tests/topotests/bfd_topo3/r3/zebra.conf | 14 + tests/topotests/bfd_topo3/r4/bfd-peers.json | 90 + tests/topotests/bfd_topo3/r4/bfdd.conf | 16 + tests/topotests/bfd_topo3/r4/bgpd.conf | 19 + tests/topotests/bfd_topo3/r4/staticd.conf | 2 + tests/topotests/bfd_topo3/r4/zebra.conf | 14 + tests/topotests/bfd_topo3/r5/bfd-peers.json | 23 + tests/topotests/bfd_topo3/r5/bfdd.conf | 11 + tests/topotests/bfd_topo3/r5/staticd.conf | 2 + tests/topotests/bfd_topo3/r5/zebra.conf | 10 + tests/topotests/bfd_topo3/r6/bfd-peers.json | 46 + tests/topotests/bfd_topo3/r6/bfd-static-down.json | 19 + tests/topotests/bfd_topo3/r6/bfd-static.json | 19 + tests/topotests/bfd_topo3/r6/bfdd.conf | 11 + tests/topotests/bfd_topo3/r6/staticd.conf | 5 + tests/topotests/bfd_topo3/r6/zebra.conf | 10 + tests/topotests/bfd_topo3/test_bfd_topo3.dot | 95 + tests/topotests/bfd_topo3/test_bfd_topo3.jpg | Bin 0 -> 42789 bytes tests/topotests/bfd_topo3/test_bfd_topo3.py | 248 + tests/topotests/bfd_vrf_topo1/__init__.py | 0 tests/topotests/bfd_vrf_topo1/r1/bfdd.conf | 15 + tests/topotests/bfd_vrf_topo1/r1/bgp_prefixes.json | 52 + tests/topotests/bfd_vrf_topo1/r1/bgp_summary.json | 11 + tests/topotests/bfd_vrf_topo1/r1/bgpd.conf | 11 + tests/topotests/bfd_vrf_topo1/r1/peers.json | 8 + tests/topotests/bfd_vrf_topo1/r1/zebra.conf | 3 + tests/topotests/bfd_vrf_topo1/r2/bfdd.conf | 23 + tests/topotests/bfd_vrf_topo1/r2/bgp_prefixes.json | 52 + tests/topotests/bfd_vrf_topo1/r2/bgp_summary.json | 19 + tests/topotests/bfd_vrf_topo1/r2/bgpd.conf | 16 + tests/topotests/bfd_vrf_topo1/r2/peers.json | 17 + tests/topotests/bfd_vrf_topo1/r2/zebra.conf | 9 + tests/topotests/bfd_vrf_topo1/r3/bfdd.conf | 14 + tests/topotests/bfd_vrf_topo1/r3/bgp_prefixes.json | 52 + tests/topotests/bfd_vrf_topo1/r3/bgp_summary.json | 11 + tests/topotests/bfd_vrf_topo1/r3/bgpd.conf | 10 + tests/topotests/bfd_vrf_topo1/r3/peers.json | 6 + tests/topotests/bfd_vrf_topo1/r3/zebra.conf | 3 + tests/topotests/bfd_vrf_topo1/r4/bfdd.conf | 12 + tests/topotests/bfd_vrf_topo1/r4/bgp_prefixes.json | 52 + tests/topotests/bfd_vrf_topo1/r4/bgp_summary.json | 11 + tests/topotests/bfd_vrf_topo1/r4/bgpd.conf | 10 + tests/topotests/bfd_vrf_topo1/r4/peers.json | 6 + tests/topotests/bfd_vrf_topo1/r4/zebra.conf | 3 + .../topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.dot | 73 + .../topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.jpg | Bin 0 -> 25713 bytes .../topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py | 267 + tests/topotests/bfd_vrflite_topo1/__init__.py | 0 .../bfd_vrflite_topo1/r1/bfd_peers_status.json | 96 + tests/topotests/bfd_vrflite_topo1/r1/bfdd.conf | 26 + tests/topotests/bfd_vrflite_topo1/r1/zebra.conf | 24 + tests/topotests/bfd_vrflite_topo1/r2/bfdd.conf | 26 + tests/topotests/bfd_vrflite_topo1/r2/zebra.conf | 24 + .../bfd_vrflite_topo1/test_bfd_vrflite_topo1.py | 140 + tests/topotests/bgp_accept_own/__init__.py | 0 tests/topotests/bgp_accept_own/ce1/bgpd.conf | 12 + tests/topotests/bgp_accept_own/ce1/zebra.conf | 9 + tests/topotests/bgp_accept_own/ce2/bgpd.conf | 12 + tests/topotests/bgp_accept_own/ce2/zebra.conf | 9 + tests/topotests/bgp_accept_own/pe1/bgpd.conf | 50 + tests/topotests/bgp_accept_own/pe1/ldpd.conf | 12 + tests/topotests/bgp_accept_own/pe1/ospfd.conf | 7 + tests/topotests/bgp_accept_own/pe1/zebra.conf | 18 + tests/topotests/bgp_accept_own/rr1/bgpd.conf | 25 + tests/topotests/bgp_accept_own/rr1/ldpd.conf | 12 + tests/topotests/bgp_accept_own/rr1/ospfd.conf | 7 + tests/topotests/bgp_accept_own/rr1/zebra.conf | 9 + .../bgp_accept_own/test_bgp_accept_own.py | 216 + .../bgp_addpath_best_selected/__init__.py | 0 .../bgp_addpath_best_selected/r1/bgpd.conf | 7 + .../bgp_addpath_best_selected/r1/zebra.conf | 4 + .../bgp_addpath_best_selected/r2/bgpd.conf | 27 + .../bgp_addpath_best_selected/r2/zebra.conf | 10 + .../bgp_addpath_best_selected/r3/bgpd.conf | 9 + .../bgp_addpath_best_selected/r3/zebra.conf | 7 + .../bgp_addpath_best_selected/r4/bgpd.conf | 9 + .../bgp_addpath_best_selected/r4/zebra.conf | 7 + .../bgp_addpath_best_selected/r5/bgpd.conf | 9 + .../bgp_addpath_best_selected/r5/zebra.conf | 7 + .../bgp_addpath_best_selected/r6/bgpd.conf | 9 + .../bgp_addpath_best_selected/r6/zebra.conf | 7 + .../bgp_addpath_best_selected/r7/bgpd.conf | 7 + .../bgp_addpath_best_selected/r7/zebra.conf | 4 + .../test_bgp_addpath_best_selected.py | 190 + .../bgp_aggregate_address_matching_med/__init__.py | 0 .../r1/bgpd.conf | 21 + .../r1/zebra.conf | 11 + .../r2/bgpd.conf | 11 + .../r2/zebra.conf | 9 + .../r3/bgpd.conf | 6 + .../r3/zebra.conf | 6 + .../test_bgp_aggregate_address_matching_med.py | 135 + .../bgp_aggregate_address_origin/__init__.py | 0 .../bgp_aggregate_address_origin/r1/bgpd.conf | 9 + .../bgp_aggregate_address_origin/r1/zebra.conf | 9 + .../bgp_aggregate_address_origin/r2/bgpd.conf | 6 + .../bgp_aggregate_address_origin/r2/zebra.conf | 6 + .../test_bgp_aggregate-address_origin.py | 106 + .../bgp_aggregate_address_route_map/__init__.py | 0 .../bgp_aggregate_address_route_map/r1/bgpd.conf | 12 + .../bgp_aggregate_address_route_map/r1/zebra.conf | 9 + .../bgp_aggregate_address_route_map/r2/bgpd.conf | 6 + .../bgp_aggregate_address_route_map/r2/zebra.conf | 6 + .../test_bgp_aggregate-address_route-map.py | 109 + .../bgp_aggregate_address_topo1/__init__.py | 0 .../bgp_aggregate_address_topo1/exabgp.env | 53 + .../bgp_aggregate_address_topo1/peer1/exabgp.cfg | 21 + .../bgp_aggregate_address_topo1/r1/bgpd.conf | 30 + .../bgp_aggregate_address_topo1/r1/zebra.conf | 13 + .../bgp_aggregate_address_topo1/r2/bgpd.conf | 7 + .../bgp_aggregate_address_topo1/r2/zebra.conf | 10 + .../test_bgp_aggregate_address_topo1.py | 279 + tests/topotests/bgp_aggregator_zero/__init__.py | 0 tests/topotests/bgp_aggregator_zero/exabgp.env | 53 + .../topotests/bgp_aggregator_zero/peer1/exabgp.cfg | 18 + tests/topotests/bgp_aggregator_zero/r1/bgpd.conf | 6 + tests/topotests/bgp_aggregator_zero/r1/zebra.conf | 6 + .../test_bgp_aggregator_zero.py | 120 + tests/topotests/bgp_aigp/__init__.py | 0 tests/topotests/bgp_aigp/r1/bgpd.conf | 12 + tests/topotests/bgp_aigp/r1/ospfd.conf | 17 + tests/topotests/bgp_aigp/r1/zebra.conf | 10 + tests/topotests/bgp_aigp/r2/bgpd.conf | 15 + tests/topotests/bgp_aigp/r2/ospfd.conf | 13 + tests/topotests/bgp_aigp/r2/zebra.conf | 10 + tests/topotests/bgp_aigp/r3/bgpd.conf | 15 + tests/topotests/bgp_aigp/r3/ospfd.conf | 13 + tests/topotests/bgp_aigp/r3/zebra.conf | 10 + tests/topotests/bgp_aigp/r4/bgpd.conf | 17 + tests/topotests/bgp_aigp/r4/ospfd.conf | 13 + tests/topotests/bgp_aigp/r4/zebra.conf | 10 + tests/topotests/bgp_aigp/r5/bgpd.conf | 17 + tests/topotests/bgp_aigp/r5/ospfd.conf | 13 + tests/topotests/bgp_aigp/r5/zebra.conf | 10 + tests/topotests/bgp_aigp/r6/bgpd.conf | 20 + tests/topotests/bgp_aigp/r6/ospfd.conf | 17 + tests/topotests/bgp_aigp/r6/zebra.conf | 13 + tests/topotests/bgp_aigp/r7/bgpd.conf | 22 + tests/topotests/bgp_aigp/r7/zebra.conf | 9 + tests/topotests/bgp_aigp/test_bgp_aigp.py | 217 + .../bgp_always_compare_med_topo1.json | 152 + .../test_bgp_always_compare_med_topo1.py | 1118 ++++ .../topotests/bgp_as_allow_in/bgp_as_allow_in.json | 266 + .../bgp_as_allow_in/test_bgp_as_allow_in.py | 957 +++ tests/topotests/bgp_as_override/__init__.py | 0 tests/topotests/bgp_as_override/r1/bgpd.conf | 10 + tests/topotests/bgp_as_override/r1/zebra.conf | 9 + tests/topotests/bgp_as_override/r2/bgpd.conf | 10 + tests/topotests/bgp_as_override/r2/zebra.conf | 9 + tests/topotests/bgp_as_override/r3/bgpd.conf | 13 + tests/topotests/bgp_as_override/r3/zebra.conf | 9 + tests/topotests/bgp_as_override/r4/bgpd.conf | 7 + tests/topotests/bgp_as_override/r4/zebra.conf | 6 + .../bgp_as_override/test_bgp_as_override.py | 109 + .../bgp_as_wide_bgp_identifier/__init__.py | 0 .../bgp_as_wide_bgp_identifier/r1/bgpd.conf | 7 + .../bgp_as_wide_bgp_identifier/r1/zebra.conf | 6 + .../bgp_as_wide_bgp_identifier/r2/bgpd.conf | 9 + .../bgp_as_wide_bgp_identifier/r2/zebra.conf | 6 + .../bgp_as_wide_bgp_identifier/r3/bgpd.conf | 7 + .../bgp_as_wide_bgp_identifier/r3/zebra.conf | 6 + .../test_bgp_as_wide_bgp_identifier.py | 102 + tests/topotests/bgp_asdot_regex/__init__.py | 0 tests/topotests/bgp_asdot_regex/r1/bgpd.conf | 27 + .../bgp_asdot_regex/r1/show_bgp_ipv4.json | 80 + tests/topotests/bgp_asdot_regex/r1/zebra.conf | 6 + tests/topotests/bgp_asdot_regex/r2/bgpd.conf | 26 + .../bgp_asdot_regex/r2/show_bgp_ipv4.json | 80 + tests/topotests/bgp_asdot_regex/r2/zebra.conf | 6 + .../bgp_asdot_regex/test_bgp_asdot_regex.py | 122 + tests/topotests/bgp_aspath_zero/__init__.py | 0 tests/topotests/bgp_aspath_zero/exabgp.env | 53 + tests/topotests/bgp_aspath_zero/peer1/exabgp.cfg | 17 + tests/topotests/bgp_aspath_zero/r1/bgpd.conf | 6 + tests/topotests/bgp_aspath_zero/r1/zebra.conf | 6 + .../bgp_aspath_zero/test_bgp_aspath_zero.py | 100 + tests/topotests/bgp_auth/R1/bgpd.conf | 18 + tests/topotests/bgp_auth/R1/bgpd_multi_vrf.conf | 39 + .../bgp_auth/R1/bgpd_multi_vrf_prefix.conf | 37 + tests/topotests/bgp_auth/R1/bgpd_prefix.conf | 18 + tests/topotests/bgp_auth/R1/bgpd_vrf.conf | 20 + tests/topotests/bgp_auth/R1/bgpd_vrf_prefix.conf | 18 + tests/topotests/bgp_auth/R1/empty.conf | 0 tests/topotests/bgp_auth/R1/ospfd.conf | 22 + tests/topotests/bgp_auth/R1/ospfd_multi_vrf.conf | 26 + tests/topotests/bgp_auth/R1/ospfd_vrf.conf | 22 + tests/topotests/bgp_auth/R1/zebra.conf | 20 + tests/topotests/bgp_auth/R2/bgpd.conf | 18 + tests/topotests/bgp_auth/R2/bgpd_multi_vrf.conf | 37 + .../bgp_auth/R2/bgpd_multi_vrf_prefix.conf | 37 + tests/topotests/bgp_auth/R2/bgpd_prefix.conf | 18 + tests/topotests/bgp_auth/R2/bgpd_vrf.conf | 18 + tests/topotests/bgp_auth/R2/bgpd_vrf_prefix.conf | 18 + tests/topotests/bgp_auth/R2/empty.conf | 0 tests/topotests/bgp_auth/R2/ospfd.conf | 22 + tests/topotests/bgp_auth/R2/ospfd_multi_vrf.conf | 26 + tests/topotests/bgp_auth/R2/ospfd_vrf.conf | 22 + tests/topotests/bgp_auth/R2/zebra.conf | 20 + tests/topotests/bgp_auth/R3/bgpd.conf | 18 + tests/topotests/bgp_auth/R3/bgpd_multi_vrf.conf | 37 + .../bgp_auth/R3/bgpd_multi_vrf_prefix.conf | 37 + tests/topotests/bgp_auth/R3/bgpd_prefix.conf | 18 + tests/topotests/bgp_auth/R3/bgpd_vrf.conf | 18 + tests/topotests/bgp_auth/R3/bgpd_vrf_prefix.conf | 18 + tests/topotests/bgp_auth/R3/empty.conf | 0 tests/topotests/bgp_auth/R3/ospfd.conf | 22 + tests/topotests/bgp_auth/R3/ospfd_multi_vrf.conf | 26 + tests/topotests/bgp_auth/R3/ospfd_vrf.conf | 22 + tests/topotests/bgp_auth/R3/zebra.conf | 20 + tests/topotests/bgp_auth/bgp_auth_common.py | 266 + tests/topotests/bgp_auth/test_bgp_auth1.py | 232 + tests/topotests/bgp_auth/test_bgp_auth2.py | 240 + tests/topotests/bgp_auth/test_bgp_auth3.py | 221 + tests/topotests/bgp_auth/test_bgp_auth4.py | 238 + .../bgp_basic_functionality_topo1/__init__.py | 0 .../bgp_basic_functionality.json | 115 + .../test_bgp_basic_functionality.py | 1169 ++++ .../bgp_bfd_down_cease_notification/__init__.py | 0 .../bgp_bfd_down_cease_notification/r1/bfdd.conf | 6 + .../bgp_bfd_down_cease_notification/r1/bgpd.conf | 11 + .../bgp_bfd_down_cease_notification/r1/zebra.conf | 9 + .../bgp_bfd_down_cease_notification/r2/bfdd.conf | 6 + .../bgp_bfd_down_cease_notification/r2/bgpd.conf | 10 + .../bgp_bfd_down_cease_notification/r2/zebra.conf | 9 + .../test_bgp_bfd_down_cease_notification.py | 112 + .../topotests/bgp_blackhole_community/__init__.py | 0 .../topotests/bgp_blackhole_community/r1/bgpd.conf | 14 + .../bgp_blackhole_community/r1/zebra.conf | 10 + .../topotests/bgp_blackhole_community/r2/bgpd.conf | 8 + .../bgp_blackhole_community/r2/zebra.conf | 12 + .../topotests/bgp_blackhole_community/r3/bgpd.conf | 6 + .../bgp_blackhole_community/r3/zebra.conf | 6 + .../topotests/bgp_blackhole_community/r4/bgpd.conf | 13 + .../bgp_blackhole_community/r4/zebra.conf | 6 + .../test_bgp_blackhole_community.py | 161 + tests/topotests/bgp_bmp/__init__.py | 0 tests/topotests/bgp_bmp/r1/bgpd.conf | 22 + tests/topotests/bgp_bmp/r1/zebra.conf | 7 + tests/topotests/bgp_bmp/r2/bgpd.conf | 19 + tests/topotests/bgp_bmp/r2/zebra.conf | 8 + tests/topotests/bgp_bmp/test_bgp_bmp.py | 246 + .../topotests/bgp_color_extcommunities/__init__.py | 0 .../bgp_color_extcommunities/r1/bgpd.conf | 17 + .../bgp_color_extcommunities/r1/zebra.conf | 3 + .../bgp_color_extcommunities/r2/bgpd.conf | 4 + .../bgp_color_extcommunities/r2/zebra.conf | 4 + .../test_bgp_color_extcommunities.py | 125 + tests/topotests/bgp_comm_list_delete/__init__.py | 0 tests/topotests/bgp_comm_list_delete/r1/bgpd.conf | 11 + tests/topotests/bgp_comm_list_delete/r1/zebra.conf | 9 + tests/topotests/bgp_comm_list_delete/r2/bgpd.conf | 13 + tests/topotests/bgp_comm_list_delete/r2/zebra.conf | 6 + .../test_bgp_comm-list_delete.py | 105 + tests/topotests/bgp_comm_list_match/__init__.py | 0 tests/topotests/bgp_comm_list_match/r1/bgpd.conf | 28 + tests/topotests/bgp_comm_list_match/r1/zebra.conf | 12 + tests/topotests/bgp_comm_list_match/r2/bgpd.conf | 24 + tests/topotests/bgp_comm_list_match/r2/zebra.conf | 9 + tests/topotests/bgp_comm_list_match/r3/bgpd.conf | 21 + tests/topotests/bgp_comm_list_match/r3/zebra.conf | 6 + .../test_bgp_comm_list_match.py | 138 + .../bgp_communities_topo1/bgp_communities.json | 175 + .../bgp_communities_topo2.json | 191 + .../bgp_communities_topo1/test_bgp_communities.py | 631 ++ .../test_bgp_communities_topo2.py | 371 ++ tests/topotests/bgp_community_alias/__init__.py | 0 tests/topotests/bgp_community_alias/r1/bgpd.conf | 26 + tests/topotests/bgp_community_alias/r1/zebra.conf | 4 + tests/topotests/bgp_community_alias/r2/bgpd.conf | 25 + tests/topotests/bgp_community_alias/r2/zebra.conf | 9 + .../test_bgp-community-alias.py | 140 + .../bgp_community_change_update/__init__.py | 0 .../bgp_community_change_update/c1/bgpd.conf | 11 + .../bgp_community_change_update/c1/zebra.conf | 6 + .../test_bgp_community_change_update.py | 209 + .../bgp_community_change_update/x1/bgpd.conf | 20 + .../bgp_community_change_update/x1/zebra.conf | 9 + .../bgp_community_change_update/y1/bgpd.conf | 12 + .../bgp_community_change_update/y1/zebra.conf | 12 + .../bgp_community_change_update/y2/bgpd.conf | 18 + .../bgp_community_change_update/y2/zebra.conf | 12 + .../bgp_community_change_update/y3/bgpd.conf | 18 + .../bgp_community_change_update/y3/zebra.conf | 12 + .../bgp_community_change_update/z1/bgpd.conf | 10 + .../bgp_community_change_update/z1/zebra.conf | 12 + .../bgp_conditional_advertisement/__init__.py | 0 .../bgp_conditional_advertisement/r1/bgpd.conf | 31 + .../bgp_conditional_advertisement/r1/zebra.conf | 19 + .../bgp_conditional_advertisement/r2/bgpd.conf | 46 + .../bgp_conditional_advertisement/r2/zebra.conf | 15 + .../bgp_conditional_advertisement/r3/bgpd.conf | 12 + .../bgp_conditional_advertisement/r3/zebra.conf | 12 + .../test_bgp_conditional_advertisement.py | 1297 ++++ .../__init__.py | 0 .../r1/frr.conf | 10 + .../r2/frr.conf | 40 + .../r3/frr.conf | 19 + ...t_bgp_conditional_advertisement_static_route.py | 138 + .../__init__.py | 0 .../r1/bgpd.conf | 17 + .../r1/zebra.conf | 6 + .../r2/bgpd.conf | 28 + .../r2/staticd.conf | 3 + .../r2/zebra.conf | 9 + .../r3/bgpd.conf | 10 + .../r3/zebra.conf | 9 + ...est_bgp_conditional_advertisement_track_peer.py | 141 + tests/topotests/bgp_confed1/__init__.py | 0 .../topotests/bgp_confed1/r1/bgp_ipv4_unicast.json | 63 + tests/topotests/bgp_confed1/r1/bgp_summary.json | 13 + tests/topotests/bgp_confed1/r1/bgpd.conf | 13 + tests/topotests/bgp_confed1/r1/isisd.conf | 0 tests/topotests/bgp_confed1/r1/zebra.conf | 6 + .../topotests/bgp_confed1/r2/bgp_ipv4_unicast.json | 63 + tests/topotests/bgp_confed1/r2/bgp_summary.json | 18 + tests/topotests/bgp_confed1/r2/bgpd.conf | 18 + tests/topotests/bgp_confed1/r2/isisd.conf | 8 + tests/topotests/bgp_confed1/r2/zebra.conf | 9 + .../topotests/bgp_confed1/r3/bgp_ipv4_unicast.json | 77 + tests/topotests/bgp_confed1/r3/bgp_summary.json | 18 + tests/topotests/bgp_confed1/r3/bgpd.conf | 17 + tests/topotests/bgp_confed1/r3/isisd.conf | 8 + tests/topotests/bgp_confed1/r3/zebra.conf | 10 + .../topotests/bgp_confed1/r4/bgp_ipv4_unicast.json | 77 + tests/topotests/bgp_confed1/r4/bgp_summary.json | 13 + tests/topotests/bgp_confed1/r4/bgpd.conf | 14 + tests/topotests/bgp_confed1/r4/isisd.conf | 0 tests/topotests/bgp_confed1/r4/zebra.conf | 6 + tests/topotests/bgp_confed1/test_bgp_confed1.py | 116 + .../topotests/bgp_confederation_astype/__init__.py | 0 .../bgp_confederation_astype/r1/bgpd.conf | 12 + .../bgp_confederation_astype/r1/zebra.conf | 7 + .../bgp_confederation_astype/r2/bgpd.conf | 13 + .../bgp_confederation_astype/r2/zebra.conf | 4 + .../bgp_confederation_astype/r3/bgpd.conf | 10 + .../bgp_confederation_astype/r3/zebra.conf | 4 + .../test_bgp_confederation_astype.py | 140 + tests/topotests/bgp_default_afi_safi/__init__.py | 0 tests/topotests/bgp_default_afi_safi/r1/bgpd.conf | 3 + tests/topotests/bgp_default_afi_safi/r1/zebra.conf | 7 + tests/topotests/bgp_default_afi_safi/r2/bgpd.conf | 5 + tests/topotests/bgp_default_afi_safi/r2/zebra.conf | 6 + tests/topotests/bgp_default_afi_safi/r3/bgpd.conf | 5 + tests/topotests/bgp_default_afi_safi/r3/zebra.conf | 6 + tests/topotests/bgp_default_afi_safi/r4/bgpd.conf | 5 + tests/topotests/bgp_default_afi_safi/r4/zebra.conf | 6 + .../test_bgp-default-afi-safi.py | 145 + .../bgp_default_orginate_vrf.json | 325 + .../bgp_default_originate_2links.json | 136 + .../bgp_default_originate_topo1.json | 294 + .../test_bgp_default_originate_2links.py | 1808 ++++++ .../test_bgp_default_originate_topo1_1.py | 2527 ++++++++ .../test_bgp_default_originate_topo1_2.py | 2427 ++++++++ .../test_bgp_default_originate_topo1_3.py | 2528 ++++++++ .../test_default_orginate_vrf.py | 1377 +++++ .../test_default_originate_conditional_routemap.py | 2092 +++++++ .../bgp_default_originate_timer/__init__.py | 0 .../bgp_default_originate_timer/r1/bgpd.conf | 18 + .../bgp_default_originate_timer/r1/zebra.conf | 7 + .../bgp_default_originate_timer/r2/bgpd.conf | 6 + .../bgp_default_originate_timer/r2/zebra.conf | 4 + .../bgp_default_originate_timer/r3/bgpd.conf | 12 + .../bgp_default_originate_timer/r3/zebra.conf | 7 + .../test_bgp_default_originate_timer.py | 123 + .../bgp_default_originate_withdraw/__init__.py | 0 .../bgp_default_originate_withdraw/r1/bgpd.conf | 12 + .../bgp_default_originate_withdraw/r1/zebra.conf | 7 + .../bgp_default_originate_withdraw/r2/bgpd.conf | 9 + .../bgp_default_originate_withdraw/r2/zebra.conf | 4 + .../bgp_default_originate_withdraw/r3/bgpd.conf | 9 + .../bgp_default_originate_withdraw/r3/zebra.conf | 5 + .../test_bgp_default_originate_withdraw.py | 159 + tests/topotests/bgp_default_route/__init__.py | 0 tests/topotests/bgp_default_route/r1/bgpd.conf | 8 + tests/topotests/bgp_default_route/r1/zebra.conf | 9 + tests/topotests/bgp_default_route/r2/bgpd.conf | 5 + tests/topotests/bgp_default_route/r2/zebra.conf | 6 + .../test_bgp_default-originate.py | 102 + .../bgp_default_route_route_map_match/__init__.py | 0 .../bgp_default_route_route_map_match/r1/bgpd.conf | 17 + .../r1/zebra.conf | 9 + .../bgp_default_route_route_map_match/r2/bgpd.conf | 8 + .../r2/zebra.conf | 6 + .../test_bgp_default-originate_route-map_match.py | 97 + .../bgp_default_route_route_map_match2/__init__.py | 0 .../r1/bgpd.conf | 13 + .../r1/zebra.conf | 6 + .../r2/bgpd.conf | 8 + .../r2/zebra.conf | 9 + .../test_bgp_default-originate_route-map_match2.py | 121 + .../__init__.py | 0 .../r1/bgpd.conf | 19 + .../r1/zebra.conf | 9 + .../r2/bgpd.conf | 8 + .../r2/zebra.conf | 6 + ...st_bgp_default-originate_route-map_match_set.py | 107 + .../bgp_default_route_route_map_set/__init__.py | 0 .../bgp_default_route_route_map_set/r1/bgpd.conf | 12 + .../bgp_default_route_route_map_set/r1/zebra.conf | 9 + .../bgp_default_route_route_map_set/r2/bgpd.conf | 8 + .../bgp_default_route_route_map_set/r2/zebra.conf | 6 + .../test_bgp_default-originate_route-map_set.py | 99 + tests/topotests/bgp_disable_addpath_rx/__init__.py | 0 .../topotests/bgp_disable_addpath_rx/r1/bgpd.conf | 10 + .../topotests/bgp_disable_addpath_rx/r1/zebra.conf | 4 + .../topotests/bgp_disable_addpath_rx/r2/bgpd.conf | 13 + .../topotests/bgp_disable_addpath_rx/r2/zebra.conf | 7 + .../topotests/bgp_disable_addpath_rx/r3/bgpd.conf | 9 + .../topotests/bgp_disable_addpath_rx/r3/zebra.conf | 7 + .../topotests/bgp_disable_addpath_rx/r4/bgpd.conf | 9 + .../topotests/bgp_disable_addpath_rx/r4/zebra.conf | 7 + .../test_disable_addpath_rx.py | 129 + tests/topotests/bgp_distance_change/__init__.py | 0 .../bgp_distance_change/bgp_admin_dist.json | 402 ++ .../bgp_distance_change/bgp_admin_dist_vrf.json | 404 ++ tests/topotests/bgp_distance_change/r1/bgpd.conf | 6 + tests/topotests/bgp_distance_change/r1/zebra.conf | 6 + tests/topotests/bgp_distance_change/r2/bgpd.conf | 8 + tests/topotests/bgp_distance_change/r2/zebra.conf | 9 + .../bgp_distance_change/test_bgp_admin_dist.py | 1269 ++++ .../bgp_distance_change/test_bgp_admin_dist_vrf.py | 887 +++ .../test_bgp_distance_change.py | 120 + .../bgp_dont_capability_negotiate/__init__.py | 0 .../bgp_dont_capability_negotiate/r1/bgpd.conf | 12 + .../bgp_dont_capability_negotiate/r1/zebra.conf | 4 + .../bgp_dont_capability_negotiate/r2/bgpd.conf | 9 + .../bgp_dont_capability_negotiate/r2/zebra.conf | 7 + .../test_bgp_dont_capability_negotiate.py | 156 + tests/topotests/bgp_dynamic_capability/__init__.py | 0 tests/topotests/bgp_dynamic_capability/r1/frr.conf | 15 + tests/topotests/bgp_dynamic_capability/r2/frr.conf | 22 + ...test_bgp_dynamic_capability_graceful_restart.py | 213 + .../test_bgp_dynamic_capability_role.py | 138 + ...test_bgp_dynamic_capability_software_version.py | 161 + .../__init__.py | 0 .../r1/bgpd.conf | 7 + .../r1/zebra.conf | 7 + .../r2/bgpd.conf | 3 + .../r2/zebra.conf | 4 + .../r3/bgpd.conf | 5 + .../r3/zebra.conf | 4 + ...est_bgp-ebgp-common-subnet-nexthop-unchanged.py | 112 + .../topotests/bgp_ebgp_requires_policy/__init__.py | 0 .../bgp_ebgp_requires_policy/r1/bgpd.conf | 12 + .../bgp_ebgp_requires_policy/r1/zebra.conf | 9 + .../bgp_ebgp_requires_policy/r2/bgpd.conf | 5 + .../bgp_ebgp_requires_policy/r2/zebra.conf | 6 + .../bgp_ebgp_requires_policy/r3/bgpd.conf | 6 + .../bgp_ebgp_requires_policy/r3/zebra.conf | 9 + .../bgp_ebgp_requires_policy/r4/bgpd.conf | 5 + .../bgp_ebgp_requires_policy/r4/zebra.conf | 6 + .../bgp_ebgp_requires_policy/r5/bgpd.conf | 7 + .../bgp_ebgp_requires_policy/r5/zebra.conf | 9 + .../bgp_ebgp_requires_policy/r6/bgpd.conf | 4 + .../bgp_ebgp_requires_policy/r6/zebra.conf | 6 + .../test_bgp_ebgp_requires_policy.py | 158 + tests/topotests/bgp_ecmp_topo1/__init__.py | 0 tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.dot | 206 + tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.pdf | Bin 0 -> 21367 bytes tests/topotests/bgp_ecmp_topo1/exabgp.env | 54 + tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py | 66 + tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg | 21 + tests/topotests/bgp_ecmp_topo1/r1/bgpd.conf | 51 + tests/topotests/bgp_ecmp_topo1/r1/summary.txt | 131 + tests/topotests/bgp_ecmp_topo1/r1/summary20.txt | 129 + tests/topotests/bgp_ecmp_topo1/r1/zebra.conf | 16 + .../bgp_ecmp_topo1/test_bgp_ecmp_topo1.py | 170 + .../topotests/bgp_ecmp_topo2/ebgp_ecmp_topo2.json | 834 +++ .../topotests/bgp_ecmp_topo2/ibgp_ecmp_topo2.json | 844 +++ .../bgp_ecmp_topo2/test_ebgp_ecmp_topo2.py | 746 +++ .../bgp_ecmp_topo2/test_ibgp_ecmp_topo2.py | 747 +++ .../topotests/bgp_ecmp_topo3/ibgp_ecmp_topo3.json | 232 + .../bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py | 270 + tests/topotests/bgp_evpn_mh/evpn-mh-topo-tests.pdf | Bin 0 -> 90963 bytes tests/topotests/bgp_evpn_mh/hostd11/evpn.conf | 0 tests/topotests/bgp_evpn_mh/hostd11/pim.conf | 0 tests/topotests/bgp_evpn_mh/hostd11/zebra.conf | 0 tests/topotests/bgp_evpn_mh/hostd12/evpn.conf | 0 tests/topotests/bgp_evpn_mh/hostd12/pim.conf | 0 tests/topotests/bgp_evpn_mh/hostd12/zebra.conf | 0 tests/topotests/bgp_evpn_mh/hostd21/evpn.conf | 0 tests/topotests/bgp_evpn_mh/hostd21/pim.conf | 0 tests/topotests/bgp_evpn_mh/hostd21/zebra.conf | 0 tests/topotests/bgp_evpn_mh/hostd22/evpn.conf | 0 tests/topotests/bgp_evpn_mh/hostd22/pim.conf | 0 tests/topotests/bgp_evpn_mh/hostd22/zebra.conf | 0 tests/topotests/bgp_evpn_mh/leaf1/evpn.conf | 21 + tests/topotests/bgp_evpn_mh/leaf1/pim.conf | 26 + tests/topotests/bgp_evpn_mh/leaf1/zebra.conf | 30 + tests/topotests/bgp_evpn_mh/leaf2/evpn.conf | 21 + tests/topotests/bgp_evpn_mh/leaf2/pim.conf | 20 + tests/topotests/bgp_evpn_mh/leaf2/zebra.conf | 24 + tests/topotests/bgp_evpn_mh/spine1/evpn.conf | 13 + tests/topotests/bgp_evpn_mh/spine1/pim.conf | 18 + tests/topotests/bgp_evpn_mh/spine1/zebra.conf | 11 + tests/topotests/bgp_evpn_mh/spine2/evpn.conf | 13 + tests/topotests/bgp_evpn_mh/spine2/pim.conf | 13 + tests/topotests/bgp_evpn_mh/spine2/zebra.conf | 13 + tests/topotests/bgp_evpn_mh/test_evpn_mh.py | 819 +++ tests/topotests/bgp_evpn_mh/torm11/evpn.conf | 21 + tests/topotests/bgp_evpn_mh/torm11/pim.conf | 19 + tests/topotests/bgp_evpn_mh/torm11/zebra.conf | 27 + tests/topotests/bgp_evpn_mh/torm12/evpn.conf | 21 + tests/topotests/bgp_evpn_mh/torm12/pim.conf | 19 + tests/topotests/bgp_evpn_mh/torm12/zebra.conf | 28 + tests/topotests/bgp_evpn_mh/torm21/evpn.conf | 21 + tests/topotests/bgp_evpn_mh/torm21/pim.conf | 19 + tests/topotests/bgp_evpn_mh/torm21/zebra.conf | 29 + tests/topotests/bgp_evpn_mh/torm22/evpn.conf | 20 + tests/topotests/bgp_evpn_mh/torm22/pim.conf | 19 + tests/topotests/bgp_evpn_mh/torm22/zebra.conf | 29 + .../PE1/bgp_vni_routes_base.json | 192 + .../PE1/bgp_vni_routes_no_rt2.json | 8 + .../PE1/bgp_vni_routes_no_rt5.json | 192 + .../PE1/bgp_vrf_ipv4_base.json | 27 + .../PE1/bgp_vrf_ipv4_no_rt2.json | 27 + .../PE1/bgp_vrf_ipv4_no_rt5.json | 6 + .../PE1/bgp_vrf_ipv6_base.json | 27 + .../PE1/bgp_vrf_ipv6_no_rt2.json | 27 + .../PE1/bgp_vrf_ipv6_no_rt5.json | 6 + .../bgp_evpn_overlay_index_gateway/PE1/bgpd.conf | 30 + .../bgp_evpn_overlay_index_gateway/PE1/zebra.conf | 14 + .../PE1/zebra_vrf_ipv4_base.json | 56 + .../PE1/zebra_vrf_ipv4_no_rt2.json | 56 + .../PE1/zebra_vrf_ipv4_no_rt5.json | 29 + .../PE1/zebra_vrf_ipv6_base.json | 55 + .../PE1/zebra_vrf_ipv6_no_rt2.json | 55 + .../PE1/zebra_vrf_ipv6_no_rt5.json | 29 + .../PE2/bgp_vni_routes_base.json | 192 + .../PE2/bgp_vni_routes_no_rt2.json | 68 + .../PE2/bgp_vni_routes_no_rt5.json | 192 + .../PE2/bgp_vrf_ipv4_base.json | 27 + .../PE2/bgp_vrf_ipv4_no_rt2.json | 27 + .../PE2/bgp_vrf_ipv4_no_rt5.json | 6 + .../PE2/bgp_vrf_ipv6_base.json | 28 + .../PE2/bgp_vrf_ipv6_no_rt2.json | 28 + .../PE2/bgp_vrf_ipv6_no_rt5.json | 6 + .../bgp_evpn_overlay_index_gateway/PE2/bgpd.conf | 14 + .../bgp_evpn_overlay_index_gateway/PE2/zebra.conf | 14 + .../PE2/zebra_vrf_ipv4_base.json | 56 + .../PE2/zebra_vrf_ipv4_no_rt2.json | 29 + .../PE2/zebra_vrf_ipv4_no_rt5.json | 29 + .../PE2/zebra_vrf_ipv6_base.json | 56 + .../PE2/zebra_vrf_ipv6_no_rt2.json | 29 + .../PE2/zebra_vrf_ipv6_no_rt5.json | 29 + .../bgp_evpn_overlay_index_gateway/__init__.py | 0 .../bgp_evpn_overlay_index_gateway/host1/bgpd.conf | 18 + .../host1/zebra.conf | 4 + .../bgp_evpn_overlay_index_gateway/host2/bgpd.conf | 1 + .../host2/zebra.conf | 4 + .../test_bgp_evpn_overlay_index_gateway.py | 401 ++ tests/topotests/bgp_evpn_rt5/__init__.py | 0 tests/topotests/bgp_evpn_rt5/r1/bgpd.conf | 26 + tests/topotests/bgp_evpn_rt5/r1/zebra.conf | 22 + tests/topotests/bgp_evpn_rt5/r2/bgpd.conf | 27 + tests/topotests/bgp_evpn_rt5/r2/zebra.conf | 18 + tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py | 223 + .../bgp_evpn_vxlan_macvrf_soo_topo1/P1/bgpd.conf | 1 + .../bgp_evpn_vxlan_macvrf_soo_topo1/P1/ospfd.conf | 13 + .../bgp_evpn_vxlan_macvrf_soo_topo1/P1/zebra.conf | 7 + .../PE1/bgp.l2vpn.evpn.vni.json | 19 + .../bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgpd.conf | 18 + .../PE1/evpn.vni.json | 17 + .../bgp_evpn_vxlan_macvrf_soo_topo1/PE1/ospfd.conf | 9 + .../bgp_evpn_vxlan_macvrf_soo_topo1/PE1/zebra.conf | 8 + .../PE2/bgp.l2vpn.evpn.vni.json | 19 + .../bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgpd.conf | 18 + .../PE2/evpn.vni.json | 16 + .../bgp_evpn_vxlan_macvrf_soo_topo1/PE2/ospfd.conf | 9 + .../bgp_evpn_vxlan_macvrf_soo_topo1/PE2/zebra.conf | 6 + .../host1/bgpd.conf | 1 + .../host1/ospfd.conf | 1 + .../host1/zebra.conf | 3 + .../host2/bgpd.conf | 1 + .../host2/ospfd.conf | 1 + .../host2/zebra.conf | 3 + .../test_bgp_evpn_vxlan_macvrf_soo.py | 839 +++ .../bgp_evpn_vxlan_svd_topo1/P1/bgpd.conf | 1 + .../bgp_evpn_vxlan_svd_topo1/P1/ospfd.conf | 13 + .../bgp_evpn_vxlan_svd_topo1/P1/zebra.conf | 7 + .../bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf | 19 + .../bgp_evpn_vxlan_svd_topo1/PE1/evpn.vni.json | 16 + .../bgp_evpn_vxlan_svd_topo1/PE1/ospfd.conf | 9 + .../bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf | 13 + .../bgp_evpn_vxlan_svd_topo1/PE2/bgpd.conf | 24 + .../bgp_evpn_vxlan_svd_topo1/PE2/evpn.vni.json | 15 + .../bgp_evpn_vxlan_svd_topo1/PE2/ospfd.conf | 9 + .../bgp_evpn_vxlan_svd_topo1/PE2/zebra.conf | 17 + .../topotests/bgp_evpn_vxlan_svd_topo1/__init__.py | 0 .../bgp_evpn_vxlan_svd_topo1/host1/bgpd.conf | 1 + .../bgp_evpn_vxlan_svd_topo1/host1/ospfd.conf | 1 + .../bgp_evpn_vxlan_svd_topo1/host1/zebra.conf | 3 + .../bgp_evpn_vxlan_svd_topo1/host2/bgpd.conf | 1 + .../bgp_evpn_vxlan_svd_topo1/host2/ospfd.conf | 1 + .../bgp_evpn_vxlan_svd_topo1/host2/zebra.conf | 3 + .../test_bgp_evpn_vxlan_svd.py | 556 ++ tests/topotests/bgp_evpn_vxlan_topo1/P1/bgpd.conf | 1 + tests/topotests/bgp_evpn_vxlan_topo1/P1/ospfd.conf | 13 + tests/topotests/bgp_evpn_vxlan_topo1/P1/zebra.conf | 7 + tests/topotests/bgp_evpn_vxlan_topo1/PE1/bgpd.conf | 11 + .../bgp_evpn_vxlan_topo1/PE1/evpn.vni.json | 17 + .../topotests/bgp_evpn_vxlan_topo1/PE1/ospfd.conf | 9 + .../topotests/bgp_evpn_vxlan_topo1/PE1/zebra.conf | 8 + tests/topotests/bgp_evpn_vxlan_topo1/PE2/bgpd.conf | 12 + .../bgp_evpn_vxlan_topo1/PE2/evpn.vni.json | 16 + .../topotests/bgp_evpn_vxlan_topo1/PE2/ospfd.conf | 9 + .../topotests/bgp_evpn_vxlan_topo1/PE2/zebra.conf | 6 + tests/topotests/bgp_evpn_vxlan_topo1/__init__.py | 0 .../topotests/bgp_evpn_vxlan_topo1/host1/bgpd.conf | 1 + .../bgp_evpn_vxlan_topo1/host1/ospfd.conf | 1 + .../bgp_evpn_vxlan_topo1/host1/zebra.conf | 3 + .../topotests/bgp_evpn_vxlan_topo1/host2/bgpd.conf | 1 + .../bgp_evpn_vxlan_topo1/host2/ospfd.conf | 1 + .../bgp_evpn_vxlan_topo1/host2/zebra.conf | 3 + .../bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py | 436 ++ .../topotests/bgp_extcomm_list_delete/__init__.py | 0 .../topotests/bgp_extcomm_list_delete/r1/bgpd.conf | 20 + .../bgp_extcomm_list_delete/r1/zebra.conf | 6 + .../topotests/bgp_extcomm_list_delete/r2/bgpd.conf | 10 + .../bgp_extcomm_list_delete/r2/zebra.conf | 6 + .../test_bgp_extcomm-list_delete.py | 162 + .../__init__.py | 0 .../r1/bgpd.conf | 6 + .../r1/zebra.conf | 4 + .../r2/bgpd.conf | 8 + .../r2/zebra.conf | 7 + ...test_bgp_extended_optional_parameters_length.py | 93 + .../bgp_features/r1/bgp_delayopen_neighbor.json | 6 + .../r1/bgp_delayopen_summary_established.json | 11 + .../r1/bgp_delayopen_summary_shutdown.json | 11 + .../bgp_features/r1/bgp_shutdown_summary.json | 19 + tests/topotests/bgp_features/r1/bgp_summary.json | 27 + tests/topotests/bgp_features/r1/bgpd.conf | 41 + tests/topotests/bgp_features/r1/ip_route.json | 364 ++ .../topotests/bgp_features/r1/ip_route_norib.json | 281 + tests/topotests/bgp_features/r1/ospf6d.conf | 21 + tests/topotests/bgp_features/r1/ospf_neighbor.json | 16 + tests/topotests/bgp_features/r1/ospfd.conf | 26 + tests/topotests/bgp_features/r1/show_bgp.json | 350 ++ .../bgp_features/r1/show_bgp_metric_test.json | 57 + tests/topotests/bgp_features/r1/zebra.conf | 29 + .../bgp_features/r2/bgp_delayopen_neighbor.json | 6 + .../r2/bgp_delayopen_summary_connect.json | 11 + .../r2/bgp_delayopen_summary_established.json | 11 + .../r2/bgp_delayopen_summary_shutdown.json | 11 + .../bgp_features/r2/bgp_shutdown_summary.json | 19 + tests/topotests/bgp_features/r2/bgp_summary.json | 27 + tests/topotests/bgp_features/r2/bgpd.conf | 41 + tests/topotests/bgp_features/r2/ospf6d.conf | 21 + tests/topotests/bgp_features/r2/ospf_neighbor.json | 16 + tests/topotests/bgp_features/r2/ospfd.conf | 26 + tests/topotests/bgp_features/r2/show_bgp.json | 349 ++ .../bgp_features/r2/show_bgp_metric_test.json | 57 + tests/topotests/bgp_features/r2/zebra.conf | 28 + tests/topotests/bgp_features/r3/bgp_summary.json | 0 tests/topotests/bgp_features/r3/ospf6d.conf | 21 + tests/topotests/bgp_features/r3/ospf_neighbor.json | 16 + tests/topotests/bgp_features/r3/ospfd.conf | 26 + tests/topotests/bgp_features/r3/zebra.conf | 23 + .../r4/bgp_delayopen_summary_established.json | 11 + .../r4/bgp_delayopen_summary_shutdown.json | 11 + .../bgp_features/r4/bgp_shutdown_summary.json | 14 + tests/topotests/bgp_features/r4/bgp_summary.json | 18 + tests/topotests/bgp_features/r4/bgpd.conf | 34 + .../bgp_features/r4/show_bgp_metric_test.json | 27 + tests/topotests/bgp_features/r4/zebra.conf | 18 + .../bgp_features/r5/bgp_delayopen_neighbor.json | 6 + .../r5/bgp_delayopen_summary_connect.json | 11 + .../r5/bgp_delayopen_summary_established.json | 11 + .../r5/bgp_delayopen_summary_shutdown.json | 11 + tests/topotests/bgp_features/r5/bgp_summary.json | 19 + tests/topotests/bgp_features/r5/bgpd.conf | 34 + .../bgp_features/r5/show_bgp_metric_test.json | 27 + tests/topotests/bgp_features/r5/zebra.conf | 18 + tests/topotests/bgp_features/test_bgp_features.dot | 83 + tests/topotests/bgp_features/test_bgp_features.pdf | Bin 0 -> 20710 bytes tests/topotests/bgp_features/test_bgp_features.py | 1102 ++++ tests/topotests/bgp_flowspec/__init__.py | 0 tests/topotests/bgp_flowspec/exabgp.env | 54 + tests/topotests/bgp_flowspec/peer1/exabgp.cfg | 33 + tests/topotests/bgp_flowspec/r1/bgpd.conf | 19 + tests/topotests/bgp_flowspec/r1/summary.txt | 50 + tests/topotests/bgp_flowspec/r1/zebra.conf | 9 + .../bgp_flowspec/test_bgp_flowspec_topo.py | 193 + .../bgp_gr_topojson_topo1.json | 115 + .../test_bgp_gr_functionality_topo1-1.py | 1527 +++++ .../test_bgp_gr_functionality_topo1-2.py | 391 ++ .../test_bgp_gr_functionality_topo1-3.py | 2720 +++++++++ .../test_bgp_gr_functionality_topo1-4.py | 1685 ++++++ .../bgp_gr_topojson_topo2.json | 334 ++ .../test_bgp_gr_functionality_topo2-1.py | 1497 +++++ .../test_bgp_gr_functionality_topo2-2.py | 1194 ++++ .../test_bgp_gr_functionality_topo2-3.py | 1346 +++++ .../test_bgp_gr_functionality_topo2-4.py | 1009 ++++ .../bgp_gr_functionality_topo3.json | 222 + .../test_bgp_gr_functionality_topo3.py | 541 ++ tests/topotests/bgp_gr_notification/__init__.py | 0 tests/topotests/bgp_gr_notification/r1/bgpd.conf | 10 + tests/topotests/bgp_gr_notification/r1/zebra.conf | 9 + tests/topotests/bgp_gr_notification/r2/bgpd.conf | 11 + tests/topotests/bgp_gr_notification/r2/zebra.conf | 9 + .../test_bgp_gr_notification.py | 211 + .../bgp_gr_restart_retain_routes/__init__.py | 0 .../bgp_gr_restart_retain_routes/r1/bgpd.conf | 11 + .../bgp_gr_restart_retain_routes/r1/zebra.conf | 7 + .../bgp_gr_restart_retain_routes/r2/bgpd.conf | 8 + .../bgp_gr_restart_retain_routes/r2/zebra.conf | 5 + .../test_bgp_gr_restart_retain_routes.py | 109 + tests/topotests/bgp_gshut/__init__.py | 0 tests/topotests/bgp_gshut/r1/bgp_route_1.json | 12 + tests/topotests/bgp_gshut/r1/bgp_route_2.json | 17 + tests/topotests/bgp_gshut/r1/bgpd.conf | 10 + tests/topotests/bgp_gshut/r1/zebra.conf | 9 + tests/topotests/bgp_gshut/r2/bgp_sum_1.json | 15 + tests/topotests/bgp_gshut/r2/bgp_sum_2.json | 15 + tests/topotests/bgp_gshut/r2/bgpd.conf | 20 + tests/topotests/bgp_gshut/r2/zebra.conf | 13 + tests/topotests/bgp_gshut/r3/bgp_route_1.json | 9 + tests/topotests/bgp_gshut/r3/bgp_route_2.json | 16 + tests/topotests/bgp_gshut/r3/bgpd.conf | 11 + tests/topotests/bgp_gshut/r3/zebra.conf | 9 + tests/topotests/bgp_gshut/r4/bgpd.conf | 11 + tests/topotests/bgp_gshut/r4/zebra.conf | 9 + tests/topotests/bgp_gshut/r5/bgp_route_1.json | 9 + tests/topotests/bgp_gshut/r5/bgp_route_2.json | 16 + tests/topotests/bgp_gshut/r5/bgpd.conf | 7 + tests/topotests/bgp_gshut/r5/zebra.conf | 9 + tests/topotests/bgp_gshut/test_bgp_gshut.py | 343 ++ .../bgp_gshut_topo1/ebgp_gshut_topo1.json | 221 + .../bgp_gshut_topo1/ibgp_gshut_topo1.json | 221 + .../bgp_gshut_topo1/test_ebgp_gshut_topo1.py | 593 ++ .../bgp_gshut_topo1/test_ibgp_gshut_topo1.py | 640 ++ tests/topotests/bgp_instance_del_test/__init__.py | 0 tests/topotests/bgp_instance_del_test/ce1 | 1 + tests/topotests/bgp_instance_del_test/ce2 | 1 + tests/topotests/bgp_instance_del_test/ce3 | 1 + tests/topotests/bgp_instance_del_test/ce4 | 1 + tests/topotests/bgp_instance_del_test/customize.py | 1 + tests/topotests/bgp_instance_del_test/r1 | 1 + tests/topotests/bgp_instance_del_test/r2 | 1 + tests/topotests/bgp_instance_del_test/r3 | 1 + tests/topotests/bgp_instance_del_test/r4 | 1 + tests/topotests/bgp_instance_del_test/scripts | 1 + .../test_bgp_instance_del_test.py | 86 + tests/topotests/bgp_ipv4_class_e_peer/__init__.py | 0 tests/topotests/bgp_ipv4_class_e_peer/r1/bgpd.conf | 12 + .../topotests/bgp_ipv4_class_e_peer/r1/zebra.conf | 11 + tests/topotests/bgp_ipv4_class_e_peer/r2/bgpd.conf | 9 + .../topotests/bgp_ipv4_class_e_peer/r2/zebra.conf | 8 + .../test_bgp_ipv4_class_e_peer.py | 114 + .../bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json | 85 + .../bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json | 95 + .../rfc5549_ebgp_unnumbered_nbr.json | 97 + .../bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json | 95 + .../rfc5549_ibgp_unnumbered_nbr.json | 97 + .../test_rfc5549_ebgp_ibgp_nbr.py | 950 +++ .../bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py | 617 ++ .../test_rfc5549_ebgp_unnumbered_nbr.py | 766 +++ .../bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py | 975 +++ .../test_rfc5549_ibgp_unnumbered_nbr.py | 311 + tests/topotests/bgp_ipv6_ll_peering/__init__.py | 0 tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf | 6 + tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf | 4 + tests/topotests/bgp_ipv6_ll_peering/r2/bgpd.conf | 6 + tests/topotests/bgp_ipv6_ll_peering/r2/zebra.conf | 4 + .../test_bgp_ipv6_ll_peering.py | 88 + tests/topotests/bgp_ipv6_rtadv/__init__.py | 0 tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf | 15 + tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json | 40 + tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json | 34 + tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf | 9 + tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf | 18 + tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json | 40 + tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json | 21 + tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf | 9 + .../bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot | 44 + .../bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py | 135 + .../topotests/bgp_l3vpn_to_bgp_direct/__init__.py | 0 .../bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf | 32 + .../bgp_l3vpn_to_bgp_direct/ce1/zebra.conf | 17 + .../bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf | 32 + .../bgp_l3vpn_to_bgp_direct/ce2/zebra.conf | 17 + .../bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf | 32 + .../bgp_l3vpn_to_bgp_direct/ce3/zebra.conf | 17 + .../topotests/bgp_l3vpn_to_bgp_direct/customize.py | 133 + .../topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf | 42 + .../topotests/bgp_l3vpn_to_bgp_direct/r1/ldpd.conf | 23 + .../bgp_l3vpn_to_bgp_direct/r1/ospfd.conf | 12 + .../bgp_l3vpn_to_bgp_direct/r1/zebra.conf | 27 + .../topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf | 33 + .../topotests/bgp_l3vpn_to_bgp_direct/r2/ldpd.conf | 25 + .../bgp_l3vpn_to_bgp_direct/r2/ospfd.conf | 19 + .../bgp_l3vpn_to_bgp_direct/r2/zebra.conf | 31 + .../topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf | 41 + .../topotests/bgp_l3vpn_to_bgp_direct/r3/ldpd.conf | 23 + .../bgp_l3vpn_to_bgp_direct/r3/ospfd.conf | 17 + .../bgp_l3vpn_to_bgp_direct/r3/zebra.conf | 32 + .../topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf | 41 + .../topotests/bgp_l3vpn_to_bgp_direct/r4/ldpd.conf | 23 + .../bgp_l3vpn_to_bgp_direct/r4/ospfd.conf | 12 + .../bgp_l3vpn_to_bgp_direct/r4/zebra.conf | 26 + .../bgp_l3vpn_to_bgp_direct/scripts/add_routes.py | 193 + .../bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py | 71 + .../scripts/check_routes.py | 55 + .../bgp_l3vpn_to_bgp_direct/scripts/cleanup_all.py | 114 + .../test_bgp_l3vpn_to_bgp_direct.py | 64 + tests/topotests/bgp_l3vpn_to_bgp_vrf/__init__.py | 0 tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf | 55 + .../topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf | 0 .../topotests/bgp_l3vpn_to_bgp_vrf/ce1/zebra.conf | 17 + tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf | 55 + .../topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf | 0 .../topotests/bgp_l3vpn_to_bgp_vrf/ce2/zebra.conf | 17 + tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf | 45 + .../topotests/bgp_l3vpn_to_bgp_vrf/ce3/zebra.conf | 17 + tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf | 45 + .../topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf | 17 + tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py | 216 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf | 55 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ldpd.conf | 24 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf | 12 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf | 25 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf | 35 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ldpd.conf | 26 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf | 19 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/zebra.conf | 28 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf | 48 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ldpd.conf | 24 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf | 17 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/zebra.conf | 29 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf | 72 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ldpd.conf | 24 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf | 12 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/zebra.conf | 28 + .../bgp_l3vpn_to_bgp_vrf/scripts/add_routes.py | 59 + .../bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py | 64 + .../scripts/check_linux_mpls.py | 83 + .../scripts/check_linux_vrf.py | 74 + .../bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py | 878 +++ .../bgp_l3vpn_to_bgp_vrf/scripts/cleanup_all.py | 120 + .../scripts/del_bgp_instances.py | 30 + .../scripts/notification_check.py | 22 + .../bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py | 87 + .../bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py | 253 + .../test_bgp_l3vpn_to_bgp_vrf.py | 115 + .../bgp_labeled_unicast_addpath/__init__.py | 0 .../bgp_labeled_unicast_addpath/r1/bgpd.conf | 14 + .../bgp_labeled_unicast_addpath/r1/zebra.conf | 7 + .../bgp_labeled_unicast_addpath/r2/bgpd.conf | 14 + .../bgp_labeled_unicast_addpath/r2/zebra.conf | 7 + .../bgp_labeled_unicast_addpath/r3/bgpd.conf | 35 + .../bgp_labeled_unicast_addpath/r3/zebra.conf | 13 + .../bgp_labeled_unicast_addpath/r4/bgpd.conf | 10 + .../bgp_labeled_unicast_addpath/r4/zebra.conf | 4 + .../bgp_labeled_unicast_addpath/r5/bgpd.conf | 14 + .../bgp_labeled_unicast_addpath/r5/zebra.conf | 7 + .../test_bgp_labeled_unicast_addpath.py | 136 + .../__init__.py | 0 .../r1/bgpd.conf | 35 + .../r1/zebra.conf | 5 + .../r2/bgpd.conf | 19 + .../r2/zebra.conf | 5 + .../test_bgp_labeled_unicast_default_originate.py | 130 + .../bgp_large_comm_list_match/__init__.py | 0 .../bgp_large_comm_list_match/r1/bgpd.conf | 28 + .../bgp_large_comm_list_match/r1/zebra.conf | 12 + .../bgp_large_comm_list_match/r2/bgpd.conf | 24 + .../bgp_large_comm_list_match/r2/zebra.conf | 9 + .../bgp_large_comm_list_match/r3/bgpd.conf | 21 + .../bgp_large_comm_list_match/r3/zebra.conf | 6 + .../test_bgp_large_comm_list_match.py | 145 + tests/topotests/bgp_large_community/__init__.py | 0 .../bgp_large_community_topo_1.json | 262 + .../bgp_large_community_topo_2.json | 344 ++ .../test_bgp_large_community_topo_1.py | 1216 ++++ .../test_bgp_large_community_topo_2.py | 2220 +++++++ tests/topotests/bgp_link_bw_ip/__init__.py | 0 tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json | 19 + tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json | 32 + tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json | 32 + tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json | 32 + tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json | 29 + tests/topotests/bgp_link_bw_ip/r1/bgpd.conf | 12 + tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json | 20 + tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json | 20 + tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json | 20 + tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json | 20 + tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json | 20 + tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json | 15 + tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json | 15 + tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json | 20 + tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json | 20 + tests/topotests/bgp_link_bw_ip/r1/v4_route.json | 84 + tests/topotests/bgp_link_bw_ip/r1/zebra.conf | 7 + tests/topotests/bgp_link_bw_ip/r10/bgpd.conf | 17 + tests/topotests/bgp_link_bw_ip/r10/zebra.conf | 6 + tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json | 19 + tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json | 19 + tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json | 32 + tests/topotests/bgp_link_bw_ip/r2/bgpd.conf | 13 + tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json | 19 + tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json | 20 + tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json | 15 + tests/topotests/bgp_link_bw_ip/r2/zebra.conf | 10 + tests/topotests/bgp_link_bw_ip/r3/bgp-route-1.json | 29 + tests/topotests/bgp_link_bw_ip/r3/bgpd.conf | 12 + tests/topotests/bgp_link_bw_ip/r3/zebra.conf | 7 + tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json | 23 + tests/topotests/bgp_link_bw_ip/r4/bgpd.conf | 32 + tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json | 21 + tests/topotests/bgp_link_bw_ip/r4/zebra.conf | 10 + tests/topotests/bgp_link_bw_ip/r5/bgpd.conf | 23 + tests/topotests/bgp_link_bw_ip/r5/zebra.conf | 7 + tests/topotests/bgp_link_bw_ip/r6/bgpd.conf | 24 + tests/topotests/bgp_link_bw_ip/r6/zebra.conf | 7 + tests/topotests/bgp_link_bw_ip/r7/bgpd.conf | 17 + tests/topotests/bgp_link_bw_ip/r7/zebra.conf | 6 + tests/topotests/bgp_link_bw_ip/r8/bgpd.conf | 17 + tests/topotests/bgp_link_bw_ip/r8/zebra.conf | 6 + tests/topotests/bgp_link_bw_ip/r9/bgpd.conf | 17 + tests/topotests/bgp_link_bw_ip/r9/zebra.conf | 6 + .../topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py | 575 ++ .../bgp_listen_on_multiple_addresses.json | 154 + .../test_bgp_listen_on_multiple_addresses.py | 136 + tests/topotests/bgp_llgr/__init__.py | 0 tests/topotests/bgp_llgr/r0/bgpd.conf | 12 + tests/topotests/bgp_llgr/r0/zebra.conf | 7 + tests/topotests/bgp_llgr/r1/bgpd.conf | 12 + tests/topotests/bgp_llgr/r1/zebra.conf | 7 + tests/topotests/bgp_llgr/r2/bgpd.conf | 18 + tests/topotests/bgp_llgr/r2/zebra.conf | 13 + tests/topotests/bgp_llgr/r3/bgpd.conf | 8 + tests/topotests/bgp_llgr/r3/zebra.conf | 4 + tests/topotests/bgp_llgr/r4/bgpd.conf | 13 + tests/topotests/bgp_llgr/r4/zebra.conf | 7 + tests/topotests/bgp_llgr/test_bgp_llgr.py | 185 + tests/topotests/bgp_local_as/__init__.py | 0 tests/topotests/bgp_local_as/r1/bgpd.conf | 14 + tests/topotests/bgp_local_as/r1/zebra.conf | 10 + tests/topotests/bgp_local_as/r2/bgpd.conf | 6 + tests/topotests/bgp_local_as/r2/zebra.conf | 4 + tests/topotests/bgp_local_as/r3/bgpd.conf | 6 + tests/topotests/bgp_local_as/r3/zebra.conf | 4 + tests/topotests/bgp_local_as/test_bgp_local_as.py | 121 + .../__init__.py | 0 .../r1/bgpd.conf | 8 + .../r1/zebra.conf | 9 + .../r2/bgpd.conf | 5 + .../r2/zebra.conf | 6 + .../r3/bgpd.conf | 8 + .../r3/zebra.conf | 9 + .../r4/bgpd.conf | 5 + .../r4/zebra.conf | 6 + .../test_bgp_local_as_dotplus_private_remove.py | 129 + .../bgp_local_as_private_remove/__init__.py | 0 .../bgp_local_as_private_remove/r1/bgpd.conf | 8 + .../bgp_local_as_private_remove/r1/zebra.conf | 9 + .../bgp_local_as_private_remove/r2/bgpd.conf | 5 + .../bgp_local_as_private_remove/r2/zebra.conf | 6 + .../bgp_local_as_private_remove/r3/bgpd.conf | 8 + .../bgp_local_as_private_remove/r3/zebra.conf | 9 + .../bgp_local_as_private_remove/r4/bgpd.conf | 5 + .../bgp_local_as_private_remove/r4/zebra.conf | 6 + .../test_bgp_local_as_private_remove.py | 116 + .../topotests/bgp_local_asn/bgp_local_asn_agg.json | 147 + .../bgp_local_asn/bgp_local_asn_ecmp.json | 317 + .../bgp_local_asn/bgp_local_asn_topo1.json | 132 + .../bgp_local_asn/bgp_local_asn_topo2.json | 117 + .../bgp_local_asn/bgp_local_asn_vrf_topo1.json | 152 + .../bgp_local_asn/bgp_local_asn_vrf_topo2.json | 128 + .../bgp_local_asn/test_bgp_local_asn_agg.py | 407 ++ .../bgp_local_asn/test_bgp_local_asn_ecmp.py | 511 ++ .../bgp_local_asn/test_bgp_local_asn_topo1.py | 3642 ++++++++++++ .../bgp_local_asn/test_bgp_local_asn_topo2.py | 686 +++ .../bgp_local_asn/test_bgp_local_asn_vrf_topo1.py | 1095 ++++ .../bgp_local_asn/test_bgp_local_asn_vrf_topo2.py | 813 +++ .../bgp_local_asn_dot/bgp_local_asn_dot_agg.json | 147 + .../bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json | 317 + .../bgp_local_asn_dot/bgp_local_asn_dot_topo1.json | 132 + .../test_bgp_local_asn_dot_agg.py | 420 ++ .../test_bgp_local_asn_dot_ecmp.py | 524 ++ .../test_bgp_local_asn_dot_topo1.py | 3655 ++++++++++++ tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf | 15 + tests/topotests/bgp_lu_explicitnull/r1/zebra.conf | 6 + tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf | 15 + tests/topotests/bgp_lu_explicitnull/r2/zebra.conf | 6 + .../test_bgp_lu_explicitnull.py | 196 + tests/topotests/bgp_lu_topo1/R1/bgpd.conf | 21 + .../topotests/bgp_lu_topo1/R1/labelpool.summ.json | 6 + tests/topotests/bgp_lu_topo1/R1/zebra.conf | 6 + tests/topotests/bgp_lu_topo1/R2/bgpd.conf | 23 + .../topotests/bgp_lu_topo1/R2/labelpool.summ.json | 6 + tests/topotests/bgp_lu_topo1/R2/zebra.conf | 11 + tests/topotests/bgp_lu_topo1/R3/bgpd.conf | 523 ++ tests/topotests/bgp_lu_topo1/R3/zebra.conf | 9 + tests/topotests/bgp_lu_topo1/test_bgp_lu.py | 168 + tests/topotests/bgp_lu_topo2/R1/bgpd.conf | 29 + .../topotests/bgp_lu_topo2/R1/labelpool.summ.json | 6 + tests/topotests/bgp_lu_topo2/R1/zebra.conf | 13 + tests/topotests/bgp_lu_topo2/R2/bgpd.conf | 27 + .../topotests/bgp_lu_topo2/R2/labelpool.summ.json | 6 + tests/topotests/bgp_lu_topo2/R2/zebra.conf | 14 + tests/topotests/bgp_lu_topo2/R3/bgpd.conf | 70 + tests/topotests/bgp_lu_topo2/R3/staticd.conf | 5 + tests/topotests/bgp_lu_topo2/R3/zebra.conf | 11 + tests/topotests/bgp_lu_topo2/R4/bgpd.conf | 23 + tests/topotests/bgp_lu_topo2/R4/zebra.conf | 9 + tests/topotests/bgp_lu_topo2/test_bgp_lu2.py | 208 + tests/topotests/bgp_max_med_on_startup/__init__.py | 0 .../topotests/bgp_max_med_on_startup/r1/bgpd.conf | 11 + .../topotests/bgp_max_med_on_startup/r1/zebra.conf | 9 + .../topotests/bgp_max_med_on_startup/r2/bgpd.conf | 7 + .../topotests/bgp_max_med_on_startup/r2/zebra.conf | 6 + .../test_bgp_max_med_on_startup.py | 101 + .../bgp_maximum_prefix_invalid_update/__init__.py | 0 .../bgp_maximum_prefix_invalid_update/r1/bgpd.conf | 13 + .../r1/zebra.conf | 10 + .../bgp_maximum_prefix_invalid_update/r2/bgpd.conf | 9 + .../r2/zebra.conf | 6 + .../test_bgp_maximum_prefix_invalid_update.py | 94 + tests/topotests/bgp_maximum_prefix_out/__init__.py | 0 .../topotests/bgp_maximum_prefix_out/r1/bgpd.conf | 11 + .../topotests/bgp_maximum_prefix_out/r1/zebra.conf | 13 + .../topotests/bgp_maximum_prefix_out/r2/bgpd.conf | 8 + .../topotests/bgp_maximum_prefix_out/r2/zebra.conf | 6 + .../test_bgp_maximum_prefix_out.py | 187 + tests/topotests/bgp_minimum_holdtime/__init__.py | 0 tests/topotests/bgp_minimum_holdtime/r1/bgpd.conf | 6 + tests/topotests/bgp_minimum_holdtime/r1/zebra.conf | 6 + tests/topotests/bgp_minimum_holdtime/r2/bgpd.conf | 5 + tests/topotests/bgp_minimum_holdtime/r2/zebra.conf | 6 + .../test_bgp_minimum_holdtime.py | 85 + .../bgp_multi_vrf_topo1/bgp_multi_vrf_topo1.json | 884 +++ .../test_bgp_multi_vrf_topo1.py | 6277 ++++++++++++++++++++ .../bgp_multi_vrf_topo2/bgp_multi_vrf_topo2.json | 1553 +++++ .../test_bgp_multi_vrf_topo2.py | 3839 ++++++++++++ tests/topotests/bgp_multiview_topo1/README.md | 125 + tests/topotests/bgp_multiview_topo1/exabgp.env | 54 + .../bgp_multiview_topo1/peer1/exa-send.py | 31 + .../topotests/bgp_multiview_topo1/peer1/exabgp.cfg | 21 + .../bgp_multiview_topo1/peer2/exa-send.py | 31 + .../topotests/bgp_multiview_topo1/peer2/exabgp.cfg | 21 + .../bgp_multiview_topo1/peer3/exa-send.py | 31 + .../topotests/bgp_multiview_topo1/peer3/exabgp.cfg | 21 + .../bgp_multiview_topo1/peer4/exa-send.py | 31 + .../topotests/bgp_multiview_topo1/peer4/exabgp.cfg | 21 + .../bgp_multiview_topo1/peer5/exa-send.py | 31 + .../topotests/bgp_multiview_topo1/peer5/exabgp.cfg | 21 + .../bgp_multiview_topo1/peer6/exa-send.py | 31 + .../topotests/bgp_multiview_topo1/peer6/exabgp.cfg | 21 + .../bgp_multiview_topo1/peer7/exa-send.py | 31 + .../topotests/bgp_multiview_topo1/peer7/exabgp.cfg | 21 + .../bgp_multiview_topo1/peer8/exa-send.py | 31 + .../topotests/bgp_multiview_topo1/peer8/exabgp.cfg | 21 + tests/topotests/bgp_multiview_topo1/r1/bgpd.conf | 61 + tests/topotests/bgp_multiview_topo1/r1/view_1.json | 728 +++ tests/topotests/bgp_multiview_topo1/r1/view_2.json | 489 ++ tests/topotests/bgp_multiview_topo1/r1/view_3.json | 728 +++ tests/topotests/bgp_multiview_topo1/r1/zebra.conf | 23 + .../test_bgp_multiview_topo1.py | 225 + .../bgp_node_target_extcommunities/__init__.py | 0 .../bgp_node_target_extcommunities/r1/frr.conf | 21 + .../bgp_node_target_extcommunities/r2/frr.conf | 8 + .../bgp_node_target_extcommunities/r3/frr.conf | 8 + .../bgp_node_target_extcommunities/r4/frr.conf | 8 + .../test_bgp_node_target_extcommunities.py | 132 + tests/topotests/bgp_orf/__init__.py | 0 tests/topotests/bgp_orf/r1/bgpd.conf | 9 + tests/topotests/bgp_orf/r1/zebra.conf | 8 + tests/topotests/bgp_orf/r2/bgpd.conf | 9 + tests/topotests/bgp_orf/r2/zebra.conf | 4 + tests/topotests/bgp_orf/test_bgp_orf.py | 146 + .../bgp_path_attribute_discard/__init__.py | 0 .../bgp_path_attribute_discard/exabgp.env | 53 + .../bgp_path_attribute_discard/peer1/exabgp.cfg | 24 + .../bgp_path_attribute_discard/r1/bgpd.conf | 6 + .../bgp_path_attribute_discard/r1/zebra.conf | 4 + .../test_bgp_path_attribute_discard.py | 154 + .../__init__.py | 0 .../r1/bgpd.conf | 14 + .../r1/zebra.conf | 4 + .../r2/bgpd.conf | 6 + .../r2/zebra.conf | 4 + .../test_bgp_path_attribute_treat_as_withdraw.py | 149 + .../bgp_path_attributes_topo1/__init__.py | 0 .../bgp_path_attributes.json | 363 ++ .../test_bgp_path_attributes.py | 1528 +++++ tests/topotests/bgp_path_selection/__init__.py | 0 tests/topotests/bgp_path_selection/r1/bgpd.conf | 28 + tests/topotests/bgp_path_selection/r1/ldpd.conf | 26 + tests/topotests/bgp_path_selection/r1/staticd.conf | 2 + tests/topotests/bgp_path_selection/r1/zebra.conf | 11 + tests/topotests/bgp_path_selection/r2/bgpd.conf | 25 + tests/topotests/bgp_path_selection/r2/ldpd.conf | 26 + tests/topotests/bgp_path_selection/r2/staticd.conf | 0 tests/topotests/bgp_path_selection/r2/zebra.conf | 7 + tests/topotests/bgp_path_selection/r3/bgpd.conf | 25 + tests/topotests/bgp_path_selection/r3/ldpd.conf | 24 + tests/topotests/bgp_path_selection/r3/staticd.conf | 0 tests/topotests/bgp_path_selection/r3/zebra.conf | 7 + .../bgp_path_selection/test_bgp_path_selection.py | 220 + .../bgp_peer_graceful_shutdown/__init__.py | 0 .../bgp_peer_graceful_shutdown/r1/bgpd.conf | 8 + .../bgp_peer_graceful_shutdown/r1/zebra.conf | 7 + .../bgp_peer_graceful_shutdown/r2/bgpd.conf | 8 + .../bgp_peer_graceful_shutdown/r2/zebra.conf | 7 + .../bgp_peer_graceful_shutdown/r3/bgpd.conf | 5 + .../bgp_peer_graceful_shutdown/r3/zebra.conf | 4 + .../test_bgp_peer_graceful_shutdown.py | 107 + tests/topotests/bgp_peer_group/__init__.py | 0 tests/topotests/bgp_peer_group/r1/bgpd.conf | 8 + tests/topotests/bgp_peer_group/r1/zebra.conf | 6 + tests/topotests/bgp_peer_group/r2/bgpd.conf | 7 + tests/topotests/bgp_peer_group/r2/zebra.conf | 6 + tests/topotests/bgp_peer_group/r3/bgpd.conf | 11 + tests/topotests/bgp_peer_group/r3/zebra.conf | 6 + .../bgp_peer_group/test_bgp_peer-group.py | 101 + .../bgp_peer_type_multipath_relax/exabgp.env | 53 + .../peer1/exa_readpipe.py | 19 + .../bgp_peer_type_multipath_relax/peer1/exabgp.cfg | 21 + .../peer2/exa_readpipe.py | 19 + .../bgp_peer_type_multipath_relax/peer2/exabgp.cfg | 21 + .../peer3/exa_readpipe.py | 19 + .../bgp_peer_type_multipath_relax/peer3/exabgp.cfg | 21 + .../peer4/exa_readpipe.py | 19 + .../bgp_peer_type_multipath_relax/peer4/exabgp.cfg | 21 + .../bgp_peer_type_multipath_relax/r1/bgpd.conf | 16 + .../r1/multipath.json | 50 + .../r1/not-multipath.json | 50 + .../r1/prefix1-eBGP-confed.json | 33 + .../r1/prefix1-eBGP-iBGP.json | 33 + .../r1/prefix1-no-recursive.json | 35 + .../r1/prefix1-recursive.json | 36 + .../bgp_peer_type_multipath_relax/r1/prefix1.json | 33 + .../r1/prefix3-ip-route.json | 23 + .../r1/prefix3-no-recursive.json | 21 + .../r1/prefix3-recursive.json | 23 + .../bgp_peer_type_multipath_relax/r1/zebra.conf | 27 + .../bgp_peer_type_multipath_relax/r2/bgpd.conf | 19 + .../bgp_peer_type_multipath_relax/r2/staticd.conf | 4 + .../bgp_peer_type_multipath_relax/r2/zebra.conf | 19 + .../test_bgp_peer-type_multipath-relax.py | 372 ++ tests/topotests/bgp_prefix_list_any/__init__.py | 0 tests/topotests/bgp_prefix_list_any/r1/bgpd.conf | 15 + tests/topotests/bgp_prefix_list_any/r1/zebra.conf | 5 + tests/topotests/bgp_prefix_list_any/r2/bgpd.conf | 50 + tests/topotests/bgp_prefix_list_any/r2/zebra.conf | 5 + .../test_bgp_prefix_list_any.py | 105 + tests/topotests/bgp_prefix_list_topo1/__init__.py | 0 .../bgp_prefix_list_topo1/prefix_lists.json | 123 + .../bgp_prefix_list_topo1/prefix_modify.json | 120 + .../bgp_prefix_list_topo1/test_prefix_lists.py | 1341 +++++ .../bgp_prefix_list_topo1/test_prefix_modify.py | 404 ++ tests/topotests/bgp_prefix_sid/__init__.py | 0 tests/topotests/bgp_prefix_sid/exabgp.env | 53 + tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg | 103 + tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg | 19 + tests/topotests/bgp_prefix_sid/r1/bgpd.conf | 17 + tests/topotests/bgp_prefix_sid/r1/zebra.conf | 7 + .../bgp_prefix_sid/test_bgp_prefix_sid.py | 172 + tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg | 29 + tests/topotests/bgp_prefix_sid2/peer1/exabgp.env | 53 + tests/topotests/bgp_prefix_sid2/r1/bgpd.conf | 26 + .../bgp_prefix_sid2/r1/vpnv6_rib_entry1.json | 50 + .../bgp_prefix_sid2/r1/vpnv6_rib_entry2.json | 50 + tests/topotests/bgp_prefix_sid2/r1/zebra.conf | 7 + .../bgp_prefix_sid2/test_bgp_prefix_sid2.py | 103 + .../bgp_recursive_route_ebgp_multi_hop.json | 321 + .../test_bgp_recursive_route_ebgp_multi_hop.py | 2407 ++++++++ tests/topotests/bgp_reject_as_sets/__init__.py | 0 tests/topotests/bgp_reject_as_sets/r1/bgpd.conf | 10 + tests/topotests/bgp_reject_as_sets/r1/zebra.conf | 9 + tests/topotests/bgp_reject_as_sets/r2/bgpd.conf | 13 + tests/topotests/bgp_reject_as_sets/r2/zebra.conf | 9 + tests/topotests/bgp_reject_as_sets/r3/bgpd.conf | 11 + tests/topotests/bgp_reject_as_sets/r3/zebra.conf | 9 + .../bgp_reject_as_sets/test_bgp_reject_as_sets.py | 138 + tests/topotests/bgp_remove_private_as/r1/bgpd.conf | 53 + .../topotests/bgp_remove_private_as/r1/zebra.conf | 10 + tests/topotests/bgp_remove_private_as/r2/bgpd.conf | 22 + .../topotests/bgp_remove_private_as/r2/zebra.conf | 14 + tests/topotests/bgp_remove_private_as/r3/bgpd.conf | 19 + .../topotests/bgp_remove_private_as/r3/zebra.conf | 10 + tests/topotests/bgp_remove_private_as/r4/bgpd.conf | 19 + .../topotests/bgp_remove_private_as/r4/zebra.conf | 10 + tests/topotests/bgp_remove_private_as/r5/bgpd.conf | 22 + .../topotests/bgp_remove_private_as/r5/zebra.conf | 14 + .../test_bgp_remove_private_as.py | 415 ++ .../bgp_remove_private_as_route_map/__init__.py | 0 .../bgp_remove_private_as_route_map/r1/frr.conf | 10 + .../bgp_remove_private_as_route_map/r2/frr.conf | 19 + .../test_bgp_remove_private_as_route_map.py | 90 + tests/topotests/bgp_rfapi_basic_sanity/__init__.py | 0 .../topotests/bgp_rfapi_basic_sanity/customize.py | 102 + .../topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf | 50 + .../topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf | 12 + .../topotests/bgp_rfapi_basic_sanity/r1/zebra.conf | 24 + .../topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf | 33 + .../topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf | 19 + .../topotests/bgp_rfapi_basic_sanity/r2/zebra.conf | 27 + .../topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf | 48 + .../topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf | 17 + .../topotests/bgp_rfapi_basic_sanity/r3/zebra.conf | 29 + .../topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf | 49 + .../topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf | 12 + .../topotests/bgp_rfapi_basic_sanity/r4/zebra.conf | 23 + .../bgp_rfapi_basic_sanity/scripts/add_routes.py | 159 + .../bgp_rfapi_basic_sanity/scripts/adjacencies.py | 50 + .../bgp_rfapi_basic_sanity/scripts/check_close.py | 102 + .../bgp_rfapi_basic_sanity/scripts/check_routes.py | 74 + .../scripts/check_timeout.py | 325 + .../bgp_rfapi_basic_sanity/scripts/cleanup_all.py | 124 + .../test_bgp_rfapi_basic_sanity.py | 84 + .../bgp_rfapi_basic_sanity_config2/__init__.py | 0 .../bgp_rfapi_basic_sanity_config2/customize.py | 1 + .../bgp_rfapi_basic_sanity_config2/r1/bgpd.conf | 51 + .../bgp_rfapi_basic_sanity_config2/r1/ospfd.conf | 1 + .../bgp_rfapi_basic_sanity_config2/r1/zebra.conf | 1 + .../bgp_rfapi_basic_sanity_config2/r2/bgpd.conf | 33 + .../bgp_rfapi_basic_sanity_config2/r2/ospfd.conf | 1 + .../bgp_rfapi_basic_sanity_config2/r2/zebra.conf | 1 + .../bgp_rfapi_basic_sanity_config2/r3/bgpd.conf | 49 + .../bgp_rfapi_basic_sanity_config2/r3/ospfd.conf | 1 + .../bgp_rfapi_basic_sanity_config2/r3/zebra.conf | 1 + .../bgp_rfapi_basic_sanity_config2/r4/bgpd.conf | 50 + .../bgp_rfapi_basic_sanity_config2/r4/ospfd.conf | 1 + .../bgp_rfapi_basic_sanity_config2/r4/zebra.conf | 1 + .../bgp_rfapi_basic_sanity_config2/scripts | 1 + .../test_bgp_rfapi_basic_sanity_config2.py | 1 + .../bgp_rmap_extcommunity_none/__init__.py | 0 .../bgp_rmap_extcommunity_none/r1/bgpd.conf | 8 + .../bgp_rmap_extcommunity_none/r1/zebra.conf | 4 + .../bgp_rmap_extcommunity_none/r2/bgpd.conf | 12 + .../bgp_rmap_extcommunity_none/r2/zebra.conf | 7 + .../test_bgp_rmap_extcommunity_none.py | 122 + tests/topotests/bgp_roles_capability/__init__.py | 0 tests/topotests/bgp_roles_capability/r1/bgpd.conf | 25 + tests/topotests/bgp_roles_capability/r1/zebra.conf | 18 + tests/topotests/bgp_roles_capability/r2/bgpd.conf | 5 + tests/topotests/bgp_roles_capability/r2/zebra.conf | 6 + tests/topotests/bgp_roles_capability/r3/bgpd.conf | 4 + tests/topotests/bgp_roles_capability/r3/zebra.conf | 6 + tests/topotests/bgp_roles_capability/r4/bgpd.conf | 3 + tests/topotests/bgp_roles_capability/r4/zebra.conf | 6 + tests/topotests/bgp_roles_capability/r5/bgpd.conf | 3 + tests/topotests/bgp_roles_capability/r5/zebra.conf | 6 + tests/topotests/bgp_roles_capability/r6/bgpd.conf | 4 + tests/topotests/bgp_roles_capability/r6/zebra.conf | 6 + .../roles_capability_stand.dot | 15 + .../roles_capability_stand.jpg | Bin 0 -> 22695 bytes .../test_bgp_roles_capability.py | 172 + tests/topotests/bgp_roles_filtering/__init__.py | 0 tests/topotests/bgp_roles_filtering/r1/bgpd.conf | 12 + tests/topotests/bgp_roles_filtering/r1/zebra.conf | 6 + tests/topotests/bgp_roles_filtering/r10/bgpd.conf | 21 + tests/topotests/bgp_roles_filtering/r10/zebra.conf | 24 + tests/topotests/bgp_roles_filtering/r2/bgpd.conf | 12 + tests/topotests/bgp_roles_filtering/r2/zebra.conf | 6 + tests/topotests/bgp_roles_filtering/r3/bgpd.conf | 12 + tests/topotests/bgp_roles_filtering/r3/zebra.conf | 6 + tests/topotests/bgp_roles_filtering/r4/bgpd.conf | 11 + tests/topotests/bgp_roles_filtering/r4/zebra.conf | 6 + tests/topotests/bgp_roles_filtering/r5/bgpd.conf | 11 + tests/topotests/bgp_roles_filtering/r5/zebra.conf | 6 + tests/topotests/bgp_roles_filtering/r6/bgpd.conf | 11 + tests/topotests/bgp_roles_filtering/r6/zebra.conf | 6 + tests/topotests/bgp_roles_filtering/r7/bgpd.conf | 11 + tests/topotests/bgp_roles_filtering/r7/zebra.conf | 6 + .../bgp_roles_filtering/roles_filtering_stand.dot | 21 + .../bgp_roles_filtering/roles_filtering_stand.jpg | Bin 0 -> 54044 bytes .../test_bgp_roles_filtering.py | 131 + .../bgp_route_aggregation/bgp_aggregation.json | 249 + .../bgp_route_aggregation/test_bgp_aggregation.py | 1160 ++++ .../bgp_route_map/bgp_route_map_topo1.json | 187 + .../bgp_route_map/bgp_route_map_topo2.json | 316 + .../bgp_route_map/test_route_map_topo1.py | 1393 +++++ .../bgp_route_map/test_route_map_topo2.py | 3958 ++++++++++++ .../bgp_route_map_delay_timer/__init__.py | 0 .../bgp_route_map_delay_timer/r1/bgpd.conf | 24 + .../bgp_route_map_delay_timer/r1/zebra.conf | 4 + .../bgp_route_map_delay_timer/r2/bgpd.conf | 4 + .../bgp_route_map_delay_timer/r2/zebra.conf | 4 + .../test_bgp_route_map_delay_timer.py | 120 + .../bgp_route_map_match_ipv6_nexthop/__init__.py | 0 .../bgp_route_map_match_ipv6_nexthop/r1/bgpd.conf | 34 + .../bgp_route_map_match_ipv6_nexthop/r1/zebra.conf | 4 + .../bgp_route_map_match_ipv6_nexthop/r2/bgpd.conf | 35 + .../bgp_route_map_match_ipv6_nexthop/r2/zebra.conf | 11 + .../test_bgp_route_map_match_ipv6_nexthop.py | 108 + .../__init__.py | 0 .../r1/frr.conf | 32 + .../r2/frr.conf | 10 + .../r3/frr.conf | 10 + .../test_bgp_route_map_match_source_protocol.py | 115 + .../bgp_route_map_on_match_next/__init__.py | 0 .../bgp_route_map_on_match_next/r1/bgpd.conf | 10 + .../bgp_route_map_on_match_next/r1/zebra.conf | 6 + .../bgp_route_map_on_match_next/r2/bgpd.conf | 17 + .../bgp_route_map_on_match_next/r2/zebra.conf | 6 + .../test_bgp_route_map_on_match_next.py | 111 + .../topotests/bgp_route_map_vpn_import/__init__.py | 0 .../bgp_route_map_vpn_import/r1/bgpd.conf | 46 + .../bgp_route_map_vpn_import/r1/zebra.conf | 16 + .../test_bgp_route_map_vpn_import.py | 113 + .../bgp_route_origin_parser/pe1/bgpd.conf | 2 + .../test_bgp_route_origin_parser.py | 129 + .../topotests/bgp_route_server_client/__init__.py | 0 .../topotests/bgp_route_server_client/r1/bgpd.conf | 12 + .../bgp_route_server_client/r1/zebra.conf | 7 + .../topotests/bgp_route_server_client/r2/bgpd.conf | 17 + .../bgp_route_server_client/r2/zebra.conf | 7 + .../topotests/bgp_route_server_client/r3/bgpd.conf | 12 + .../bgp_route_server_client/r3/zebra.conf | 7 + .../test_bgp_route_server_client.py | 130 + tests/topotests/bgp_rr_ibgp/spine1/bgpd.conf | 11 + .../bgp_rr_ibgp/spine1/show_ip_route.json_ref | 144 + tests/topotests/bgp_rr_ibgp/spine1/zebra.conf | 9 + .../bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py | 177 + tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf | 6 + .../bgp_rr_ibgp/tor1/show_ip_route.json_ref | 157 + tests/topotests/bgp_rr_ibgp/tor1/zebra.conf | 12 + tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf | 6 + .../bgp_rr_ibgp/tor2/show_ip_route.json_ref | 157 + tests/topotests/bgp_rr_ibgp/tor2/zebra.conf | 13 + .../bgp_sender_as_path_loop_detection/__init__.py | 0 .../bgp_sender_as_path_loop_detection/r1/bgpd.conf | 19 + .../r1/zebra.conf | 10 + .../bgp_sender_as_path_loop_detection/r2/bgpd.conf | 10 + .../r2/zebra.conf | 9 + .../bgp_sender_as_path_loop_detection/r3/bgpd.conf | 6 + .../r3/zebra.conf | 6 + .../test_bgp_sender-as-path-loop-detection.py | 134 + tests/topotests/bgp_set_aspath_exclude/__init__.py | 0 .../topotests/bgp_set_aspath_exclude/r1/bgpd.conf | 17 + .../topotests/bgp_set_aspath_exclude/r1/zebra.conf | 6 + .../topotests/bgp_set_aspath_exclude/r2/bgpd.conf | 8 + .../topotests/bgp_set_aspath_exclude/r2/zebra.conf | 9 + .../topotests/bgp_set_aspath_exclude/r3/bgpd.conf | 9 + .../topotests/bgp_set_aspath_exclude/r3/zebra.conf | 10 + .../test_bgp_set_aspath_exclude.py | 145 + tests/topotests/bgp_set_aspath_replace/__init__.py | 0 .../topotests/bgp_set_aspath_replace/r1/bgpd.conf | 18 + .../topotests/bgp_set_aspath_replace/r1/zebra.conf | 6 + .../topotests/bgp_set_aspath_replace/r2/bgpd.conf | 8 + .../topotests/bgp_set_aspath_replace/r2/zebra.conf | 9 + .../topotests/bgp_set_aspath_replace/r3/bgpd.conf | 9 + .../topotests/bgp_set_aspath_replace/r3/zebra.conf | 10 + .../test_bgp_set_aspath_replace.py | 201 + .../__init__.py | 0 .../r1/bgpd.conf | 8 + .../r1/zebra.conf | 6 + .../r2/bgpd.conf | 15 + .../r2/zebra.conf | 9 + .../r3/bgpd.conf | 15 + .../r3/zebra.conf | 9 + .../test_bgp_set_local-preference_add_subtract.py | 117 + tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf | 31 + tests/topotests/bgp_snmp_bgp4v2mib/r1/zebra.conf | 5 + tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf | 23 + tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf | 17 + tests/topotests/bgp_snmp_bgp4v2mib/r2/zebra.conf | 5 + .../bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py | 236 + tests/topotests/bgp_snmp_mplsl3vpn/ce1/bgpd.conf | 12 + tests/topotests/bgp_snmp_mplsl3vpn/ce1/snmpd.conf | 18 + tests/topotests/bgp_snmp_mplsl3vpn/ce1/zebra.conf | 19 + tests/topotests/bgp_snmp_mplsl3vpn/ce2/bgpd.conf | 12 + tests/topotests/bgp_snmp_mplsl3vpn/ce2/snmpd.conf | 18 + tests/topotests/bgp_snmp_mplsl3vpn/ce2/zebra.conf | 19 + tests/topotests/bgp_snmp_mplsl3vpn/ce3/bgpd.conf | 12 + tests/topotests/bgp_snmp_mplsl3vpn/ce3/snmpd.conf | 18 + tests/topotests/bgp_snmp_mplsl3vpn/ce3/zebra.conf | 19 + tests/topotests/bgp_snmp_mplsl3vpn/ce4/bgpd.conf | 12 + tests/topotests/bgp_snmp_mplsl3vpn/ce4/snmpd.conf | 18 + tests/topotests/bgp_snmp_mplsl3vpn/ce4/zebra.conf | 19 + tests/topotests/bgp_snmp_mplsl3vpn/r1/bgpd.conf | 48 + tests/topotests/bgp_snmp_mplsl3vpn/r1/isisd.conf | 46 + tests/topotests/bgp_snmp_mplsl3vpn/r1/snmpd.conf | 20 + tests/topotests/bgp_snmp_mplsl3vpn/r1/zebra.conf | 33 + tests/topotests/bgp_snmp_mplsl3vpn/r2/isisd.conf | 37 + tests/topotests/bgp_snmp_mplsl3vpn/r2/snmpd.conf | 18 + tests/topotests/bgp_snmp_mplsl3vpn/r2/zebra.conf | 24 + tests/topotests/bgp_snmp_mplsl3vpn/r3/isisd.conf | 45 + tests/topotests/bgp_snmp_mplsl3vpn/r3/snmpd.conf | 18 + tests/topotests/bgp_snmp_mplsl3vpn/r3/zebra.conf | 27 + tests/topotests/bgp_snmp_mplsl3vpn/r4/bgpd.conf | 43 + tests/topotests/bgp_snmp_mplsl3vpn/r4/isisd.conf | 36 + tests/topotests/bgp_snmp_mplsl3vpn/r4/snmpd.conf | 18 + tests/topotests/bgp_snmp_mplsl3vpn/r4/zebra.conf | 27 + .../bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py | 738 +++ tests/topotests/bgp_software_version/__init__.py | 0 tests/topotests/bgp_software_version/r1/bgpd.conf | 8 + tests/topotests/bgp_software_version/r1/zebra.conf | 4 + tests/topotests/bgp_software_version/r2/bgpd.conf | 7 + tests/topotests/bgp_software_version/r2/zebra.conf | 4 + .../test_bgp_software_version.py | 98 + tests/topotests/bgp_soo/__init__.py | 0 tests/topotests/bgp_soo/cpe1/bgpd.conf | 10 + tests/topotests/bgp_soo/cpe1/zebra.conf | 12 + tests/topotests/bgp_soo/cpe2/bgpd.conf | 10 + tests/topotests/bgp_soo/cpe2/zebra.conf | 9 + tests/topotests/bgp_soo/pe1/bgpd.conf | 27 + tests/topotests/bgp_soo/pe1/ldpd.conf | 10 + tests/topotests/bgp_soo/pe1/ospfd.conf | 7 + tests/topotests/bgp_soo/pe1/zebra.conf | 12 + tests/topotests/bgp_soo/pe2/bgpd.conf | 31 + tests/topotests/bgp_soo/pe2/ldpd.conf | 10 + tests/topotests/bgp_soo/pe2/ospfd.conf | 7 + tests/topotests/bgp_soo/pe2/zebra.conf | 12 + tests/topotests/bgp_soo/test_bgp_soo.py | 173 + .../bgp_srv6l3vpn_over_ipv6/c11/bgpd.conf | 0 .../bgp_srv6l3vpn_over_ipv6/c11/staticd.conf | 4 + .../bgp_srv6l3vpn_over_ipv6/c11/zebra.conf | 6 + .../bgp_srv6l3vpn_over_ipv6/c12/bgpd.conf | 0 .../bgp_srv6l3vpn_over_ipv6/c12/staticd.conf | 4 + .../bgp_srv6l3vpn_over_ipv6/c12/zebra.conf | 6 + .../bgp_srv6l3vpn_over_ipv6/c21/bgpd.conf | 0 .../bgp_srv6l3vpn_over_ipv6/c21/staticd.conf | 4 + .../bgp_srv6l3vpn_over_ipv6/c21/zebra.conf | 6 + .../bgp_srv6l3vpn_over_ipv6/c22/bgpd.conf | 0 .../bgp_srv6l3vpn_over_ipv6/c22/staticd.conf | 5 + .../bgp_srv6l3vpn_over_ipv6/c22/zebra.conf | 9 + .../bgp_srv6l3vpn_over_ipv6/c31/bgpd.conf | 0 .../bgp_srv6l3vpn_over_ipv6/c31/staticd.conf | 4 + .../bgp_srv6l3vpn_over_ipv6/c31/zebra.conf | 6 + .../bgp_srv6l3vpn_over_ipv6/c32/bgpd.conf | 0 .../bgp_srv6l3vpn_over_ipv6/c32/staticd.conf | 4 + .../bgp_srv6l3vpn_over_ipv6/c32/zebra.conf | 6 + .../topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf | 57 + .../bgp_srv6l3vpn_over_ipv6/r1/staticd.conf | 4 + .../bgp_srv6l3vpn_over_ipv6/r1/zebra.conf | 32 + .../topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf | 52 + .../bgp_srv6l3vpn_over_ipv6/r2/staticd.conf | 4 + .../bgp_srv6l3vpn_over_ipv6/r2/zebra.conf | 29 + .../topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf | 52 + .../bgp_srv6l3vpn_over_ipv6/r3/staticd.conf | 6 + .../bgp_srv6l3vpn_over_ipv6/r3/zebra.conf | 29 + .../test_bgp_srv6l3vpn_over_ipv6.py | 118 + .../bgp_srv6l3vpn_route_leak/ce1/bgpd.conf | 0 .../bgp_srv6l3vpn_route_leak/ce1/zebra.conf | 9 + .../bgp_srv6l3vpn_route_leak/pe1/bgpd.conf | 41 + .../pe1/results/default_ipv4_vpn.json | 31 + .../pe1/results/vrf10_ipv4_unicast.json | 25 + .../pe1/results/vrf20_ipv4.json | 22 + .../pe1/results/vrf20_ipv4_unicast.json | 27 + .../bgp_srv6l3vpn_route_leak/pe1/zebra.conf | 27 + .../test_bgp_srv6l3vpn_route_leak.py | 115 + tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf | 8 + .../topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json | 58 + tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf | 16 + tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf | 8 + .../topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json | 58 + tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf | 16 + tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf | 8 + .../topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json | 58 + tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf | 14 + tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf | 8 + .../topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json | 58 + tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf | 14 + tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf | 8 + .../topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json | 58 + tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf | 14 + tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf | 8 + .../topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json | 58 + tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf | 14 + tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf | 79 + .../topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json | 169 + .../r1/vrf10_afv4_auto_no_sid_rib.json | 23 + .../r1/vrf10_afv4_auto_sid_rib.json | 53 + .../r1/vrf10_afv4_manual_no_sid_rib.json | 23 + .../r1/vrf10_afv4_manual_sid_rib.json | 54 + .../r1/vrf10_afv6_auto_no_sid_rib.json | 77 + .../r1/vrf10_afv6_auto_sid_rib.json | 107 + .../r1/vrf10_afv6_manual_no_sid_rib.json | 77 + .../r1/vrf10_afv6_manual_sid_rib.json | 106 + .../r1/vrf10_pervrf4_auto_sid_rib.json | 54 + .../r1/vrf10_pervrf4_manual_sid_rib.json | 53 + .../r1/vrf10_pervrf6_auto_sid_rib.json | 106 + .../r1/vrf10_pervrf6_manual_sid_rib.json | 106 + .../r1/vrf10_pervrf_auto_no_sid_rib.json | 77 + .../r1/vrf10_pervrf_manual_no_sid_rib.json | 22 + .../topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json | 112 + .../topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json | 106 + tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf | 43 + tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf | 80 + .../topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json | 169 + .../topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json | 106 + .../topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json | 112 + tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf | 43 + .../bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py | 438 ++ .../bgp_srv6l3vpn_to_bgp_vrf/ce1/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf/ce1/ipv6_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf/ce1/zebra.conf | 14 + .../bgp_srv6l3vpn_to_bgp_vrf/ce2/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf/ce2/ipv6_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf/ce2/zebra.conf | 14 + .../bgp_srv6l3vpn_to_bgp_vrf/ce3/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf/ce3/ipv6_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf/ce3/zebra.conf | 14 + .../bgp_srv6l3vpn_to_bgp_vrf/ce4/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf/ce4/ipv6_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf/ce4/zebra.conf | 14 + .../bgp_srv6l3vpn_to_bgp_vrf/ce5/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf/ce5/ipv6_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf/ce5/zebra.conf | 14 + .../bgp_srv6l3vpn_to_bgp_vrf/ce6/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf/ce6/ipv6_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf/ce6/zebra.conf | 14 + .../bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf | 66 + .../bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib.json | 170 + .../r1/vpnv6_rib_locator_deleted.json | 160 + .../r1/vpnv6_rib_locator_recreated.json | 169 + .../bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json | 86 + .../bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json | 92 + .../bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf | 42 + .../bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf | 67 + .../bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib.json | 170 + .../r2/vpnv6_rib_locator_deleted.json | 93 + .../r2/vpnv6_rib_locator_recreated.json | 169 + .../bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json | 92 + .../bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json | 86 + .../bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf | 42 + .../test_bgp_srv6l3vpn_to_bgp_vrf.py | 276 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce1/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce1/ip_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce1/zebra.conf | 14 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce2/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce2/ip_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce2/zebra.conf | 14 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce3/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce3/ip_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce3/zebra.conf | 14 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce4/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce4/ip_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce4/zebra.conf | 14 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce5/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce5/ip_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce5/zebra.conf | 14 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce6/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce6/ip_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf2/ce6/zebra.conf | 14 + .../bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf | 68 + .../bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json | 167 + .../bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf10_rib.json | 86 + .../bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf20_rib.json | 92 + .../bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf | 41 + .../bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf | 68 + .../bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json | 167 + .../bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf10_rib.json | 92 + .../bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf20_rib.json | 86 + .../bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf | 40 + .../test_bgp_srv6l3vpn_to_bgp_vrf2.py | 146 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce1/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce1/ip_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce1/ipv6_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce1/zebra.conf | 16 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce2/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce2/ip_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce2/ipv6_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce2/zebra.conf | 16 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce3/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce3/ip_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce3/ipv6_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce3/zebra.conf | 16 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce4/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce4/ip_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce4/ipv6_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce4/zebra.conf | 16 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce5/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce5/ip_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce5/ipv6_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce5/zebra.conf | 16 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce6/bgpd.conf | 8 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce6/ip_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce6/ipv6_rib.json | 58 + .../bgp_srv6l3vpn_to_bgp_vrf3/ce6/zebra.conf | 16 + .../bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf | 87 + .../bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json | 167 + .../r1/vpnv4_rib_locator_deleted.json | 90 + .../r1/vpnv4_rib_locator_recreated.json | 166 + .../r1/vpnv4_rib_sid_vpn_export_disabled.json | 115 + .../r1/vpnv4_rib_sid_vpn_export_reenabled.json | 167 + .../bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json | 170 + .../r1/vpnv6_rib_locator_deleted.json | 160 + .../r1/vpnv6_rib_locator_recreated.json | 169 + .../r1/vpnv6_rib_sid_vpn_export_disabled.json | 116 + .../r1/vpnv6_rib_sid_vpn_export_reenabled.json | 170 + .../bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v4_rib.json | 86 + .../bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v6_rib.json | 86 + .../bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v4_rib.json | 92 + .../bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v6_rib.json | 92 + .../bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf | 43 + .../bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf | 88 + .../bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json | 167 + .../r2/vpnv4_rib_locator_deleted.json | 90 + .../r2/vpnv4_rib_locator_recreated.json | 166 + .../r2/vpnv4_rib_sid_vpn_export_disabled.json | 117 + .../r2/vpnv4_rib_sid_vpn_export_reenabled.json | 167 + .../bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json | 170 + .../r2/vpnv6_rib_locator_deleted.json | 93 + .../r2/vpnv6_rib_locator_recreated.json | 169 + .../r2/vpnv6_rib_sid_vpn_export_disabled.json | 120 + .../r2/vpnv6_rib_sid_vpn_export_reenabled.json | 170 + .../bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v4_rib.json | 92 + .../bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v6_rib.json | 92 + .../bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v4_rib.json | 86 + .../bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v6_rib.json | 86 + .../bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf | 43 + .../test_bgp_srv6l3vpn_to_bgp_vrf3.py | 337 ++ .../bgp_suppress_fib/r1/bgp_ipv4_allowas.json | 40 + tests/topotests/bgp_suppress_fib/r1/bgpd.conf | 15 + tests/topotests/bgp_suppress_fib/r1/zebra.conf | 9 + .../bgp_suppress_fib/r2/bgp_ipv4_allowas.json | 68 + .../bgp_suppress_fib/r2/bgpd.allowas_in.conf | 18 + tests/topotests/bgp_suppress_fib/r2/bgpd.conf | 11 + .../bgp_suppress_fib/r2/no_bgp_ipv4_allowas.json | 1 + .../topotests/bgp_suppress_fib/r2/v4_override.json | 20 + tests/topotests/bgp_suppress_fib/r2/zebra.conf | 16 + tests/topotests/bgp_suppress_fib/r3/bgpd.conf | 9 + .../topotests/bgp_suppress_fib/r3/v4_override.json | 4 + tests/topotests/bgp_suppress_fib/r3/v4_route.json | 28 + tests/topotests/bgp_suppress_fib/r3/v4_route2.json | 4 + tests/topotests/bgp_suppress_fib/r3/v4_route3.json | 23 + tests/topotests/bgp_suppress_fib/r3/zebra.conf | 6 + .../bgp_suppress_fib/test_bgp_suppress_fib.py | 237 + tests/topotests/bgp_tcp_mss/__init__.py | 0 tests/topotests/bgp_tcp_mss/bgp_vrf_tcp_mss.json | 222 + tests/topotests/bgp_tcp_mss/r1/bgpd.conf | 6 + tests/topotests/bgp_tcp_mss/r1/zebra.conf | 6 + tests/topotests/bgp_tcp_mss/r2/bgpd.conf | 6 + tests/topotests/bgp_tcp_mss/r2/zebra.conf | 6 + tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py | 162 + .../topotests/bgp_tcp_mss/test_bgp_vrf_tcp_mss.py | 753 +++ tests/topotests/bgp_tcp_mss_passive/__init__.py | 0 tests/topotests/bgp_tcp_mss_passive/r1/frr.conf | 12 + tests/topotests/bgp_tcp_mss_passive/r2/frr.conf | 10 + .../test_bgp_tcp_mss_passive.py | 106 + tests/topotests/bgp_unique_rid/bgp_unique_rid.json | 505 ++ .../bgp_unique_rid/bgp_unique_rid_vrf.json | 529 ++ .../bgp_unique_rid/test_bgp_unique_rid.py | 891 +++ .../bgp_unique_rid/test_bgp_unique_rid_vrf.py | 466 ++ tests/topotests/bgp_unnumbered/__init__.py | 0 tests/topotests/bgp_unnumbered/r1/bgpd.conf | 9 + tests/topotests/bgp_unnumbered/r1/zebra.conf | 10 + tests/topotests/bgp_unnumbered/r2/bgpd.conf | 9 + tests/topotests/bgp_unnumbered/r2/zebra.conf | 12 + .../bgp_unnumbered/test_bgp_unnumbered.py | 119 + tests/topotests/bgp_update_delay/__init__.py | 0 tests/topotests/bgp_update_delay/r1/bgpd.conf | 10 + tests/topotests/bgp_update_delay/r1/zebra.conf | 9 + tests/topotests/bgp_update_delay/r2/bgpd.conf | 18 + tests/topotests/bgp_update_delay/r2/zebra.conf | 16 + tests/topotests/bgp_update_delay/r3/bgpd.conf | 10 + tests/topotests/bgp_update_delay/r3/zebra.conf | 9 + tests/topotests/bgp_update_delay/r4/bgpd.conf | 11 + tests/topotests/bgp_update_delay/r4/zebra.conf | 9 + tests/topotests/bgp_update_delay/r5/bgpd.conf | 11 + tests/topotests/bgp_update_delay/r5/zebra.conf | 9 + .../bgp_update_delay/test_bgp_update_delay.py | 295 + tests/topotests/bgp_vpn_5549_route_map/__init__.py | 0 .../bgp_vpn_5549_route_map/cpe1/bgpd.conf | 9 + .../bgp_vpn_5549_route_map/cpe1/zebra.conf | 9 + .../bgp_vpn_5549_route_map/cpe2/bgpd.conf | 6 + .../bgp_vpn_5549_route_map/cpe2/zebra.conf | 6 + .../topotests/bgp_vpn_5549_route_map/pe1/bgpd.conf | 38 + .../topotests/bgp_vpn_5549_route_map/pe1/ldpd.conf | 10 + .../bgp_vpn_5549_route_map/pe1/ospf6d.conf | 12 + .../bgp_vpn_5549_route_map/pe1/zebra.conf | 14 + .../topotests/bgp_vpn_5549_route_map/pe2/bgpd.conf | 29 + .../topotests/bgp_vpn_5549_route_map/pe2/ldpd.conf | 10 + .../bgp_vpn_5549_route_map/pe2/ospf6d.conf | 12 + .../bgp_vpn_5549_route_map/pe2/zebra.conf | 14 + .../test_bgp_vpn_5549_route_map.py | 171 + tests/topotests/bgp_vpnv4_asbr/__init__.py | 0 tests/topotests/bgp_vpnv4_asbr/h1/zebra.conf | 7 + tests/topotests/bgp_vpnv4_asbr/h2/zebra.conf | 6 + tests/topotests/bgp_vpnv4_asbr/h3/zebra.conf | 6 + .../bgp_vpnv4_asbr/r1/bgp_ipv4_routes.json | 49 + tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf | 29 + tests/topotests/bgp_vpnv4_asbr/r1/zebra.conf | 10 + tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf | 31 + .../bgp_vpnv4_asbr/r2/ipv4_vpn_summary.json | 24 + tests/topotests/bgp_vpnv4_asbr/r2/zebra.conf | 13 + tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf | 25 + tests/topotests/bgp_vpnv4_asbr/r3/zebra.conf | 14 + tests/topotests/bgp_vpnv4_asbr/rr100/bgpd.conf | 29 + tests/topotests/bgp_vpnv4_asbr/rr100/zebra.conf | 7 + tests/topotests/bgp_vpnv4_asbr/rs200/bgpd.conf | 19 + tests/topotests/bgp_vpnv4_asbr/rs200/zebra.conf | 4 + .../bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py | 917 +++ tests/topotests/bgp_vpnv4_ebgp/__init__.py | 0 .../bgp_vpnv4_ebgp/r1/bgp_ipv4_routes.json | 49 + tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf | 28 + tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json | 62 + tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf | 7 + .../bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json | 38 + tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf | 25 + tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf | 7 + .../bgp_vpnv4_ebgp/r3/bgp_ipv4_routes.json | 38 + tests/topotests/bgp_vpnv4_ebgp/r3/bgpd.conf | 25 + tests/topotests/bgp_vpnv4_ebgp/r3/zebra.conf | 7 + .../bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py | 228 + tests/topotests/bgp_vpnv4_gre/__init__.py | 0 tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf | 27 + tests/topotests/bgp_vpnv4_gre/r1/ipv4_routes.json | 50 + tests/topotests/bgp_vpnv4_gre/r1/zebra.conf | 14 + .../bgp_vpnv4_gre/r2/bgp_ipv4_routes.json | 38 + tests/topotests/bgp_vpnv4_gre/r2/bgpd.conf | 22 + tests/topotests/bgp_vpnv4_gre/r2/zebra.conf | 14 + .../topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py | 178 + tests/topotests/bgp_vpnv4_noretain/__init__.py | 0 tests/topotests/bgp_vpnv4_noretain/r1/bgpd.conf | 46 + .../bgp_vpnv4_noretain/r1/ipv4_vpn_routes_all.json | 175 + .../r1/ipv4_vpn_routes_no_retain_init.json | 121 + ...pv4_vpn_routes_no_retain_init_plus_r2_vrf2.json | 148 + ...pv4_vpn_routes_no_retain_init_plus_r2_vrf3.json | 148 + .../r1/ipv4_vrf_all_routes_init.json | 156 + .../r1/ipv4_vrf_all_routes_plus_r1_vrf1.json | 190 + .../r1/ipv4_vrf_all_routes_plus_r2_vrf2.json | 188 + .../r1/ipv4_vrf_all_routes_plus_r2_vrf3.json | 188 + tests/topotests/bgp_vpnv4_noretain/r1/zebra.conf | 16 + tests/topotests/bgp_vpnv4_noretain/r2/bgpd.conf | 54 + .../bgp_vpnv4_noretain/r2/ipv4_vpn_routes_all.json | 177 + .../bgp_vpnv4_noretain/r2/ipv4_vpn_summary.json | 17 + tests/topotests/bgp_vpnv4_noretain/r2/zebra.conf | 22 + .../bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py | 567 ++ .../bgp_vpnv4_per_nexthop_label/__init__.py | 0 .../r1/bgp_ipv4_routes_vrf1.json | 143 + .../bgp_vpnv4_per_nexthop_label/r1/bgpd.conf | 30 + .../r1/ipv4_routes.json | 50 + .../bgp_vpnv4_per_nexthop_label/r1/zebra.conf | 18 + .../bgp_vpnv4_per_nexthop_label/r11/bgpd.conf | 11 + .../bgp_vpnv4_per_nexthop_label/r11/zebra.conf | 4 + .../bgp_vpnv4_per_nexthop_label/r12/bgpd.conf | 9 + .../bgp_vpnv4_per_nexthop_label/r12/zebra.conf | 4 + .../bgp_vpnv4_per_nexthop_label/r13/bgpd.conf | 9 + .../bgp_vpnv4_per_nexthop_label/r13/zebra.conf | 4 + .../r2/bgp_ipv4_routes.json | 38 + .../r2/bgp_vpnv4_routes.json | 187 + .../bgp_vpnv4_per_nexthop_label/r2/bgpd.conf | 25 + .../bgp_vpnv4_per_nexthop_label/r2/zebra.conf | 7 + .../bgp_vpnv4_per_nexthop_label/rr/bgpd.conf | 13 + .../bgp_vpnv4_per_nexthop_label/rr/zebra.conf | 4 + .../test_bgp_vpnv4_per_nexthop_label.py | 806 +++ .../bgp_vpnv6_per_nexthop_label/__init__.py | 0 .../r1/bgp_ipv6_routes_vrf1.json | 186 + .../bgp_vpnv6_per_nexthop_label/r1/bgpd.conf | 45 + .../bgp_vpnv6_per_nexthop_label/r1/zebra.conf | 18 + .../bgp_vpnv6_per_nexthop_label/r11/bgpd.conf | 15 + .../bgp_vpnv6_per_nexthop_label/r11/zebra.conf | 4 + .../bgp_vpnv6_per_nexthop_label/r12/bgpd.conf | 13 + .../bgp_vpnv6_per_nexthop_label/r12/zebra.conf | 4 + .../bgp_vpnv6_per_nexthop_label/r13/bgpd.conf | 12 + .../bgp_vpnv6_per_nexthop_label/r13/zebra.conf | 4 + .../r2/bgp_vpnv6_routes.json | 187 + .../bgp_vpnv6_per_nexthop_label/r2/bgpd.conf | 25 + .../bgp_vpnv6_per_nexthop_label/r2/zebra.conf | 7 + .../bgp_vpnv6_per_nexthop_label/rr/bgpd.conf | 24 + .../bgp_vpnv6_per_nexthop_label/rr/zebra.conf | 4 + .../test_bgp_vpnv6_per_nexthop_label.py | 808 +++ .../bgp_vrf_dynamic_route_leak_topo1.json | 563 ++ .../bgp_vrf_dynamic_route_leak_topo2.json | 563 ++ .../test_bgp_vrf_dynamic_route_leak_topo1.py | 1903 ++++++ .../test_bgp_vrf_dynamic_route_leak_topo2.py | 890 +++ .../bgp_vrf_dynamic_route_leak_topo3.json | 1088 ++++ .../test_bgp_vrf_dynamic_route_leak_topo3.py | 1790 ++++++ .../bgp_vrf_dynamic_route_leak_topo4.json | 1088 ++++ .../test_bgp_vrf_dynamic_route_leak_topo4-1.py | 394 ++ .../test_bgp_vrf_dynamic_route_leak_topo4-2.py | 919 +++ .../test_bgp_vrf_dynamic_route_leak_topo4-3.py | 919 +++ .../bgp_vrf_leaking_5549_routes/ce1/bgpd.conf | 23 + .../bgp_vrf_leaking_5549_routes/ce1/zebra.conf | 13 + .../bgp_vrf_leaking_5549_routes/pe1/bgpd.conf | 41 + .../pe1/results/default_ipv4_vpn.json | 32 + .../pe1/results/vrf10_ipv4_unicast.json | 32 + .../pe1/results/vrf20_ipv4_unicast.json | 34 + .../bgp_vrf_leaking_5549_routes/pe1/zebra.conf | 10 + .../test_bgp_vrf_leaking.py | 108 + .../__init__.py | 0 .../r1/bgpd.conf | 31 + .../r1/zebra.conf | 6 + .../test_bgp_vrf_leaking_rt_change_route_maps.py | 91 + .../bgp_vrf_lite_best_path_topo1.json | 563 ++ .../bgp_vrf_lite_best_path_topo2.json | 1088 ++++ .../test_bgp_vrf_lite_best_path_topo1.py | 879 +++ .../test_bgp_vrf_lite_best_path_topo2.py | 526 ++ .../topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py | 0 .../topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf | 15 + .../bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json | 44 + .../bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json | 37 + .../bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf | 9 + .../topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf | 18 + .../bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json | 44 + .../bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json | 23 + .../bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf | 9 + .../test_bgp_vrf_lite_ipv6_rtadv.dot | 44 + .../test_bgp_vrf_lite_ipv6_rtadv.py | 155 + tests/topotests/bgp_vrf_md5_peering/__init__.py | 0 tests/topotests/bgp_vrf_md5_peering/exabgp.env | 53 + .../topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg | 13 + tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf | 11 + tests/topotests/bgp_vrf_md5_peering/r1/zebra.conf | 6 + .../test_bgp_vrf_md5_peering.py | 87 + tests/topotests/bgp_vrf_netns/__init__.py | 0 .../topotests/bgp_vrf_netns/bgp-vrf-netns-topo.dot | 50 + .../topotests/bgp_vrf_netns/bgp-vrf-netns-topo.pdf | Bin 0 -> 13104 bytes tests/topotests/bgp_vrf_netns/exabgp.env | 54 + tests/topotests/bgp_vrf_netns/peer1/exa-send.py | 28 + tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg | 21 + tests/topotests/bgp_vrf_netns/r1/bgpd.conf | 10 + tests/topotests/bgp_vrf_netns/r1/summary.txt | 17 + tests/topotests/bgp_vrf_netns/r1/summary20.txt | 15 + tests/topotests/bgp_vrf_netns/r1/zebra.conf | 7 + .../bgp_vrf_netns/test_bgp_vrf_netns_topo.py | 212 + .../bgp_vrf_route_leak_basic/r1/bgpd.conf | 16 + .../bgp_vrf_route_leak_basic/r1/zebra.conf | 18 + .../topotests/bgp_vrf_route_leak_basic/setup_vrfs | 16 + .../test_bgp-vrf-route-leak-basic.py | 136 + tests/topotests/config_timing/r1/staticd.conf | 1 + tests/topotests/config_timing/r1/zebra.conf | 16 + .../topotests/config_timing/test_config_timing.py | 262 + tests/topotests/conftest.py | 691 +++ tests/topotests/cspf_topo1/r1/isisd.conf | 35 + tests/topotests/cspf_topo1/r1/sharpd.conf | 2 + tests/topotests/cspf_topo1/r1/zebra.conf | 28 + tests/topotests/cspf_topo1/r2/isisd.conf | 50 + tests/topotests/cspf_topo1/r2/zebra.conf | 46 + tests/topotests/cspf_topo1/r3/isisd.conf | 36 + tests/topotests/cspf_topo1/r3/zebra.conf | 27 + tests/topotests/cspf_topo1/r4/isisd.conf | 42 + tests/topotests/cspf_topo1/r4/zebra.conf | 26 + .../cspf_topo1/reference/cspf-failed-dst.txt | 1 + .../cspf_topo1/reference/cspf-failed-same.txt | 1 + .../cspf_topo1/reference/cspf-failed-src.txt | 1 + .../topotests/cspf_topo1/reference/cspf-failed.txt | 1 + .../cspf_topo1/reference/cspf-ipv4-delay.txt | 3 + .../cspf_topo1/reference/cspf-ipv4-metric.txt | 3 + .../cspf_topo1/reference/cspf-ipv4-te-metric.txt | 3 + .../cspf_topo1/reference/cspf-ipv6-delay.txt | 3 + .../cspf_topo1/reference/cspf-ipv6-metric.txt | 3 + .../cspf_topo1/reference/cspf-ipv6-te-metric.txt | 3 + .../topotests/cspf_topo1/reference/sharp-ted.json | 858 +++ tests/topotests/cspf_topo1/test_cspf_topo1.py | 304 + tests/topotests/docker/README.md | 72 + tests/topotests/docker/build.sh | 11 + tests/topotests/docker/frr-topotests.sh | 155 + tests/topotests/docker/inner/compile_frr.sh | 87 + tests/topotests/docker/inner/entrypoint.sh | 31 + tests/topotests/docker/inner/funcs.sh | 53 + tests/topotests/docker/inner/motd.txt | 15 + tests/topotests/docker/inner/openvswitch.sh | 40 + tests/topotests/eigrp_topo1/r1/eigrpd.conf | 8 + tests/topotests/eigrp_topo1/r1/show_ip_eigrp.json | 32 + tests/topotests/eigrp_topo1/r1/show_ip_eigrp.ref | 14 + .../eigrp_topo1/r1/show_ip_route.json_ref | 90 + tests/topotests/eigrp_topo1/r1/zebra.conf | 20 + tests/topotests/eigrp_topo1/r2/eigrpd.conf | 7 + tests/topotests/eigrp_topo1/r2/show_ip_eigrp.json | 32 + tests/topotests/eigrp_topo1/r2/show_ip_eigrp.ref | 14 + .../eigrp_topo1/r2/show_ip_route.json_ref | 90 + tests/topotests/eigrp_topo1/r2/zebra.conf | 21 + tests/topotests/eigrp_topo1/r3/eigrpd.conf | 6 + tests/topotests/eigrp_topo1/r3/show_ip_eigrp.json | 32 + tests/topotests/eigrp_topo1/r3/show_ip_eigrp.ref | 14 + .../eigrp_topo1/r3/show_ip_route.json_ref | 108 + tests/topotests/eigrp_topo1/r3/zebra.conf | 22 + tests/topotests/eigrp_topo1/test_eigrp_topo1.dot | 62 + tests/topotests/eigrp_topo1/test_eigrp_topo1.py | 279 + tests/topotests/evpn_pim_1/host1/bgpd.conf | 1 + tests/topotests/evpn_pim_1/host1/pimd.conf | 4 + tests/topotests/evpn_pim_1/host1/zebra.conf | 5 + tests/topotests/evpn_pim_1/host2/bgpd.conf | 1 + tests/topotests/evpn_pim_1/host2/pimd.conf | 4 + tests/topotests/evpn_pim_1/host2/zebra.conf | 5 + tests/topotests/evpn_pim_1/leaf1/bgpd.conf | 11 + tests/topotests/evpn_pim_1/leaf1/pimd.conf | 16 + tests/topotests/evpn_pim_1/leaf1/zebra.conf | 6 + tests/topotests/evpn_pim_1/leaf2/bgpd.conf | 11 + tests/topotests/evpn_pim_1/leaf2/pimd.conf | 14 + tests/topotests/evpn_pim_1/leaf2/zebra.conf | 6 + tests/topotests/evpn_pim_1/spine/bgp.summ.json | 39 + tests/topotests/evpn_pim_1/spine/bgpd.conf | 13 + tests/topotests/evpn_pim_1/spine/join-info.json | 34 + tests/topotests/evpn_pim_1/spine/pimd.conf | 14 + tests/topotests/evpn_pim_1/spine/zebra.conf | 8 + tests/topotests/evpn_pim_1/test_evpn_pim_topo1.py | 204 + tests/topotests/evpn_type5_test_topo1/__init__.py | 0 .../evpn_type5_chaos_topo1.json | 887 +++ .../evpn_type5_test_topo1/evpn_type5_topo1.json | 1004 ++++ .../test_evpn_type5_chaos_topo1.py | 1007 ++++ .../evpn_type5_test_topo1/test_evpn_type5_topo1.py | 2322 ++++++++ tests/topotests/example_munet/munet.yaml | 17 + tests/topotests/example_munet/r1/daemons | 6 + tests/topotests/example_munet/r1/frr.conf | 7 + tests/topotests/example_munet/r1/vtysh.conf | 1 + tests/topotests/example_munet/r2/daemons | 6 + tests/topotests/example_munet/r2/frr.conf | 10 + tests/topotests/example_munet/r2/vtysh.conf | 1 + tests/topotests/example_munet/r3/daemons | 6 + tests/topotests/example_munet/r3/frr.conf | 7 + tests/topotests/example_munet/r3/vtysh.conf | 1 + tests/topotests/example_munet/test_munet.py | 10 + tests/topotests/example_test/__init__.py | 0 tests/topotests/example_test/r1/zebra.conf | 8 + tests/topotests/example_test/r2/zebra.conf | 4 + tests/topotests/example_test/test_example.py | 70 + tests/topotests/example_test/test_template.dot | 51 + tests/topotests/example_test/test_template.jpg | Bin 0 -> 15470 bytes tests/topotests/example_test/test_template.py | 160 + .../topotests/example_test/test_template_json.json | 188 + tests/topotests/example_test/test_template_json.py | 55 + tests/topotests/example_topojson_test/__init__.py | 0 .../test_topo_json_multiple_links/__init__.py | 0 .../example_topojson_multiple_links.json | 152 + .../test_example_topojson_multiple_links.py | 184 + .../test_topo_json_single_link/__init__.py | 0 .../example_topojson.json | 153 + .../test_example_topojson.py | 184 + .../__init__.py | 0 .../example_topojson.json | 161 + .../test_example_topojson.py | 195 + tests/topotests/grpc_basic/lib | 1 + tests/topotests/grpc_basic/r1/zebra.conf | 8 + tests/topotests/grpc_basic/r2/zebra.conf | 8 + tests/topotests/grpc_basic/test_basic_grpc.py | 166 + .../isis_advertise_high_metrics/__init__.py | 0 .../isis_advertise_high_metrics/r1/isisd.conf | 19 + .../isis_advertise_high_metrics/r1/zebra.conf | 5 + .../isis_advertise_high_metrics/r2/isisd.conf | 18 + .../isis_advertise_high_metrics/r2/zebra.conf | 5 + .../isis_advertise_high_metrics/r3/isisd.conf | 20 + .../isis_advertise_high_metrics/r3/zebra.conf | 5 + .../isis_advertise_high_metrics/r4/isisd.conf | 19 + .../isis_advertise_high_metrics/r4/zebra.conf | 5 + .../test_isis_advertise_high_metrics.py | 473 ++ tests/topotests/isis_lfa_topo1/__init__.py | 0 tests/topotests/isis_lfa_topo1/rt1/bfdd.conf | 4 + tests/topotests/isis_lfa_topo1/rt1/isisd.conf | 57 + .../isis_lfa_topo1/rt1/step1/show_ipv6_route.ref | 236 + .../step1/show_yang_interface_isis_adjacencies.ref | 101 + .../rt1/step10/show_ipv6_route.ref.diff | 46 + .../rt1/step11/show_ipv6_route.ref.diff | 23 + .../rt1/step12/show_ipv6_route.ref.diff | 107 + .../rt1/step13/show_ipv6_route.ref.diff | 45 + .../rt1/step14/show_ipv6_route.ref.diff | 0 .../rt1/step15/show_ipv6_route.ref.diff | 50 + .../rt1/step16/show_ipv6_route.ref.diff | 53 + .../rt1/step2/show_ipv6_route.ref.diff | 164 + .../rt1/step3/show_ipv6_route.ref.diff | 164 + .../rt1/step4/show_ipv6_route.ref.diff | 142 + .../rt1/step5/show_ipv6_route.ref.diff | 142 + .../rt1/step6/show_ipv6_route.ref.diff | 164 + .../rt1/step7/show_ipv6_route.ref.diff | 37 + .../rt1/step8/show_ipv6_route.ref.diff | 129 + .../rt1/step9/show_ipv6_route.ref.diff | 46 + tests/topotests/isis_lfa_topo1/rt1/zebra.conf | 16 + tests/topotests/isis_lfa_topo1/rt2/bfdd.conf | 4 + tests/topotests/isis_lfa_topo1/rt2/isisd.conf | 42 + .../isis_lfa_topo1/rt2/step1/show_ipv6_route.ref | 162 + .../step1/show_yang_interface_isis_adjacencies.ref | 63 + tests/topotests/isis_lfa_topo1/rt2/zebra.conf | 16 + tests/topotests/isis_lfa_topo1/rt3/isisd.conf | 41 + .../isis_lfa_topo1/rt3/step1/show_ipv6_route.ref | 188 + .../step1/show_yang_interface_isis_adjacencies.ref | 63 + tests/topotests/isis_lfa_topo1/rt3/zebra.conf | 16 + tests/topotests/isis_lfa_topo1/rt4/isisd.conf | 34 + .../isis_lfa_topo1/rt4/step1/show_ipv6_route.ref | 172 + .../step1/show_yang_interface_isis_adjacencies.ref | 44 + tests/topotests/isis_lfa_topo1/rt4/zebra.conf | 16 + tests/topotests/isis_lfa_topo1/rt5/isisd.conf | 34 + .../isis_lfa_topo1/rt5/step1/show_ipv6_route.ref | 176 + .../step1/show_yang_interface_isis_adjacencies.ref | 44 + tests/topotests/isis_lfa_topo1/rt5/zebra.conf | 16 + tests/topotests/isis_lfa_topo1/rt6/isisd.conf | 33 + .../isis_lfa_topo1/rt6/step1/show_ipv6_route.ref | 172 + .../step1/show_yang_interface_isis_adjacencies.ref | 44 + tests/topotests/isis_lfa_topo1/rt6/zebra.conf | 16 + tests/topotests/isis_lfa_topo1/rt7/isisd.conf | 56 + .../isis_lfa_topo1/rt7/step1/show_ipv6_route.ref | 186 + .../step1/show_yang_interface_isis_adjacencies.ref | 101 + tests/topotests/isis_lfa_topo1/rt7/zebra.conf | 16 + .../isis_lfa_topo1/test_isis_lfa_topo1.py | 1086 ++++ tests/topotests/isis_lsp_bits_topo1/__init__.py | 0 tests/topotests/isis_lsp_bits_topo1/rt1/isisd.conf | 28 + .../rt1/step1/show_ip_route.ref | 89 + .../rt1/step1/show_ipv6_route.ref | 65 + .../step1/show_yang_interface_isis_adjacencies.ref | 32 + .../rt1/step2/show_ip_route.ref | 82 + .../rt1/step2/show_ipv6_route.ref | 59 + .../rt1/step3/show_ip_route.ref | 62 + .../rt1/step3/show_ipv6_route.ref | 40 + .../rt1/step4/show_ip_route.ref | 89 + .../rt1/step4/show_ipv6_route.ref | 65 + tests/topotests/isis_lsp_bits_topo1/rt1/zebra.conf | 19 + tests/topotests/isis_lsp_bits_topo1/rt2/isisd.conf | 37 + .../rt2/step1/show_ip_route.ref | 77 + .../rt2/step1/show_ipv6_route.ref | 40 + .../step1/show_yang_interface_isis_adjacencies.ref | 58 + tests/topotests/isis_lsp_bits_topo1/rt2/zebra.conf | 22 + tests/topotests/isis_lsp_bits_topo1/rt3/isisd.conf | 37 + .../rt3/step1/show_ip_route.ref | 97 + .../rt3/step1/show_ipv6_route.ref | 59 + .../step1/show_yang_interface_isis_adjacencies.ref | 51 + tests/topotests/isis_lsp_bits_topo1/rt3/zebra.conf | 22 + tests/topotests/isis_lsp_bits_topo1/rt4/isisd.conf | 45 + .../rt4/step1/show_ip_route.ref | 94 + .../rt4/step1/show_ipv6_route.ref | 21 + .../step1/show_yang_interface_isis_adjacencies.ref | 63 + tests/topotests/isis_lsp_bits_topo1/rt4/zebra.conf | 25 + tests/topotests/isis_lsp_bits_topo1/rt5/isisd.conf | 45 + .../rt5/step1/show_ip_route.ref | 116 + .../rt5/step1/show_ipv6_route.ref | 40 + .../step1/show_yang_interface_isis_adjacencies.ref | 63 + tests/topotests/isis_lsp_bits_topo1/rt5/zebra.conf | 25 + tests/topotests/isis_lsp_bits_topo1/rt6/isisd.conf | 34 + .../rt6/step1/show_ip_route.ref | 106 + .../rt6/step1/show_ipv6_route.ref | 46 + .../step1/show_yang_interface_isis_adjacencies.ref | 44 + .../rt6/step2/show_ip_route.ref | 99 + .../rt6/step2/show_ipv6_route.ref | 40 + .../rt6/step3/show_ip_route.ref | 79 + .../rt6/step3/show_ipv6_route.ref | 21 + .../rt6/step4/show_ip_route.ref | 106 + .../rt6/step4/show_ipv6_route.ref | 46 + tests/topotests/isis_lsp_bits_topo1/rt6/zebra.conf | 22 + .../test_isis_lsp_bits_topo1.py | 364 ++ tests/topotests/isis_rlfa_topo1/__init__.py | 0 tests/topotests/isis_rlfa_topo1/rt1/isisd.conf | 41 + tests/topotests/isis_rlfa_topo1/rt1/ldpd.conf | 30 + .../isis_rlfa_topo1/rt1/step1/show_ip_route.ref | 235 + .../isis_rlfa_topo1/rt1/step1/show_ipv6_route.ref | 207 + .../step1/show_yang_interface_isis_adjacencies.ref | 44 + .../rt1/step10/show_ip_route.ref.diff | 68 + .../rt1/step10/show_ipv6_route.ref.diff | 62 + .../rt1/step2/show_ip_route.ref.diff | 134 + .../rt1/step2/show_ipv6_route.ref.diff | 122 + .../rt1/step3/show_ip_route.ref.diff | 134 + .../rt1/step3/show_ipv6_route.ref.diff | 122 + .../rt1/step4/show_ip_route.ref.diff | 68 + .../rt1/step4/show_ipv6_route.ref.diff | 62 + .../rt1/step5/show_ip_route.ref.diff | 68 + .../rt1/step5/show_ipv6_route.ref.diff | 62 + .../rt1/step6/show_ip_route.ref.diff | 134 + .../rt1/step6/show_ipv6_route.ref.diff | 122 + .../rt1/step7/show_ip_route.ref.diff | 0 .../rt1/step7/show_ipv6_route.ref.diff | 0 .../rt1/step8/show_ip_route.ref.diff | 0 .../rt1/step8/show_ipv6_route.ref.diff | 0 .../rt1/step9/show_ip_route.ref.diff | 68 + .../rt1/step9/show_ipv6_route.ref.diff | 62 + tests/topotests/isis_rlfa_topo1/rt1/zebra.conf | 22 + tests/topotests/isis_rlfa_topo1/rt2/isisd.conf | 34 + tests/topotests/isis_rlfa_topo1/rt2/ldpd.conf | 30 + tests/topotests/isis_rlfa_topo1/rt2/zebra.conf | 22 + tests/topotests/isis_rlfa_topo1/rt3/isisd.conf | 34 + tests/topotests/isis_rlfa_topo1/rt3/ldpd.conf | 30 + tests/topotests/isis_rlfa_topo1/rt3/zebra.conf | 22 + tests/topotests/isis_rlfa_topo1/rt4/isisd.conf | 34 + tests/topotests/isis_rlfa_topo1/rt4/ldpd.conf | 30 + tests/topotests/isis_rlfa_topo1/rt4/zebra.conf | 22 + tests/topotests/isis_rlfa_topo1/rt5/isisd.conf | 34 + tests/topotests/isis_rlfa_topo1/rt5/ldpd.conf | 30 + tests/topotests/isis_rlfa_topo1/rt5/zebra.conf | 22 + tests/topotests/isis_rlfa_topo1/rt6/isisd.conf | 34 + tests/topotests/isis_rlfa_topo1/rt6/ldpd.conf | 30 + tests/topotests/isis_rlfa_topo1/rt6/zebra.conf | 22 + tests/topotests/isis_rlfa_topo1/rt7/isisd.conf | 34 + tests/topotests/isis_rlfa_topo1/rt7/ldpd.conf | 30 + tests/topotests/isis_rlfa_topo1/rt7/zebra.conf | 22 + tests/topotests/isis_rlfa_topo1/rt8/isisd.conf | 34 + tests/topotests/isis_rlfa_topo1/rt8/ldpd.conf | 30 + tests/topotests/isis_rlfa_topo1/rt8/zebra.conf | 22 + .../isis_rlfa_topo1/test_isis_rlfa_topo1.py | 644 ++ tests/topotests/isis_snmp/ce3/zebra.conf | 12 + tests/topotests/isis_snmp/r1/isisd.conf | 24 + tests/topotests/isis_snmp/r1/ldpd.conf | 25 + .../r1/show_yang_interface_isis_adjacencies.ref | 40 + tests/topotests/isis_snmp/r1/snmpd.conf | 18 + tests/topotests/isis_snmp/r1/zebra.conf | 24 + tests/topotests/isis_snmp/r2/isisd.conf | 25 + tests/topotests/isis_snmp/r2/ldpd.conf | 25 + .../r2/show_yang_interface_isis_adjacencies.ref | 40 + tests/topotests/isis_snmp/r2/snmpd.conf | 18 + tests/topotests/isis_snmp/r2/zebra.conf | 24 + tests/topotests/isis_snmp/r3/isisd.conf | 25 + tests/topotests/isis_snmp/r3/ldpd.conf | 25 + .../r3/show_yang_interface_isis_adjacencies.ref | 40 + tests/topotests/isis_snmp/r3/snmpd.conf | 18 + tests/topotests/isis_snmp/r3/zebra.conf | 28 + tests/topotests/isis_snmp/r4/isisd.conf | 24 + tests/topotests/isis_snmp/r4/ldpd.conf | 25 + .../r4/show_yang_interface_isis_adjacencies.ref | 40 + tests/topotests/isis_snmp/r4/snmpd.conf | 18 + tests/topotests/isis_snmp/r4/zebra.conf | 24 + tests/topotests/isis_snmp/r5/isisd.conf | 25 + tests/topotests/isis_snmp/r5/ldpd.conf | 25 + tests/topotests/isis_snmp/r5/ldpdconf | 25 + .../r5/show_yang_interface_isis_adjacencies.ref | 40 + tests/topotests/isis_snmp/r5/snmpd.conf | 18 + tests/topotests/isis_snmp/r5/zebra.conf | 24 + tests/topotests/isis_snmp/test_isis_snmp.dot | 114 + tests/topotests/isis_snmp/test_isis_snmp.py | 361 ++ .../isis_sr_flex_algo_topo1/rt1/isisd.conf | 96 + .../rt1/step1/show_isis_flex_algo.ref | 125 + .../rt1/step1/show_mpls_table.ref | 514 ++ .../rt1/step10/show_isis_flex_algo.ref | 126 + .../rt1/step10/show_mpls_table.ref | 514 ++ .../rt1/step11/show_isis_flex_algo.ref | 126 + .../rt1/step11/show_mpls_table.ref | 514 ++ .../rt1/step2/show_isis_flex_algo.ref | 126 + .../rt1/step2/show_mpls_table.ref | 514 ++ .../rt1/step3/show_isis_flex_algo.ref | 115 + .../rt1/step3/show_mpls_table.ref | 450 ++ .../rt1/step4/show_isis_flex_algo.ref | 126 + .../rt1/step4/show_mpls_table.ref | 514 ++ .../rt1/step5/show_isis_flex_algo.ref | 126 + .../rt1/step5/show_mpls_table.ref | 514 ++ .../rt1/step6/show_isis_flex_algo.ref | 112 + .../rt1/step6/show_mpls_table.ref | 450 ++ .../rt1/step7/show_isis_flex_algo.ref | 108 + .../rt1/step7/show_mpls_table.ref | 450 ++ .../rt1/step8/show_isis_flex_algo.ref | 126 + .../rt1/step8/show_mpls_table.ref | 514 ++ .../rt1/step9/show_isis_flex_algo.ref | 126 + .../rt1/step9/show_mpls_table.ref | 514 ++ .../isis_sr_flex_algo_topo1/rt1/zebra.conf | 39 + .../isis_sr_flex_algo_topo1/rt2/isisd.conf | 96 + .../rt2/step1/show_isis_flex_algo.ref | 125 + .../rt2/step1/show_mpls_table.ref | 514 ++ .../rt2/step10/show_isis_flex_algo.ref | 125 + .../rt2/step10/show_mpls_table.ref | 514 ++ .../rt2/step11/show_isis_flex_algo.ref | 125 + .../rt2/step11/show_mpls_table.ref | 514 ++ .../rt2/step2/show_isis_flex_algo.ref | 125 + .../rt2/step2/show_mpls_table.ref | 514 ++ .../rt2/step3/show_isis_flex_algo.ref | 114 + .../rt2/step3/show_mpls_table.ref | 450 ++ .../rt2/step4/show_isis_flex_algo.ref | 125 + .../rt2/step4/show_mpls_table.ref | 514 ++ .../rt2/step5/show_isis_flex_algo.ref | 125 + .../rt2/step5/show_mpls_table.ref | 514 ++ .../rt2/step6/show_isis_flex_algo.ref | 111 + .../rt2/step6/show_mpls_table.ref | 450 ++ .../rt2/step7/show_isis_flex_algo.ref | 107 + .../rt2/step7/show_mpls_table.ref | 450 ++ .../rt2/step8/show_isis_flex_algo.ref | 125 + .../rt2/step8/show_mpls_table.ref | 514 ++ .../rt2/step9/show_isis_flex_algo.ref | 125 + .../rt2/step9/show_mpls_table.ref | 482 ++ .../isis_sr_flex_algo_topo1/rt2/zebra.conf | 39 + .../isis_sr_flex_algo_topo1/rt3/isisd.conf | 76 + .../rt3/step1/show_isis_flex_algo.ref | 125 + .../rt3/step1/show_mpls_table.ref | 514 ++ .../rt3/step10/show_isis_flex_algo.ref | 125 + .../rt3/step10/show_mpls_table.ref | 514 ++ .../rt3/step11/show_isis_flex_algo.ref | 125 + .../rt3/step11/show_mpls_table.ref | 514 ++ .../rt3/step2/show_isis_flex_algo.ref | 125 + .../rt3/step2/show_mpls_table.ref | 514 ++ .../rt3/step3/show_isis_flex_algo.ref | 114 + .../rt3/step3/show_mpls_table.ref | 450 ++ .../rt3/step4/show_isis_flex_algo.ref | 125 + .../rt3/step4/show_mpls_table.ref | 514 ++ .../rt3/step5/show_isis_flex_algo.ref | 125 + .../rt3/step5/show_mpls_table.ref | 514 ++ .../rt3/step6/show_isis_flex_algo.ref | 111 + .../rt3/step6/show_mpls_table.ref | 450 ++ .../rt3/step7/show_isis_flex_algo.ref | 107 + .../rt3/step7/show_mpls_table.ref | 450 ++ .../rt3/step8/show_isis_flex_algo.ref | 125 + .../rt3/step8/show_mpls_table.ref | 514 ++ .../rt3/step9/show_isis_flex_algo.ref | 125 + .../rt3/step9/show_mpls_table.ref | 482 ++ .../isis_sr_flex_algo_topo1/rt3/zebra.conf | 39 + .../test_isis_sr_flex_algo_topo1.py | 631 ++ tests/topotests/isis_sr_flex_algo_topo2/README.md | 8 + .../isis_sr_flex_algo_topo2/configure-te.sh | 46 + .../isis_sr_flex_algo_topo2/rt0/bgpd.conf | 17 + .../isis_sr_flex_algo_topo2/rt0/isisd.conf | 56 + .../isis_sr_flex_algo_topo2/rt0/pathd.conf | 20 + .../isis_sr_flex_algo_topo2/rt0/step1/route.json | 438 ++ .../isis_sr_flex_algo_topo2/rt0/zebra.conf | 34 + .../isis_sr_flex_algo_topo2/rt1/isisd.conf | 60 + .../isis_sr_flex_algo_topo2/rt1/step1/route.json | 428 ++ .../isis_sr_flex_algo_topo2/rt1/zebra.conf | 40 + .../isis_sr_flex_algo_topo2/rt2/isisd.conf | 54 + .../isis_sr_flex_algo_topo2/rt2/step1/route.json | 408 ++ .../isis_sr_flex_algo_topo2/rt2/zebra.conf | 37 + .../isis_sr_flex_algo_topo2/rt3/isisd.conf | 60 + .../isis_sr_flex_algo_topo2/rt3/step1/route.json | 428 ++ .../isis_sr_flex_algo_topo2/rt3/zebra.conf | 40 + .../isis_sr_flex_algo_topo2/rt4/isisd.conf | 52 + .../isis_sr_flex_algo_topo2/rt4/step1/route.json | 286 + .../isis_sr_flex_algo_topo2/rt4/zebra.conf | 31 + .../isis_sr_flex_algo_topo2/rt5/isisd.conf | 60 + .../isis_sr_flex_algo_topo2/rt5/step1/route.json | 428 ++ .../isis_sr_flex_algo_topo2/rt5/zebra.conf | 40 + .../isis_sr_flex_algo_topo2/rt6/isisd.conf | 54 + .../isis_sr_flex_algo_topo2/rt6/step1/route.json | 408 ++ .../isis_sr_flex_algo_topo2/rt6/zebra.conf | 37 + .../isis_sr_flex_algo_topo2/rt7/isisd.conf | 60 + .../isis_sr_flex_algo_topo2/rt7/step1/route.json | 428 ++ .../isis_sr_flex_algo_topo2/rt7/zebra.conf | 40 + .../isis_sr_flex_algo_topo2/rt8/isisd.conf | 50 + .../isis_sr_flex_algo_topo2/rt8/step1/route.json | 286 + .../isis_sr_flex_algo_topo2/rt8/zebra.conf | 29 + .../isis_sr_flex_algo_topo2/rt9/bgpd.conf | 17 + .../isis_sr_flex_algo_topo2/rt9/isisd.conf | 56 + .../isis_sr_flex_algo_topo2/rt9/pathd.conf | 20 + .../isis_sr_flex_algo_topo2/rt9/step1/route.json | 438 ++ .../isis_sr_flex_algo_topo2/rt9/zebra.conf | 34 + .../test_isis_sr_flex_algo_topo2.py | 187 + tests/topotests/isis_sr_te_topo1/dst/zebra.conf | 19 + tests/topotests/isis_sr_te_topo1/rt1/bgpd.conf | 16 + tests/topotests/isis_sr_te_topo1/rt1/isisd.conf | 30 + tests/topotests/isis_sr_te_topo1/rt1/pathd.conf | 21 + .../rt1/step1/show_mpls_table_with_candidate.ref | 91 + .../step1/show_mpls_table_without_candidate.ref | 74 + .../rt1/step2/show_operational_data.ref | 13 + .../step2/show_operational_data_with_candidate.ref | 20 + ...show_operational_data_with_single_candidate.ref | 20 + .../show_operational_data_with_two_candidates.ref | 25 + .../isis_sr_te_topo1/rt1/step4/show_mpls_table.ref | 20 + .../rt1/step4/show_mpls_table_add_segment.ref | 21 + .../rt1/step4/show_mpls_table_change_segment.ref | 21 + .../rt1/step5/show_ip_route_bgp_active_srte.ref | 29 + .../rt1/step5/show_ip_route_bgp_empty.ref | 2 + .../rt1/step5/show_ip_route_bgp_inactive_srte.ref | 38 + .../rt1/step5/show_operational_data_active.ref | 20 + .../rt1/step5/show_operational_data_inactive.ref | 20 + tests/topotests/isis_sr_te_topo1/rt1/zebra.conf | 19 + tests/topotests/isis_sr_te_topo1/rt2/isisd.conf | 41 + tests/topotests/isis_sr_te_topo1/rt2/zebra.conf | 25 + tests/topotests/isis_sr_te_topo1/rt3/isisd.conf | 41 + tests/topotests/isis_sr_te_topo1/rt3/zebra.conf | 25 + tests/topotests/isis_sr_te_topo1/rt4/isisd.conf | 48 + tests/topotests/isis_sr_te_topo1/rt4/zebra.conf | 28 + tests/topotests/isis_sr_te_topo1/rt5/isisd.conf | 48 + tests/topotests/isis_sr_te_topo1/rt5/zebra.conf | 28 + tests/topotests/isis_sr_te_topo1/rt6/bgpd.conf | 12 + tests/topotests/isis_sr_te_topo1/rt6/isisd.conf | 36 + tests/topotests/isis_sr_te_topo1/rt6/pathd.conf | 21 + .../rt6/step1/show_mpls_table_with_candidate.ref | 91 + .../step1/show_mpls_table_without_candidate.ref | 74 + .../rt6/step2/show_operational_data.ref | 13 + .../step2/show_operational_data_with_candidate.ref | 19 + ...show_operational_data_with_single_candidate.ref | 19 + .../show_operational_data_with_two_candidates.ref | 23 + .../isis_sr_te_topo1/rt6/step4/show_mpls_table.ref | 20 + tests/topotests/isis_sr_te_topo1/rt6/zebra.conf | 27 + .../isis_sr_te_topo1/test_isis_sr_te_topo1.py | 857 +++ tests/topotests/isis_sr_topo1/__init__.py | 0 tests/topotests/isis_sr_topo1/rt1/isisd.conf | 32 + .../isis_sr_topo1/rt1/step1/show_ip_route.ref | 327 + .../isis_sr_topo1/rt1/step1/show_ipv6_route.ref | 152 + .../isis_sr_topo1/rt1/step1/show_mpls_table.ref | 170 + .../step1/show_yang_interface_isis_adjacencies.ref | 32 + .../isis_sr_topo1/rt1/step10/show_ip_route.ref | 320 + .../isis_sr_topo1/rt1/step10/show_ipv6_route.ref | 152 + .../isis_sr_topo1/rt1/step10/show_mpls_table.ref | 192 + .../show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt1/step2/show_ip_route.ref | 320 + .../isis_sr_topo1/rt1/step2/show_ipv6_route.ref | 152 + .../isis_sr_topo1/rt1/step2/show_mpls_table.ref | 170 + .../step2/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt1/step3/show_ip_route.ref | 287 + .../isis_sr_topo1/rt1/step3/show_ipv6_route.ref | 121 + .../isis_sr_topo1/rt1/step3/show_mpls_table.ref | 134 + .../step3/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt1/step4/show_ip_route.ref | 320 + .../isis_sr_topo1/rt1/step4/show_ipv6_route.ref | 152 + .../isis_sr_topo1/rt1/step4/show_mpls_table.ref | 170 + .../step4/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt1/step5/show_ip_route.ref | 314 + .../isis_sr_topo1/rt1/step5/show_ipv6_route.ref | 146 + .../isis_sr_topo1/rt1/step5/show_mpls_table.ref | 134 + .../step5/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt1/step6/show_ip_route.ref | 320 + .../isis_sr_topo1/rt1/step6/show_ipv6_route.ref | 152 + .../isis_sr_topo1/rt1/step6/show_mpls_table.ref | 170 + .../step6/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt1/step7/show_ip_route.ref | 320 + .../isis_sr_topo1/rt1/step7/show_ipv6_route.ref | 152 + .../isis_sr_topo1/rt1/step7/show_mpls_table.ref | 170 + .../step7/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt1/step8/show_ip_route.ref | 320 + .../isis_sr_topo1/rt1/step8/show_ipv6_route.ref | 152 + .../isis_sr_topo1/rt1/step8/show_mpls_table.ref | 170 + .../step8/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt1/step9/show_ip_route.ref | 320 + .../isis_sr_topo1/rt1/step9/show_ipv6_route.ref | 152 + .../isis_sr_topo1/rt1/step9/show_mpls_table.ref | 192 + .../step9/show_yang_interface_isis_adjacencies.ref | 1 + tests/topotests/isis_sr_topo1/rt1/zebra.conf | 19 + tests/topotests/isis_sr_topo1/rt2/isisd.conf | 45 + .../isis_sr_topo1/rt2/step1/show_ip_route.ref | 380 ++ .../isis_sr_topo1/rt2/step1/show_ipv6_route.ref | 179 + .../isis_sr_topo1/rt2/step1/show_mpls_table.ref | 228 + .../step1/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_sr_topo1/rt2/step10/show_ip_route.ref | 282 + .../isis_sr_topo1/rt2/step10/show_ipv6_route.ref | 161 + .../isis_sr_topo1/rt2/step10/show_mpls_table.ref | 186 + .../show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt2/step2/show_ip_route.ref | 353 ++ .../isis_sr_topo1/rt2/step2/show_ipv6_route.ref | 161 + .../isis_sr_topo1/rt2/step2/show_mpls_table.ref | 204 + .../step2/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt2/step3/show_ip_route.ref | 306 + .../isis_sr_topo1/rt2/step3/show_ipv6_route.ref | 130 + .../isis_sr_topo1/rt2/step3/show_mpls_table.ref | 168 + .../step3/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt2/step4/show_ip_route.ref | 353 ++ .../isis_sr_topo1/rt2/step4/show_ipv6_route.ref | 161 + .../isis_sr_topo1/rt2/step4/show_mpls_table.ref | 204 + .../step4/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt2/step5/show_ip_route.ref | 347 ++ .../isis_sr_topo1/rt2/step5/show_ipv6_route.ref | 155 + .../isis_sr_topo1/rt2/step5/show_mpls_table.ref | 168 + .../step5/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt2/step6/show_ip_route.ref | 353 ++ .../isis_sr_topo1/rt2/step6/show_ipv6_route.ref | 161 + .../isis_sr_topo1/rt2/step6/show_mpls_table.ref | 204 + .../step6/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt2/step7/show_ip_route.ref | 350 ++ .../isis_sr_topo1/rt2/step7/show_ipv6_route.ref | 158 + .../isis_sr_topo1/rt2/step7/show_mpls_table.ref | 180 + .../step7/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt2/step8/show_ip_route.ref | 353 ++ .../isis_sr_topo1/rt2/step8/show_ipv6_route.ref | 161 + .../isis_sr_topo1/rt2/step8/show_mpls_table.ref | 204 + .../step8/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt2/step9/show_ip_route.ref | 353 ++ .../isis_sr_topo1/rt2/step9/show_ipv6_route.ref | 161 + .../isis_sr_topo1/rt2/step9/show_mpls_table.ref | 204 + .../step9/show_yang_interface_isis_adjacencies.ref | 1 + tests/topotests/isis_sr_topo1/rt2/zebra.conf | 25 + tests/topotests/isis_sr_topo1/rt3/isisd.conf | 45 + .../isis_sr_topo1/rt3/step1/show_ip_route.ref | 380 ++ .../isis_sr_topo1/rt3/step1/show_ipv6_route.ref | 179 + .../isis_sr_topo1/rt3/step1/show_mpls_table.ref | 228 + .../step1/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_sr_topo1/rt3/step10/show_ip_route.ref | 360 ++ .../isis_sr_topo1/rt3/step10/show_ipv6_route.ref | 161 + .../isis_sr_topo1/rt3/step10/show_mpls_table.ref | 204 + .../show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt3/step2/show_ip_route.ref | 360 ++ .../isis_sr_topo1/rt3/step2/show_ipv6_route.ref | 161 + .../isis_sr_topo1/rt3/step2/show_mpls_table.ref | 204 + .../step2/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt3/step3/show_ip_route.ref | 313 + .../isis_sr_topo1/rt3/step3/show_ipv6_route.ref | 130 + .../isis_sr_topo1/rt3/step3/show_mpls_table.ref | 168 + .../step3/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt3/step4/show_ip_route.ref | 360 ++ .../isis_sr_topo1/rt3/step4/show_ipv6_route.ref | 161 + .../isis_sr_topo1/rt3/step4/show_mpls_table.ref | 204 + .../step4/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt3/step5/show_ip_route.ref | 354 ++ .../isis_sr_topo1/rt3/step5/show_ipv6_route.ref | 155 + .../isis_sr_topo1/rt3/step5/show_mpls_table.ref | 168 + .../step5/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt3/step6/show_ip_route.ref | 360 ++ .../isis_sr_topo1/rt3/step6/show_ipv6_route.ref | 161 + .../isis_sr_topo1/rt3/step6/show_mpls_table.ref | 204 + .../step6/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt3/step7/show_ip_route.ref | 357 ++ .../isis_sr_topo1/rt3/step7/show_ipv6_route.ref | 158 + .../isis_sr_topo1/rt3/step7/show_mpls_table.ref | 180 + .../step7/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt3/step8/show_ip_route.ref | 360 ++ .../isis_sr_topo1/rt3/step8/show_ipv6_route.ref | 161 + .../isis_sr_topo1/rt3/step8/show_mpls_table.ref | 204 + .../step8/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt3/step9/show_ip_route.ref | 360 ++ .../isis_sr_topo1/rt3/step9/show_ipv6_route.ref | 161 + .../isis_sr_topo1/rt3/step9/show_mpls_table.ref | 204 + .../step9/show_yang_interface_isis_adjacencies.ref | 1 + tests/topotests/isis_sr_topo1/rt3/zebra.conf | 25 + tests/topotests/isis_sr_topo1/rt4/isisd.conf | 55 + .../isis_sr_topo1/rt4/step1/show_ip_route.ref | 342 ++ .../isis_sr_topo1/rt4/step1/show_ipv6_route.ref | 148 + .../isis_sr_topo1/rt4/step1/show_mpls_table.ref | 215 + .../step1/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_sr_topo1/rt4/step10/show_ip_route.ref | 297 + .../isis_sr_topo1/rt4/step10/show_ipv6_route.ref | 139 + .../isis_sr_topo1/rt4/step10/show_mpls_table.ref | 185 + .../show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt4/step2/show_ip_route.ref | 355 ++ .../isis_sr_topo1/rt4/step2/show_ipv6_route.ref | 139 + .../isis_sr_topo1/rt4/step2/show_mpls_table.ref | 203 + .../step2/show_yang_interface_isis_adjacencies.ref | 63 + .../isis_sr_topo1/rt4/step3/show_ip_route.ref | 336 ++ .../isis_sr_topo1/rt4/step3/show_ipv6_route.ref | 126 + .../isis_sr_topo1/rt4/step3/show_mpls_table.ref | 197 + .../step3/show_yang_interface_isis_adjacencies.ref | 44 + .../isis_sr_topo1/rt4/step4/show_ip_route.ref | 355 ++ .../isis_sr_topo1/rt4/step4/show_ipv6_route.ref | 139 + .../isis_sr_topo1/rt4/step4/show_mpls_table.ref | 203 + .../step4/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt4/step5/show_ip_route.ref | 347 ++ .../isis_sr_topo1/rt4/step5/show_ipv6_route.ref | 133 + .../isis_sr_topo1/rt4/step5/show_mpls_table.ref | 143 + .../step5/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt4/step6/show_ip_route.ref | 355 ++ .../isis_sr_topo1/rt4/step6/show_ipv6_route.ref | 139 + .../isis_sr_topo1/rt4/step6/show_mpls_table.ref | 203 + .../step6/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt4/step7/show_ip_route.ref | 349 ++ .../isis_sr_topo1/rt4/step7/show_ipv6_route.ref | 133 + .../isis_sr_topo1/rt4/step7/show_mpls_table.ref | 167 + .../step7/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt4/step8/show_ip_route.ref | 355 ++ .../isis_sr_topo1/rt4/step8/show_ipv6_route.ref | 139 + .../isis_sr_topo1/rt4/step8/show_mpls_table.ref | 203 + .../step8/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt4/step9/show_ip_route.ref | 355 ++ .../isis_sr_topo1/rt4/step9/show_ipv6_route.ref | 139 + .../isis_sr_topo1/rt4/step9/show_mpls_table.ref | 203 + .../step9/show_yang_interface_isis_adjacencies.ref | 1 + tests/topotests/isis_sr_topo1/rt4/zebra.conf | 30 + tests/topotests/isis_sr_topo1/rt5/isisd.conf | 55 + .../isis_sr_topo1/rt5/step1/show_ip_route.ref | 342 ++ .../isis_sr_topo1/rt5/step1/show_ipv6_route.ref | 148 + .../isis_sr_topo1/rt5/step1/show_mpls_table.ref | 215 + .../step1/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_sr_topo1/rt5/step10/show_ip_route.ref | 331 ++ .../isis_sr_topo1/rt5/step10/show_ipv6_route.ref | 139 + .../isis_sr_topo1/rt5/step10/show_mpls_table.ref | 203 + .../show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt5/step2/show_ip_route.ref | 338 ++ .../isis_sr_topo1/rt5/step2/show_ipv6_route.ref | 139 + .../isis_sr_topo1/rt5/step2/show_mpls_table.ref | 203 + .../step2/show_yang_interface_isis_adjacencies.ref | 63 + .../isis_sr_topo1/rt5/step3/show_ip_route.ref | 312 + .../isis_sr_topo1/rt5/step3/show_ipv6_route.ref | 126 + .../isis_sr_topo1/rt5/step3/show_mpls_table.ref | 197 + .../step3/show_yang_interface_isis_adjacencies.ref | 44 + .../isis_sr_topo1/rt5/step4/show_ip_route.ref | 323 + .../isis_sr_topo1/rt5/step4/show_ipv6_route.ref | 139 + .../isis_sr_topo1/rt5/step4/show_mpls_table.ref | 203 + .../step4/show_yang_interface_isis_adjacencies.ref | 63 + .../isis_sr_topo1/rt5/step5/show_ip_route.ref | 315 + .../isis_sr_topo1/rt5/step5/show_ipv6_route.ref | 133 + .../isis_sr_topo1/rt5/step5/show_mpls_table.ref | 143 + .../step5/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt5/step6/show_ip_route.ref | 323 + .../isis_sr_topo1/rt5/step6/show_ipv6_route.ref | 139 + .../isis_sr_topo1/rt5/step6/show_mpls_table.ref | 203 + .../step6/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt5/step7/show_ip_route.ref | 317 + .../isis_sr_topo1/rt5/step7/show_ipv6_route.ref | 133 + .../isis_sr_topo1/rt5/step7/show_mpls_table.ref | 167 + .../step7/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt5/step8/show_ip_route.ref | 323 + .../isis_sr_topo1/rt5/step8/show_ipv6_route.ref | 139 + .../isis_sr_topo1/rt5/step8/show_mpls_table.ref | 203 + .../step8/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt5/step9/show_ip_route.ref | 323 + .../isis_sr_topo1/rt5/step9/show_ipv6_route.ref | 139 + .../isis_sr_topo1/rt5/step9/show_mpls_table.ref | 203 + .../step9/show_yang_interface_isis_adjacencies.ref | 1 + tests/topotests/isis_sr_topo1/rt5/zebra.conf | 30 + tests/topotests/isis_sr_topo1/rt6/isisd.conf | 39 + .../isis_sr_topo1/rt6/step1/show_ip_route.ref | 324 + .../isis_sr_topo1/rt6/step1/show_ipv6_route.ref | 152 + .../isis_sr_topo1/rt6/step1/show_mpls_table.ref | 170 + .../step1/show_yang_interface_isis_adjacencies.ref | 44 + .../isis_sr_topo1/rt6/step10/show_ip_route.ref | 317 + .../isis_sr_topo1/rt6/step10/show_ipv6_route.ref | 152 + .../isis_sr_topo1/rt6/step10/show_mpls_table.ref | 170 + .../show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt6/step2/show_ip_route.ref | 317 + .../isis_sr_topo1/rt6/step2/show_ipv6_route.ref | 152 + .../isis_sr_topo1/rt6/step2/show_mpls_table.ref | 170 + .../step2/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt6/step3/show_ip_route.ref | 2 + .../isis_sr_topo1/rt6/step3/show_ipv6_route.ref | 2 + .../isis_sr_topo1/rt6/step3/show_mpls_table.ref | 2 + .../step3/show_yang_interface_isis_adjacencies.ref | 3 + .../isis_sr_topo1/rt6/step4/show_ip_route.ref | 317 + .../isis_sr_topo1/rt6/step4/show_ipv6_route.ref | 152 + .../isis_sr_topo1/rt6/step4/show_mpls_table.ref | 170 + .../step4/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt6/step5/show_ip_route.ref | 293 + .../isis_sr_topo1/rt6/step5/show_ipv6_route.ref | 128 + .../isis_sr_topo1/rt6/step5/show_mpls_table.ref | 2 + .../step5/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt6/step6/show_ip_route.ref | 317 + .../isis_sr_topo1/rt6/step6/show_ipv6_route.ref | 152 + .../isis_sr_topo1/rt6/step6/show_mpls_table.ref | 170 + .../step6/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt6/step7/show_ip_route.ref | 311 + .../isis_sr_topo1/rt6/step7/show_ipv6_route.ref | 146 + .../isis_sr_topo1/rt6/step7/show_mpls_table.ref | 134 + .../step7/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt6/step8/show_ip_route.ref | 317 + .../isis_sr_topo1/rt6/step8/show_ipv6_route.ref | 152 + .../isis_sr_topo1/rt6/step8/show_mpls_table.ref | 170 + .../step8/show_yang_interface_isis_adjacencies.ref | 1 + .../isis_sr_topo1/rt6/step9/show_ip_route.ref | 317 + .../isis_sr_topo1/rt6/step9/show_ipv6_route.ref | 152 + .../isis_sr_topo1/rt6/step9/show_mpls_table.ref | 170 + .../step9/show_yang_interface_isis_adjacencies.ref | 1 + tests/topotests/isis_sr_topo1/rt6/zebra.conf | 22 + .../topotests/isis_sr_topo1/test_isis_sr_topo1.py | 1073 ++++ tests/topotests/isis_srv6_topo1/dst/sharpd.conf | 0 tests/topotests/isis_srv6_topo1/dst/zebra.conf | 22 + tests/topotests/isis_srv6_topo1/rt1/isisd.conf | 35 + tests/topotests/isis_srv6_topo1/rt1/sharpd.conf | 0 .../isis_srv6_topo1/rt1/step1/show_ip_route.ref | 276 + .../isis_srv6_topo1/rt1/step1/show_ipv6_route.ref | 270 + .../rt1/step1/show_srv6_locator_table.ref | 19 + .../step1/show_yang_interface_isis_adjacencies.ref | 32 + .../isis_srv6_topo1/rt1/step2/show_ip_route.ref | 276 + .../isis_srv6_topo1/rt1/step2/show_ipv6_route.ref | 204 + .../rt1/step2/show_srv6_locator_table.ref | 2 + .../step2/show_yang_interface_isis_adjacencies.ref | 32 + .../isis_srv6_topo1/rt1/step3/show_ip_route.ref | 276 + .../isis_srv6_topo1/rt1/step3/show_ipv6_route.ref | 270 + .../rt1/step3/show_srv6_locator_table.ref | 19 + .../step3/show_yang_interface_isis_adjacencies.ref | 32 + .../isis_srv6_topo1/rt1/step4/show_ip_route.ref | 276 + .../isis_srv6_topo1/rt1/step4/show_ipv6_route.ref | 204 + .../rt1/step4/show_srv6_locator_table.ref | 19 + .../step4/show_yang_interface_isis_adjacencies.ref | 32 + .../isis_srv6_topo1/rt1/step5/show_ip_route.ref | 276 + .../isis_srv6_topo1/rt1/step5/show_ipv6_route.ref | 270 + .../rt1/step5/show_srv6_locator_table.ref | 19 + .../step5/show_yang_interface_isis_adjacencies.ref | 32 + .../isis_srv6_topo1/rt1/step6/show_ip_route.ref | 276 + .../isis_srv6_topo1/rt1/step6/show_ipv6_route.ref | 204 + .../rt1/step6/show_srv6_locator_table.ref | 19 + .../step6/show_yang_interface_isis_adjacencies.ref | 32 + .../isis_srv6_topo1/rt1/step7/show_ip_route.ref | 276 + .../isis_srv6_topo1/rt1/step7/show_ipv6_route.ref | 270 + .../rt1/step7/show_srv6_locator_table.ref | 19 + .../step7/show_yang_interface_isis_adjacencies.ref | 32 + .../isis_srv6_topo1/rt1/step8/show_ip_route.ref | 276 + .../isis_srv6_topo1/rt1/step8/show_ipv6_route.ref | 204 + .../rt1/step8/show_srv6_locator_table.ref | 2 + .../step8/show_yang_interface_isis_adjacencies.ref | 32 + .../isis_srv6_topo1/rt1/step9/show_ip_route.ref | 276 + .../isis_srv6_topo1/rt1/step9/show_ipv6_route.ref | 270 + .../rt1/step9/show_srv6_locator_table.ref | 19 + .../step9/show_yang_interface_isis_adjacencies.ref | 32 + tests/topotests/isis_srv6_topo1/rt1/zebra.conf | 28 + tests/topotests/isis_srv6_topo1/rt2/isisd.conf | 48 + .../isis_srv6_topo1/rt2/step1/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt2/step1/show_ipv6_route.ref | 346 ++ .../rt2/step1/show_srv6_locator_table.ref | 19 + .../step1/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt2/step2/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt2/step2/show_ipv6_route.ref | 327 + .../rt2/step2/show_srv6_locator_table.ref | 19 + .../step2/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt2/step3/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt2/step3/show_ipv6_route.ref | 346 ++ .../rt2/step3/show_srv6_locator_table.ref | 19 + .../step3/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt2/step4/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt2/step4/show_ipv6_route.ref | 327 + .../rt2/step4/show_srv6_locator_table.ref | 19 + .../step4/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt2/step5/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt2/step5/show_ipv6_route.ref | 346 ++ .../rt2/step5/show_srv6_locator_table.ref | 19 + .../step5/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt2/step6/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt2/step6/show_ipv6_route.ref | 327 + .../rt2/step6/show_srv6_locator_table.ref | 19 + .../step6/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt2/step7/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt2/step7/show_ipv6_route.ref | 346 ++ .../rt2/step7/show_srv6_locator_table.ref | 19 + .../step7/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt2/step8/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt2/step8/show_ipv6_route.ref | 327 + .../rt2/step8/show_srv6_locator_table.ref | 19 + .../step8/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt2/step9/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt2/step9/show_ipv6_route.ref | 346 ++ .../rt2/step9/show_srv6_locator_table.ref | 19 + .../step9/show_yang_interface_isis_adjacencies.ref | 70 + tests/topotests/isis_srv6_topo1/rt2/zebra.conf | 34 + tests/topotests/isis_srv6_topo1/rt3/isisd.conf | 48 + .../isis_srv6_topo1/rt3/step1/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt3/step1/show_ipv6_route.ref | 346 ++ .../rt3/step1/show_srv6_locator_table.ref | 19 + .../step1/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt3/step2/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt3/step2/show_ipv6_route.ref | 327 + .../rt3/step2/show_srv6_locator_table.ref | 19 + .../step2/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt3/step3/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt3/step3/show_ipv6_route.ref | 346 ++ .../rt3/step3/show_srv6_locator_table.ref | 19 + .../step3/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt3/step4/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt3/step4/show_ipv6_route.ref | 327 + .../rt3/step4/show_srv6_locator_table.ref | 19 + .../step4/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt3/step5/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt3/step5/show_ipv6_route.ref | 346 ++ .../rt3/step5/show_srv6_locator_table.ref | 19 + .../step5/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt3/step6/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt3/step6/show_ipv6_route.ref | 327 + .../rt3/step6/show_srv6_locator_table.ref | 19 + .../step6/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt3/step7/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt3/step7/show_ipv6_route.ref | 346 ++ .../rt3/step7/show_srv6_locator_table.ref | 19 + .../step7/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt3/step8/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt3/step8/show_ipv6_route.ref | 327 + .../rt3/step8/show_srv6_locator_table.ref | 19 + .../step8/show_yang_interface_isis_adjacencies.ref | 70 + .../isis_srv6_topo1/rt3/step9/show_ip_route.ref | 320 + .../isis_srv6_topo1/rt3/step9/show_ipv6_route.ref | 346 ++ .../rt3/step9/show_srv6_locator_table.ref | 19 + .../step9/show_yang_interface_isis_adjacencies.ref | 70 + tests/topotests/isis_srv6_topo1/rt3/zebra.conf | 33 + tests/topotests/isis_srv6_topo1/rt4/isisd.conf | 56 + .../isis_srv6_topo1/rt4/step1/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt4/step1/show_ipv6_route.ref | 346 ++ .../rt4/step1/show_srv6_locator_table.ref | 19 + .../step1/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt4/step2/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt4/step2/show_ipv6_route.ref | 321 + .../rt4/step2/show_srv6_locator_table.ref | 19 + .../step2/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt4/step3/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt4/step3/show_ipv6_route.ref | 346 ++ .../rt4/step3/show_srv6_locator_table.ref | 19 + .../step3/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt4/step4/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt4/step4/show_ipv6_route.ref | 321 + .../rt4/step4/show_srv6_locator_table.ref | 19 + .../step4/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt4/step5/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt4/step5/show_ipv6_route.ref | 346 ++ .../rt4/step5/show_srv6_locator_table.ref | 19 + .../step5/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt4/step6/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt4/step6/show_ipv6_route.ref | 321 + .../rt4/step6/show_srv6_locator_table.ref | 19 + .../step6/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt4/step7/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt4/step7/show_ipv6_route.ref | 346 ++ .../rt4/step7/show_srv6_locator_table.ref | 19 + .../step7/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt4/step8/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt4/step8/show_ipv6_route.ref | 321 + .../rt4/step8/show_srv6_locator_table.ref | 19 + .../step8/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt4/step9/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt4/step9/show_ipv6_route.ref | 346 ++ .../rt4/step9/show_srv6_locator_table.ref | 19 + .../step9/show_yang_interface_isis_adjacencies.ref | 82 + tests/topotests/isis_srv6_topo1/rt4/zebra.conf | 36 + tests/topotests/isis_srv6_topo1/rt5/isisd.conf | 56 + .../isis_srv6_topo1/rt5/step1/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt5/step1/show_ipv6_route.ref | 346 ++ .../rt5/step1/show_srv6_locator_table.ref | 19 + .../step1/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt5/step2/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt5/step2/show_ipv6_route.ref | 321 + .../rt5/step2/show_srv6_locator_table.ref | 19 + .../step2/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt5/step3/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt5/step3/show_ipv6_route.ref | 346 ++ .../rt5/step3/show_srv6_locator_table.ref | 19 + .../step3/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt5/step4/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt5/step4/show_ipv6_route.ref | 321 + .../rt5/step4/show_srv6_locator_table.ref | 19 + .../step4/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt5/step5/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt5/step5/show_ipv6_route.ref | 346 ++ .../rt5/step5/show_srv6_locator_table.ref | 19 + .../step5/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt5/step6/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt5/step6/show_ipv6_route.ref | 321 + .../rt5/step6/show_srv6_locator_table.ref | 19 + .../step6/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt5/step7/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt5/step7/show_ipv6_route.ref | 346 ++ .../rt5/step7/show_srv6_locator_table.ref | 19 + .../step7/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt5/step8/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt5/step8/show_ipv6_route.ref | 321 + .../rt5/step8/show_srv6_locator_table.ref | 19 + .../step8/show_yang_interface_isis_adjacencies.ref | 82 + .../isis_srv6_topo1/rt5/step9/show_ip_route.ref | 296 + .../isis_srv6_topo1/rt5/step9/show_ipv6_route.ref | 346 ++ .../rt5/step9/show_srv6_locator_table.ref | 19 + .../step9/show_yang_interface_isis_adjacencies.ref | 82 + tests/topotests/isis_srv6_topo1/rt5/zebra.conf | 36 + tests/topotests/isis_srv6_topo1/rt6/isisd.conf | 42 + tests/topotests/isis_srv6_topo1/rt6/sharpd.conf | 0 .../isis_srv6_topo1/rt6/step1/show_ip_route.ref | 273 + .../isis_srv6_topo1/rt6/step1/show_ipv6_route.ref | 268 + .../rt6/step1/show_srv6_locator_table.ref | 19 + .../step1/show_yang_interface_isis_adjacencies.ref | 44 + .../isis_srv6_topo1/rt6/step2/show_ip_route.ref | 273 + .../isis_srv6_topo1/rt6/step2/show_ipv6_route.ref | 243 + .../rt6/step2/show_srv6_locator_table.ref | 19 + .../step2/show_yang_interface_isis_adjacencies.ref | 44 + .../isis_srv6_topo1/rt6/step3/show_ip_route.ref | 273 + .../isis_srv6_topo1/rt6/step3/show_ipv6_route.ref | 268 + .../rt6/step3/show_srv6_locator_table.ref | 19 + .../step3/show_yang_interface_isis_adjacencies.ref | 44 + .../isis_srv6_topo1/rt6/step4/show_ip_route.ref | 273 + .../isis_srv6_topo1/rt6/step4/show_ipv6_route.ref | 243 + .../rt6/step4/show_srv6_locator_table.ref | 19 + .../step4/show_yang_interface_isis_adjacencies.ref | 44 + .../isis_srv6_topo1/rt6/step5/show_ip_route.ref | 273 + .../isis_srv6_topo1/rt6/step5/show_ipv6_route.ref | 268 + .../rt6/step5/show_srv6_locator_table.ref | 19 + .../step5/show_yang_interface_isis_adjacencies.ref | 44 + .../isis_srv6_topo1/rt6/step6/show_ip_route.ref | 273 + .../isis_srv6_topo1/rt6/step6/show_ipv6_route.ref | 243 + .../rt6/step6/show_srv6_locator_table.ref | 19 + .../step6/show_yang_interface_isis_adjacencies.ref | 44 + .../isis_srv6_topo1/rt6/step7/show_ip_route.ref | 273 + .../isis_srv6_topo1/rt6/step7/show_ipv6_route.ref | 268 + .../rt6/step7/show_srv6_locator_table.ref | 19 + .../step7/show_yang_interface_isis_adjacencies.ref | 44 + .../isis_srv6_topo1/rt6/step8/show_ip_route.ref | 273 + .../isis_srv6_topo1/rt6/step8/show_ipv6_route.ref | 243 + .../rt6/step8/show_srv6_locator_table.ref | 19 + .../step8/show_yang_interface_isis_adjacencies.ref | 44 + .../isis_srv6_topo1/rt6/step9/show_ip_route.ref | 273 + .../isis_srv6_topo1/rt6/step9/show_ipv6_route.ref | 268 + .../rt6/step9/show_srv6_locator_table.ref | 19 + .../step9/show_yang_interface_isis_adjacencies.ref | 44 + tests/topotests/isis_srv6_topo1/rt6/zebra.conf | 36 + .../isis_srv6_topo1/test_isis_srv6_topo1.py | 1124 ++++ tests/topotests/isis_te_topo1/__init__.py | 0 tests/topotests/isis_te_topo1/r1/isisd.conf | 33 + tests/topotests/isis_te_topo1/r1/zebra.conf | 26 + tests/topotests/isis_te_topo1/r2/isisd.conf | 49 + tests/topotests/isis_te_topo1/r2/zebra.conf | 40 + tests/topotests/isis_te_topo1/r3/isisd.conf | 36 + tests/topotests/isis_te_topo1/r3/zebra.conf | 25 + tests/topotests/isis_te_topo1/r4/isisd.conf | 41 + tests/topotests/isis_te_topo1/r4/zebra.conf | 22 + .../isis_te_topo1/reference/ted_step1.json | 838 +++ .../isis_te_topo1/reference/ted_step10.json | 967 +++ .../isis_te_topo1/reference/ted_step2.json | 642 ++ .../isis_te_topo1/reference/ted_step3.json | 742 +++ .../isis_te_topo1/reference/ted_step4.json | 742 +++ .../isis_te_topo1/reference/ted_step5.json | 938 +++ .../isis_te_topo1/reference/ted_step6.json | 939 +++ .../isis_te_topo1/reference/ted_step7.json | 969 +++ .../isis_te_topo1/reference/ted_step8.json | 969 +++ .../isis_te_topo1/reference/ted_step9.json | 1 + .../topotests/isis_te_topo1/test_isis_te_topo1.py | 306 + tests/topotests/isis_tilfa_topo1/__init__.py | 0 tests/topotests/isis_tilfa_topo1/rt1/isisd.conf | 35 + .../isis_tilfa_topo1/rt1/step1/show_ip_route.ref | 294 + .../isis_tilfa_topo1/rt1/step1/show_ipv6_route.ref | 121 + .../isis_tilfa_topo1/rt1/step1/show_mpls_table.ref | 134 + .../step1/show_yang_interface_isis_adjacencies.ref | 32 + .../rt1/step10/show_ip_route.ref.diff | 0 .../rt1/step10/show_ipv6_route.ref.diff | 0 .../rt1/step10/show_mpls_table.ref.diff | 0 .../rt1/step11/show_ip_route.ref.diff | 0 .../rt1/step11/show_ipv6_route.ref.diff | 0 .../rt1/step11/show_mpls_table.ref.diff | 0 .../rt1/step12/show_ip_route.ref.diff | 19 + .../rt1/step12/show_ipv6_route.ref.diff | 18 + .../rt1/step12/show_mpls_table.ref.diff | 28 + .../rt1/step2/show_ip_route.ref.diff | 0 .../rt1/step2/show_ipv6_route.ref.diff | 0 .../rt1/step2/show_mpls_table.ref.diff | 0 .../rt1/step3/show_ip_route.ref.diff | 0 .../rt1/step3/show_ipv6_route.ref.diff | 0 .../rt1/step3/show_mpls_table.ref.diff | 0 .../rt1/step4/show_ip_route.ref.diff | 14 + .../rt1/step4/show_ipv6_route.ref.diff | 14 + .../rt1/step4/show_mpls_table.ref.diff | 33 + .../rt1/step5/show_ip_route.ref.diff | 14 + .../rt1/step5/show_ipv6_route.ref.diff | 14 + .../rt1/step5/show_mpls_table.ref.diff | 33 + .../rt1/step6/show_ip_route.ref.diff | 0 .../rt1/step6/show_ipv6_route.ref.diff | 0 .../rt1/step6/show_mpls_table.ref.diff | 0 .../rt1/step7/show_ip_route.ref.diff | 14 + .../rt1/step7/show_ipv6_route.ref.diff | 14 + .../rt1/step7/show_mpls_table.ref.diff | 33 + .../rt1/step8/show_ip_route.ref.diff | 14 + .../rt1/step8/show_ipv6_route.ref.diff | 14 + .../rt1/step8/show_mpls_table.ref.diff | 33 + .../rt1/step9/show_ip_route.ref.diff | 11 + .../rt1/step9/show_ipv6_route.ref.diff | 11 + .../rt1/step9/show_mpls_table.ref.diff | 64 + tests/topotests/isis_tilfa_topo1/rt1/zebra.conf | 20 + tests/topotests/isis_tilfa_topo1/rt2/isisd.conf | 49 + .../isis_tilfa_topo1/rt2/step1/show_ip_route.ref | 563 ++ .../isis_tilfa_topo1/rt2/step1/show_ipv6_route.ref | 229 + .../isis_tilfa_topo1/rt2/step1/show_mpls_table.ref | 286 + .../step1/show_yang_interface_isis_adjacencies.ref | 70 + .../rt2/step10/show_ip_route.ref.diff | 0 .../rt2/step10/show_ipv6_route.ref.diff | 0 .../rt2/step10/show_mpls_table.ref.diff | 0 .../rt2/step11/show_ip_route.ref.diff | 0 .../rt2/step11/show_ipv6_route.ref.diff | 0 .../rt2/step11/show_mpls_table.ref.diff | 0 .../rt2/step12/show_ip_route.ref.diff | 0 .../rt2/step12/show_ipv6_route.ref.diff | 0 .../rt2/step12/show_mpls_table.ref.diff | 20 + .../rt2/step2/show_ip_route.ref.diff | 169 + .../rt2/step2/show_ipv6_route.ref.diff | 72 + .../rt2/step2/show_mpls_table.ref.diff | 102 + .../rt2/step3/show_ip_route.ref.diff | 169 + .../rt2/step3/show_ipv6_route.ref.diff | 72 + .../rt2/step3/show_mpls_table.ref.diff | 102 + .../rt2/step4/show_ip_route.ref.diff | 192 + .../rt2/step4/show_ipv6_route.ref.diff | 146 + .../rt2/step4/show_mpls_table.ref.diff | 200 + .../rt2/step5/show_ip_route.ref.diff | 192 + .../rt2/step5/show_ipv6_route.ref.diff | 146 + .../rt2/step5/show_mpls_table.ref.diff | 200 + .../rt2/step6/show_ip_route.ref.diff | 0 .../rt2/step6/show_ipv6_route.ref.diff | 0 .../rt2/step6/show_mpls_table.ref.diff | 0 .../rt2/step7/show_ip_route.ref.diff | 288 + .../rt2/step7/show_ipv6_route.ref.diff | 139 + .../rt2/step7/show_mpls_table.ref.diff | 207 + .../rt2/step8/show_ip_route.ref.diff | 288 + .../rt2/step8/show_ipv6_route.ref.diff | 139 + .../rt2/step8/show_mpls_table.ref.diff | 207 + .../rt2/step9/show_ip_route.ref.diff | 119 + .../rt2/step9/show_ipv6_route.ref.diff | 74 + .../rt2/step9/show_mpls_table.ref.diff | 182 + tests/topotests/isis_tilfa_topo1/rt2/zebra.conf | 26 + tests/topotests/isis_tilfa_topo1/rt3/isisd.conf | 49 + .../isis_tilfa_topo1/rt3/step1/show_ip_route.ref | 563 ++ .../isis_tilfa_topo1/rt3/step1/show_ipv6_route.ref | 229 + .../isis_tilfa_topo1/rt3/step1/show_mpls_table.ref | 286 + .../step1/show_yang_interface_isis_adjacencies.ref | 70 + .../rt3/step10/show_ip_route.ref.diff | 0 .../rt3/step10/show_ipv6_route.ref.diff | 0 .../rt3/step10/show_mpls_table.ref.diff | 0 .../rt3/step11/show_ip_route.ref.diff | 0 .../rt3/step11/show_ipv6_route.ref.diff | 0 .../rt3/step11/show_mpls_table.ref.diff | 0 .../rt3/step12/show_ip_route.ref.diff | 58 + .../rt3/step12/show_ipv6_route.ref.diff | 45 + .../rt3/step12/show_mpls_table.ref.diff | 60 + .../rt3/step2/show_ip_route.ref.diff | 0 .../rt3/step2/show_ipv6_route.ref.diff | 0 .../rt3/step2/show_mpls_table.ref.diff | 0 .../rt3/step3/show_ip_route.ref.diff | 0 .../rt3/step3/show_ipv6_route.ref.diff | 0 .../rt3/step3/show_mpls_table.ref.diff | 0 .../rt3/step4/show_ip_route.ref.diff | 288 + .../rt3/step4/show_ipv6_route.ref.diff | 139 + .../rt3/step4/show_mpls_table.ref.diff | 206 + .../rt3/step5/show_ip_route.ref.diff | 288 + .../rt3/step5/show_ipv6_route.ref.diff | 139 + .../rt3/step5/show_mpls_table.ref.diff | 206 + .../rt3/step6/show_ip_route.ref.diff | 101 + .../rt3/step6/show_ipv6_route.ref.diff | 83 + .../rt3/step6/show_mpls_table.ref.diff | 130 + .../rt3/step7/show_ip_route.ref.diff | 32 + .../rt3/step7/show_ipv6_route.ref.diff | 32 + .../rt3/step7/show_mpls_table.ref.diff | 71 + .../rt3/step8/show_ip_route.ref.diff | 32 + .../rt3/step8/show_ipv6_route.ref.diff | 32 + .../rt3/step8/show_mpls_table.ref.diff | 71 + .../rt3/step9/show_ip_route.ref.diff | 11 + .../rt3/step9/show_ipv6_route.ref.diff | 11 + .../rt3/step9/show_mpls_table.ref.diff | 133 + tests/topotests/isis_tilfa_topo1/rt3/zebra.conf | 26 + tests/topotests/isis_tilfa_topo1/rt4/isisd.conf | 58 + .../isis_tilfa_topo1/rt4/step1/show_ip_route.ref | 506 ++ .../isis_tilfa_topo1/rt4/step1/show_ipv6_route.ref | 207 + .../isis_tilfa_topo1/rt4/step1/show_mpls_table.ref | 262 + .../step1/show_yang_interface_isis_adjacencies.ref | 82 + .../rt4/step10/show_ip_route.ref.diff | 0 .../rt4/step10/show_ipv6_route.ref.diff | 0 .../rt4/step10/show_mpls_table.ref.diff | 0 .../rt4/step11/show_ip_route.ref.diff | 0 .../rt4/step11/show_ipv6_route.ref.diff | 0 .../rt4/step11/show_mpls_table.ref.diff | 0 .../rt4/step12/show_ip_route.ref.diff | 144 + .../rt4/step12/show_ipv6_route.ref.diff | 50 + .../rt4/step12/show_mpls_table.ref.diff | 78 + .../rt4/step2/show_ip_route.ref.diff | 0 .../rt4/step2/show_ipv6_route.ref.diff | 0 .../rt4/step2/show_mpls_table.ref.diff | 0 .../rt4/step3/show_ip_route.ref.diff | 0 .../rt4/step3/show_ipv6_route.ref.diff | 0 .../rt4/step3/show_mpls_table.ref.diff | 0 .../rt4/step4/show_ip_route.ref.diff | 367 ++ .../rt4/step4/show_ipv6_route.ref.diff | 161 + .../rt4/step4/show_mpls_table.ref.diff | 265 + .../rt4/step5/show_ip_route.ref.diff | 367 ++ .../rt4/step5/show_ipv6_route.ref.diff | 161 + .../rt4/step5/show_mpls_table.ref.diff | 265 + .../rt4/step6/show_ip_route.ref.diff | 56 + .../rt4/step6/show_ipv6_route.ref.diff | 38 + .../rt4/step6/show_mpls_table.ref.diff | 74 + .../rt4/step7/show_ip_route.ref.diff | 24 + .../rt4/step7/show_ipv6_route.ref.diff | 24 + .../rt4/step7/show_mpls_table.ref.diff | 53 + .../rt4/step8/show_ip_route.ref.diff | 24 + .../rt4/step8/show_ipv6_route.ref.diff | 24 + .../rt4/step8/show_mpls_table.ref.diff | 53 + .../rt4/step9/show_ip_route.ref.diff | 11 + .../rt4/step9/show_ipv6_route.ref.diff | 11 + .../rt4/step9/show_mpls_table.ref.diff | 110 + tests/topotests/isis_tilfa_topo1/rt4/zebra.conf | 29 + tests/topotests/isis_tilfa_topo1/rt5/bfdd.conf | 14 + tests/topotests/isis_tilfa_topo1/rt5/isisd.conf | 58 + .../isis_tilfa_topo1/rt5/step1/show_ip_route.ref | 506 ++ .../isis_tilfa_topo1/rt5/step1/show_ipv6_route.ref | 207 + .../isis_tilfa_topo1/rt5/step1/show_mpls_table.ref | 262 + .../step1/show_yang_interface_isis_adjacencies.ref | 82 + .../rt5/step10/show_ip_route.ref.diff | 0 .../rt5/step10/show_ipv6_route.ref.diff | 0 .../rt5/step10/show_mpls_table.ref.diff | 0 .../rt5/step11/show_ip_route.ref.diff | 0 .../rt5/step11/show_ipv6_route.ref.diff | 0 .../rt5/step11/show_mpls_table.ref.diff | 0 .../rt5/step12/show_ip_route.ref.diff | 151 + .../rt5/step12/show_ipv6_route.ref.diff | 53 + .../rt5/step12/show_mpls_table.ref.diff | 80 + .../rt5/step2/show_ip_route.ref.diff | 0 .../rt5/step2/show_ipv6_route.ref.diff | 0 .../rt5/step2/show_mpls_table.ref.diff | 0 .../rt5/step3/show_ip_route.ref.diff | 0 .../rt5/step3/show_ipv6_route.ref.diff | 0 .../rt5/step3/show_mpls_table.ref.diff | 0 .../rt5/step4/show_ip_route.ref.diff | 161 + .../rt5/step4/show_ipv6_route.ref.diff | 95 + .../rt5/step4/show_mpls_table.ref.diff | 166 + .../rt5/step5/show_ip_route.ref.diff | 161 + .../rt5/step5/show_ipv6_route.ref.diff | 95 + .../rt5/step5/show_mpls_table.ref.diff | 166 + .../rt5/step6/show_ip_route.ref.diff | 0 .../rt5/step6/show_ipv6_route.ref.diff | 0 .../rt5/step6/show_mpls_table.ref.diff | 146 + .../rt5/step7/show_ip_route.ref.diff | 0 .../rt5/step7/show_ipv6_route.ref.diff | 0 .../rt5/step7/show_mpls_table.ref.diff | 0 .../rt5/step8/show_ip_route.ref.diff | 0 .../rt5/step8/show_ipv6_route.ref.diff | 0 .../rt5/step8/show_mpls_table.ref.diff | 0 .../rt5/step9/show_ip_route.ref.diff | 0 .../rt5/step9/show_ipv6_route.ref.diff | 0 .../rt5/step9/show_mpls_table.ref.diff | 0 tests/topotests/isis_tilfa_topo1/rt5/zebra.conf | 29 + tests/topotests/isis_tilfa_topo1/rt6/bfdd.conf | 14 + tests/topotests/isis_tilfa_topo1/rt6/isisd.conf | 42 + .../isis_tilfa_topo1/rt6/step1/show_ip_route.ref | 413 ++ .../isis_tilfa_topo1/rt6/step1/show_ipv6_route.ref | 173 + .../isis_tilfa_topo1/rt6/step1/show_mpls_table.ref | 214 + .../step1/show_yang_interface_isis_adjacencies.ref | 44 + .../rt6/step10/show_ip_route.ref.diff | 0 .../rt6/step10/show_ipv6_route.ref.diff | 0 .../rt6/step10/show_mpls_table.ref.diff | 0 .../rt6/step11/show_ip_route.ref.diff | 125 + .../rt6/step11/show_ipv6_route.ref.diff | 56 + .../rt6/step11/show_mpls_table.ref.diff | 106 + .../rt6/step12/show_ip_route.ref.diff | 153 + .../rt6/step12/show_ipv6_route.ref.diff | 66 + .../rt6/step12/show_mpls_table.ref.diff | 78 + .../rt6/step2/show_ip_route.ref.diff | 0 .../rt6/step2/show_ipv6_route.ref.diff | 0 .../rt6/step2/show_mpls_table.ref.diff | 0 .../rt6/step3/show_ip_route.ref.diff | 0 .../rt6/step3/show_ipv6_route.ref.diff | 0 .../rt6/step3/show_mpls_table.ref.diff | 0 .../rt6/step4/show_ip_route.ref.diff | 70 + .../rt6/step4/show_ipv6_route.ref.diff | 70 + .../rt6/step4/show_mpls_table.ref.diff | 109 + .../rt6/step5/show_ip_route.ref.diff | 70 + .../rt6/step5/show_ipv6_route.ref.diff | 70 + .../rt6/step5/show_mpls_table.ref.diff | 112 + .../rt6/step6/show_ip_route.ref.diff | 38 + .../rt6/step6/show_ipv6_route.ref.diff | 38 + .../rt6/step6/show_mpls_table.ref.diff | 74 + .../rt6/step7/show_ip_route.ref.diff | 24 + .../rt6/step7/show_ipv6_route.ref.diff | 24 + .../rt6/step7/show_mpls_table.ref.diff | 52 + .../rt6/step8/show_ip_route.ref.diff | 24 + .../rt6/step8/show_ipv6_route.ref.diff | 24 + .../rt6/step8/show_mpls_table.ref.diff | 52 + .../rt6/step9/show_ip_route.ref.diff | 11 + .../rt6/step9/show_ipv6_route.ref.diff | 11 + .../rt6/step9/show_mpls_table.ref.diff | 39 + tests/topotests/isis_tilfa_topo1/rt6/zebra.conf | 23 + .../isis_tilfa_topo1/test_isis_tilfa_topo1.py | 1113 ++++ tests/topotests/isis_topo1/__init__.py | 0 tests/topotests/isis_topo1/r1/isisd.conf | 17 + tests/topotests/isis_topo1/r1/r1_route.json | 68 + tests/topotests/isis_topo1/r1/r1_route6.json | 66 + tests/topotests/isis_topo1/r1/r1_route6_linux.json | 14 + tests/topotests/isis_topo1/r1/r1_route_linux.json | 14 + tests/topotests/isis_topo1/r1/r1_topology.json | 96 + tests/topotests/isis_topo1/r1/zebra.conf | 9 + tests/topotests/isis_topo1/r2/isisd.conf | 17 + tests/topotests/isis_topo1/r2/r2_route.json | 68 + tests/topotests/isis_topo1/r2/r2_route6.json | 66 + tests/topotests/isis_topo1/r2/r2_route6_linux.json | 14 + tests/topotests/isis_topo1/r2/r2_route_linux.json | 14 + tests/topotests/isis_topo1/r2/r2_topology.json | 96 + tests/topotests/isis_topo1/r2/zebra.conf | 9 + tests/topotests/isis_topo1/r3/isisd.conf | 24 + tests/topotests/isis_topo1/r3/r3_route.json | 165 + tests/topotests/isis_topo1/r3/r3_route6.json | 132 + tests/topotests/isis_topo1/r3/r3_route6_linux.json | 32 + tests/topotests/isis_topo1/r3/r3_route_linux.json | 32 + tests/topotests/isis_topo1/r3/r3_topology.json | 194 + tests/topotests/isis_topo1/r3/zebra.conf | 13 + tests/topotests/isis_topo1/r4/isisd.conf | 24 + tests/topotests/isis_topo1/r4/r4_route.json | 80 + tests/topotests/isis_topo1/r4/r4_route6.json | 131 + tests/topotests/isis_topo1/r4/r4_route6_linux.json | 32 + tests/topotests/isis_topo1/r4/r4_route_linux.json | 32 + tests/topotests/isis_topo1/r4/r4_topology.json | 194 + tests/topotests/isis_topo1/r4/zebra.conf | 13 + tests/topotests/isis_topo1/r5/isisd.conf | 24 + tests/topotests/isis_topo1/r5/r5_route.json | 161 + tests/topotests/isis_topo1/r5/r5_route6.json | 115 + tests/topotests/isis_topo1/r5/r5_route6_linux.json | 26 + tests/topotests/isis_topo1/r5/r5_route_linux.json | 26 + tests/topotests/isis_topo1/r5/r5_topology.json | 156 + tests/topotests/isis_topo1/r5/zebra.conf | 15 + tests/topotests/isis_topo1/test_isis_topo1.dot | 100 + tests/topotests/isis_topo1/test_isis_topo1.jpg | Bin 0 -> 74340 bytes tests/topotests/isis_topo1/test_isis_topo1.py | 896 +++ tests/topotests/isis_topo1_vrf/__init__.py | 0 tests/topotests/isis_topo1_vrf/r1/isisd.conf | 16 + tests/topotests/isis_topo1_vrf/r1/r1_route.json | 37 + tests/topotests/isis_topo1_vrf/r1/r1_route6.json | 36 + .../isis_topo1_vrf/r1/r1_route6_linux.json | 14 + .../isis_topo1_vrf/r1/r1_route_linux.json | 13 + tests/topotests/isis_topo1_vrf/r1/r1_topology.json | 80 + tests/topotests/isis_topo1_vrf/r1/zebra.conf | 9 + tests/topotests/isis_topo1_vrf/r2/isisd.conf | 16 + tests/topotests/isis_topo1_vrf/r2/r2_route.json | 37 + tests/topotests/isis_topo1_vrf/r2/r2_route6.json | 36 + .../isis_topo1_vrf/r2/r2_route6_linux.json | 14 + .../isis_topo1_vrf/r2/r2_route_linux.json | 13 + tests/topotests/isis_topo1_vrf/r2/r2_topology.json | 80 + tests/topotests/isis_topo1_vrf/r2/zebra.conf | 9 + tests/topotests/isis_topo1_vrf/r3/isisd.conf | 23 + tests/topotests/isis_topo1_vrf/r3/r3_route.json | 86 + tests/topotests/isis_topo1_vrf/r3/r3_route6.json | 70 + .../isis_topo1_vrf/r3/r3_route6_linux.json | 26 + .../isis_topo1_vrf/r3/r3_route_linux.json | 24 + tests/topotests/isis_topo1_vrf/r3/r3_topology.json | 132 + tests/topotests/isis_topo1_vrf/r3/zebra.conf | 13 + tests/topotests/isis_topo1_vrf/r4/isisd.conf | 26 + tests/topotests/isis_topo1_vrf/r4/r4_route.json | 81 + tests/topotests/isis_topo1_vrf/r4/r4_route6.json | 70 + .../isis_topo1_vrf/r4/r4_route6_linux.json | 26 + .../isis_topo1_vrf/r4/r4_route_linux.json | 24 + tests/topotests/isis_topo1_vrf/r4/r4_topology.json | 132 + tests/topotests/isis_topo1_vrf/r4/zebra.conf | 13 + tests/topotests/isis_topo1_vrf/r5/isisd.conf | 22 + tests/topotests/isis_topo1_vrf/r5/r5_route.json | 94 + tests/topotests/isis_topo1_vrf/r5/r5_route6.json | 70 + .../isis_topo1_vrf/r5/r5_route6_linux.json | 26 + .../isis_topo1_vrf/r5/r5_route_linux.json | 24 + tests/topotests/isis_topo1_vrf/r5/r5_topology.json | 124 + tests/topotests/isis_topo1_vrf/r5/zebra.conf | 13 + .../isis_topo1_vrf/test_isis_topo1_vrf.dot | 100 + .../isis_topo1_vrf/test_isis_topo1_vrf.jpg | Bin 0 -> 74340 bytes .../isis_topo1_vrf/test_isis_topo1_vrf.py | 428 ++ tests/topotests/kinds.yaml | 30 + tests/topotests/ldp_oc_acl_topo1/r1/ldpd.conf | 25 + tests/topotests/ldp_oc_acl_topo1/r1/ospfd.conf | 12 + .../ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json | 12 + .../ldp_oc_acl_topo1/r1/show_ip_route.ref | 160 + .../ldp_oc_acl_topo1/r1/show_ldp_all_binding.ref | 61 + .../ldp_oc_acl_topo1/r1/show_ldp_binding.ref | 55 + .../ldp_oc_acl_topo1/r1/show_ldp_discovery.ref | 11 + .../ldp_oc_acl_topo1/r1/show_ldp_neighbor.ref | 10 + tests/topotests/ldp_oc_acl_topo1/r1/zebra.conf | 17 + tests/topotests/ldp_oc_acl_topo1/r2/ldpd.conf | 28 + tests/topotests/ldp_oc_acl_topo1/r2/ospfd.conf | 17 + .../ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json | 28 + .../ldp_oc_acl_topo1/r2/show_ip_route.ref | 198 + .../ldp_oc_acl_topo1/r2/show_ldp_all_binding.ref | 63 + .../ldp_oc_acl_topo1/r2/show_ldp_binding.ref | 63 + .../ldp_oc_acl_topo1/r2/show_ldp_discovery.ref | 18 + .../ldp_oc_acl_topo1/r2/show_ldp_neighbor.ref | 16 + tests/topotests/ldp_oc_acl_topo1/r2/zebra.conf | 27 + tests/topotests/ldp_oc_acl_topo1/r3/ldpd.conf | 24 + tests/topotests/ldp_oc_acl_topo1/r3/ospfd.conf | 13 + .../ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json | 20 + .../ldp_oc_acl_topo1/r3/show_ip_route.ref | 198 + .../ldp_oc_acl_topo1/r3/show_ldp_all_binding.ref | 61 + .../ldp_oc_acl_topo1/r3/show_ldp_binding.ref | 62 + .../ldp_oc_acl_topo1/r3/show_ldp_discovery.ref | 11 + .../ldp_oc_acl_topo1/r3/show_ldp_neighbor.ref | 10 + tests/topotests/ldp_oc_acl_topo1/r3/zebra.conf | 22 + tests/topotests/ldp_oc_acl_topo1/r4/ldpd.conf | 24 + tests/topotests/ldp_oc_acl_topo1/r4/ospfd.conf | 12 + .../ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json | 21 + .../ldp_oc_acl_topo1/r4/show_ip_route.ref | 186 + .../ldp_oc_acl_topo1/r4/show_ldp_all_binding.ref | 68 + .../ldp_oc_acl_topo1/r4/show_ldp_binding.ref | 68 + .../ldp_oc_acl_topo1/r4/show_ldp_discovery.ref | 2 + .../ldp_oc_acl_topo1/r4/show_ldp_neighbor.ref | 2 + tests/topotests/ldp_oc_acl_topo1/r4/zebra.conf | 17 + .../ldp_oc_acl_topo1/test_ldp_oc_acl_topo1.dot | 76 + .../ldp_oc_acl_topo1/test_ldp_oc_acl_topo1.py | 245 + tests/topotests/ldp_oc_topo1/r1/ldpd.conf | 24 + tests/topotests/ldp_oc_topo1/r1/ospfd.conf | 12 + .../ldp_oc_topo1/r1/show_ip_ospf_neighbor.json | 12 + tests/topotests/ldp_oc_topo1/r1/show_ip_route.ref | 160 + .../topotests/ldp_oc_topo1/r1/show_ldp_binding.ref | 61 + .../ldp_oc_topo1/r1/show_ldp_discovery.ref | 11 + .../ldp_oc_topo1/r1/show_ldp_neighbor.ref | 10 + tests/topotests/ldp_oc_topo1/r1/zebra.conf | 17 + tests/topotests/ldp_oc_topo1/r2/ldpd.conf | 28 + tests/topotests/ldp_oc_topo1/r2/ospfd.conf | 17 + .../ldp_oc_topo1/r2/show_ip_ospf_neighbor.json | 28 + tests/topotests/ldp_oc_topo1/r2/show_ip_route.ref | 198 + .../topotests/ldp_oc_topo1/r2/show_ldp_binding.ref | 63 + .../ldp_oc_topo1/r2/show_ldp_discovery.ref | 18 + .../ldp_oc_topo1/r2/show_ldp_neighbor.ref | 16 + tests/topotests/ldp_oc_topo1/r2/zebra.conf | 27 + tests/topotests/ldp_oc_topo1/r3/ldpd.conf | 24 + tests/topotests/ldp_oc_topo1/r3/ospfd.conf | 13 + .../ldp_oc_topo1/r3/show_ip_ospf_neighbor.json | 20 + tests/topotests/ldp_oc_topo1/r3/show_ip_route.ref | 198 + .../topotests/ldp_oc_topo1/r3/show_ldp_binding.ref | 61 + .../ldp_oc_topo1/r3/show_ldp_discovery.ref | 11 + .../ldp_oc_topo1/r3/show_ldp_neighbor.ref | 10 + tests/topotests/ldp_oc_topo1/r3/zebra.conf | 22 + tests/topotests/ldp_oc_topo1/r4/ldpd.conf | 24 + tests/topotests/ldp_oc_topo1/r4/ospfd.conf | 12 + .../ldp_oc_topo1/r4/show_ip_ospf_neighbor.json | 21 + tests/topotests/ldp_oc_topo1/r4/show_ip_route.ref | 186 + .../topotests/ldp_oc_topo1/r4/show_ldp_binding.ref | 68 + .../ldp_oc_topo1/r4/show_ldp_discovery.ref | 2 + .../ldp_oc_topo1/r4/show_ldp_neighbor.ref | 2 + tests/topotests/ldp_oc_topo1/r4/zebra.conf | 17 + tests/topotests/ldp_oc_topo1/test_ldp_oc_topo1.dot | 76 + tests/topotests/ldp_oc_topo1/test_ldp_oc_topo1.py | 225 + tests/topotests/ldp_snmp/ce1/zebra.conf | 12 + tests/topotests/ldp_snmp/ce2/zebra.conf | 12 + tests/topotests/ldp_snmp/ce3/zebra.conf | 12 + tests/topotests/ldp_snmp/r1/isisd.conf | 27 + tests/topotests/ldp_snmp/r1/ldpd.conf | 35 + tests/topotests/ldp_snmp/r1/show_ip_route.ref | 134 + .../ldp_snmp/r1/show_isis_interface_detail.ref | 16 + ...show_isis_interface_detail_r1_eth1_shutdown.ref | 16 + ...show_isis_interface_detail_r2_eth1_shutdown.ref | 16 + tests/topotests/ldp_snmp/r1/show_isis_ldp_sync.ref | 13 + .../r1/show_isis_ldp_sync_r1_eth1_shutdown.ref | 11 + .../r1/show_isis_ldp_sync_r2_eth1_shutdown.ref | 13 + tests/topotests/ldp_snmp/r1/show_l2vpn_binding.ref | 16 + tests/topotests/ldp_snmp/r1/show_l2vpn_vc.ref | 8 + tests/topotests/ldp_snmp/r1/show_ldp_binding.ref | 44 + tests/topotests/ldp_snmp/r1/show_ldp_discovery.ref | 25 + tests/topotests/ldp_snmp/r1/show_ldp_igp_sync.ref | 16 + .../r1/show_ldp_igp_sync_r1_eth1_shutdown.ref | 16 + tests/topotests/ldp_snmp/r1/show_ldp_neighbor.ref | 16 + .../r1/show_yang_interface_isis_adjacencies.ref | 42 + tests/topotests/ldp_snmp/r1/snmpd.conf | 18 + tests/topotests/ldp_snmp/r1/zebra.conf | 29 + tests/topotests/ldp_snmp/r2/isisd.conf | 28 + tests/topotests/ldp_snmp/r2/ldpd.conf | 35 + tests/topotests/ldp_snmp/r2/ospfd.conf | 19 + tests/topotests/ldp_snmp/r2/show_ip_route.ref | 134 + .../ldp_snmp/r2/show_isis_interface_detail.ref | 16 + ...show_isis_interface_detail_r1_eth1_shutdown.ref | 16 + ...show_isis_interface_detail_r2_eth1_shutdown.ref | 16 + tests/topotests/ldp_snmp/r2/show_isis_ldp_sync.ref | 13 + .../r2/show_isis_ldp_sync_r1_eth1_shutdown.ref | 13 + .../r2/show_isis_ldp_sync_r2_eth1_shutdown.ref | 11 + tests/topotests/ldp_snmp/r2/show_l2vpn_binding.ref | 16 + tests/topotests/ldp_snmp/r2/show_l2vpn_vc.ref | 8 + tests/topotests/ldp_snmp/r2/show_ldp_binding.ref | 44 + tests/topotests/ldp_snmp/r2/show_ldp_discovery.ref | 25 + tests/topotests/ldp_snmp/r2/show_ldp_igp_sync.ref | 16 + .../r2/show_ldp_igp_sync_r1_eth1_shutdown.ref | 16 + .../r2/show_ldp_igp_sync_r2_eth1_shutdown.ref | 16 + tests/topotests/ldp_snmp/r2/show_ldp_neighbor.ref | 16 + .../r2/show_yang_interface_isis_adjacencies.ref | 42 + tests/topotests/ldp_snmp/r2/snmpd.conf | 18 + tests/topotests/ldp_snmp/r2/zebra.conf | 28 + tests/topotests/ldp_snmp/r3/isisd.conf | 29 + tests/topotests/ldp_snmp/r3/ldpd.conf | 27 + tests/topotests/ldp_snmp/r3/show_ip_route.ref | 134 + .../ldp_snmp/r3/show_isis_interface_detail.ref | 16 + ...show_isis_interface_detail_r1_eth1_shutdown.ref | 16 + ...show_isis_interface_detail_r2_eth1_shutdown.ref | 16 + tests/topotests/ldp_snmp/r3/show_isis_ldp_sync.ref | 13 + .../r3/show_isis_ldp_sync_r1_eth1_shutdown.ref | 13 + .../r3/show_isis_ldp_sync_r2_eth1_shutdown.ref | 13 + tests/topotests/ldp_snmp/r3/show_l2vpn_binding.ref | 2 + tests/topotests/ldp_snmp/r3/show_l2vpn_vc.ref | 2 + tests/topotests/ldp_snmp/r3/show_ldp_binding.ref | 44 + tests/topotests/ldp_snmp/r3/show_ldp_discovery.ref | 18 + tests/topotests/ldp_snmp/r3/show_ldp_igp_sync.ref | 16 + .../r3/show_ldp_igp_sync_r1_eth1_shutdown.ref | 16 + tests/topotests/ldp_snmp/r3/show_ldp_neighbor.ref | 16 + .../r3/show_yang_interface_isis_adjacencies.ref | 42 + tests/topotests/ldp_snmp/r3/zebra.conf | 32 + tests/topotests/ldp_snmp/test_ldp_snmp_topo1.py | 372 ++ tests/topotests/ldp_sync_isis_topo1/ce1/zebra.conf | 12 + tests/topotests/ldp_sync_isis_topo1/ce2/zebra.conf | 12 + tests/topotests/ldp_sync_isis_topo1/ce3/zebra.conf | 12 + tests/topotests/ldp_sync_isis_topo1/r1/isisd.conf | 27 + tests/topotests/ldp_sync_isis_topo1/r1/ldpd.conf | 33 + .../ldp_sync_isis_topo1/r1/show_ip_route.ref | 134 + .../r1/show_isis_interface_detail.ref | 16 + ...show_isis_interface_detail_r1_eth1_shutdown.ref | 16 + ...show_isis_interface_detail_r2_eth1_shutdown.ref | 16 + .../ldp_sync_isis_topo1/r1/show_isis_ldp_sync.ref | 13 + .../r1/show_isis_ldp_sync_r1_eth1_shutdown.ref | 11 + .../r1/show_isis_ldp_sync_r2_eth1_shutdown.ref | 13 + .../ldp_sync_isis_topo1/r1/show_l2vpn_binding.ref | 16 + .../ldp_sync_isis_topo1/r1/show_l2vpn_vc.ref | 8 + .../ldp_sync_isis_topo1/r1/show_ldp_binding.ref | 44 + .../ldp_sync_isis_topo1/r1/show_ldp_discovery.ref | 25 + .../ldp_sync_isis_topo1/r1/show_ldp_igp_sync.ref | 16 + .../r1/show_ldp_igp_sync_r1_eth1_shutdown.ref | 16 + .../ldp_sync_isis_topo1/r1/show_ldp_neighbor.ref | 16 + .../r1/show_yang_interface_isis_adjacencies.ref | 42 + tests/topotests/ldp_sync_isis_topo1/r1/zebra.conf | 29 + tests/topotests/ldp_sync_isis_topo1/r2/isisd.conf | 28 + tests/topotests/ldp_sync_isis_topo1/r2/ldpd.conf | 33 + .../ldp_sync_isis_topo1/r2/show_ip_route.ref | 134 + .../r2/show_isis_interface_detail.ref | 16 + ...show_isis_interface_detail_r1_eth1_shutdown.ref | 16 + ...show_isis_interface_detail_r2_eth1_shutdown.ref | 16 + .../ldp_sync_isis_topo1/r2/show_isis_ldp_sync.ref | 13 + .../r2/show_isis_ldp_sync_r1_eth1_shutdown.ref | 13 + .../r2/show_isis_ldp_sync_r2_eth1_shutdown.ref | 11 + .../ldp_sync_isis_topo1/r2/show_l2vpn_binding.ref | 16 + .../ldp_sync_isis_topo1/r2/show_l2vpn_vc.ref | 8 + .../ldp_sync_isis_topo1/r2/show_ldp_binding.ref | 44 + .../ldp_sync_isis_topo1/r2/show_ldp_discovery.ref | 25 + .../ldp_sync_isis_topo1/r2/show_ldp_igp_sync.ref | 16 + .../r2/show_ldp_igp_sync_r1_eth1_shutdown.ref | 16 + .../r2/show_ldp_igp_sync_r2_eth1_shutdown.ref | 16 + .../ldp_sync_isis_topo1/r2/show_ldp_neighbor.ref | 16 + .../r2/show_yang_interface_isis_adjacencies.ref | 42 + tests/topotests/ldp_sync_isis_topo1/r2/zebra.conf | 28 + tests/topotests/ldp_sync_isis_topo1/r3/isisd.conf | 29 + tests/topotests/ldp_sync_isis_topo1/r3/ldpd.conf | 25 + .../ldp_sync_isis_topo1/r3/show_ip_route.ref | 134 + .../r3/show_isis_interface_detail.ref | 16 + ...show_isis_interface_detail_r1_eth1_shutdown.ref | 16 + ...show_isis_interface_detail_r2_eth1_shutdown.ref | 16 + .../ldp_sync_isis_topo1/r3/show_isis_ldp_sync.ref | 13 + .../r3/show_isis_ldp_sync_r1_eth1_shutdown.ref | 13 + .../r3/show_isis_ldp_sync_r2_eth1_shutdown.ref | 13 + .../ldp_sync_isis_topo1/r3/show_l2vpn_binding.ref | 2 + .../ldp_sync_isis_topo1/r3/show_l2vpn_vc.ref | 2 + .../ldp_sync_isis_topo1/r3/show_ldp_binding.ref | 44 + .../ldp_sync_isis_topo1/r3/show_ldp_discovery.ref | 18 + .../ldp_sync_isis_topo1/r3/show_ldp_igp_sync.ref | 16 + .../r3/show_ldp_igp_sync_r1_eth1_shutdown.ref | 16 + .../ldp_sync_isis_topo1/r3/show_ldp_neighbor.ref | 16 + .../r3/show_yang_interface_isis_adjacencies.ref | 42 + tests/topotests/ldp_sync_isis_topo1/r3/zebra.conf | 32 + .../test_ldp_sync_isis_topo1.dot | 111 + .../test_ldp_sync_isis_topo1.py | 613 ++ tests/topotests/ldp_sync_ospf_topo1/ce1/zebra.conf | 12 + tests/topotests/ldp_sync_ospf_topo1/ce2/zebra.conf | 12 + tests/topotests/ldp_sync_ospf_topo1/ce3/zebra.conf | 12 + tests/topotests/ldp_sync_ospf_topo1/r1/ldpd.conf | 33 + .../topotests/ldp_sync_ospf_topo1/r1/ospf-nbrs.txt | 0 tests/topotests/ldp_sync_ospf_topo1/r1/ospfd.conf | 20 + .../r1/show_ip_ospf_interface.ref | 11 + .../r1/show_ip_ospf_interface_r1_eth1_shutdown.ref | 8 + .../r1/show_ip_ospf_interface_r2_eth1_shutdown.ref | 11 + .../r1/show_ip_ospf_neighbor.json | 26 + .../ldp_sync_ospf_topo1/r1/show_ip_route.ref | 147 + .../ldp_sync_ospf_topo1/r1/show_l2vpn_binding.ref | 16 + .../ldp_sync_ospf_topo1/r1/show_l2vpn_vc.ref | 8 + .../ldp_sync_ospf_topo1/r1/show_ldp_binding.ref | 44 + .../ldp_sync_ospf_topo1/r1/show_ldp_discovery.ref | 25 + .../ldp_sync_ospf_topo1/r1/show_ldp_igp_sync.ref | 16 + .../r1/show_ldp_igp_sync_r1_eth1_shutdown.ref | 16 + .../ldp_sync_ospf_topo1/r1/show_ldp_neighbor.ref | 16 + .../ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync.ref | 12 + .../r1/show_ospf_ldp_sync_r1_eth1_shutdown.ref | 7 + .../r1/show_ospf_ldp_sync_r2_eth1_shutdown.ref | 12 + tests/topotests/ldp_sync_ospf_topo1/r1/zebra.conf | 29 + tests/topotests/ldp_sync_ospf_topo1/r2/ldpd.conf | 33 + tests/topotests/ldp_sync_ospf_topo1/r2/ospfd.conf | 19 + .../r2/show_ip_ospf_interface.ref | 11 + .../r2/show_ip_ospf_interface_r1_eth1_shutdown.ref | 11 + .../r2/show_ip_ospf_interface_r2_eth1_shutdown.ref | 8 + .../r2/show_ip_ospf_neighbor.json | 26 + .../ldp_sync_ospf_topo1/r2/show_ip_route.ref | 147 + .../ldp_sync_ospf_topo1/r2/show_l2vpn_binding.ref | 16 + .../ldp_sync_ospf_topo1/r2/show_l2vpn_vc.ref | 8 + .../ldp_sync_ospf_topo1/r2/show_ldp_binding.ref | 44 + .../ldp_sync_ospf_topo1/r2/show_ldp_discovery.ref | 25 + .../ldp_sync_ospf_topo1/r2/show_ldp_igp_sync.ref | 16 + .../r2/show_ldp_igp_sync_r1_eth1_shutdown.ref | 16 + .../ldp_sync_ospf_topo1/r2/show_ldp_neighbor.ref | 16 + .../ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync.ref | 12 + .../r2/show_ospf_ldp_sync_r1_eth1_shutdown.ref | 12 + .../r2/show_ospf_ldp_sync_r2_eth1_shutdown.ref | 7 + tests/topotests/ldp_sync_ospf_topo1/r2/zebra.conf | 28 + tests/topotests/ldp_sync_ospf_topo1/r3/ldpd.conf | 25 + tests/topotests/ldp_sync_ospf_topo1/r3/ospfd.conf | 18 + .../r3/show_ip_ospf_interface.ref | 11 + .../r3/show_ip_ospf_interface_r1_eth1_shutdown.ref | 11 + .../r3/show_ip_ospf_interface_r2_eth1_shutdown.ref | 11 + .../r3/show_ip_ospf_neighbor.json | 26 + .../ldp_sync_ospf_topo1/r3/show_ip_route.ref | 147 + .../ldp_sync_ospf_topo1/r3/show_l2vpn_binding.ref | 2 + .../ldp_sync_ospf_topo1/r3/show_l2vpn_vc.ref | 2 + .../ldp_sync_ospf_topo1/r3/show_ldp_binding.ref | 44 + .../ldp_sync_ospf_topo1/r3/show_ldp_discovery.ref | 18 + .../ldp_sync_ospf_topo1/r3/show_ldp_igp_sync.ref | 16 + .../r3/show_ldp_igp_sync_r1_eth1_shutdown.ref | 16 + .../ldp_sync_ospf_topo1/r3/show_ldp_neighbor.ref | 16 + .../ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync.ref | 12 + .../r3/show_ospf_ldp_sync_r1_eth1_shutdown.ref | 12 + .../r3/show_ospf_ldp_sync_r2_eth1_shutdown.ref | 12 + tests/topotests/ldp_sync_ospf_topo1/r3/zebra.conf | 32 + .../test_ldp_sync_ospf_topo1.dot | 111 + .../test_ldp_sync_ospf_topo1.py | 430 ++ tests/topotests/ldp_topo1/r1/ip_mpls_route.ref | 6 + tests/topotests/ldp_topo1/r1/ldpd.conf | 23 + tests/topotests/ldp_topo1/r1/ospfd.conf | 11 + .../ldp_topo1/r1/show_ip_ospf_neighbor.json | 14 + tests/topotests/ldp_topo1/r1/show_ipv4_route.ref | 7 + .../ldp_topo1/r1/show_mpls_ldp_binding.ref | 8 + .../ldp_topo1/r1/show_mpls_ldp_discovery.ref | 2 + .../ldp_topo1/r1/show_mpls_ldp_interface.ref | 2 + .../ldp_topo1/r1/show_mpls_ldp_neighbor.ref | 2 + tests/topotests/ldp_topo1/r1/show_mpls_table.ref | 8 + tests/topotests/ldp_topo1/r1/zebra.conf | 17 + tests/topotests/ldp_topo1/r2/ip_mpls_route.ref | 3 + tests/topotests/ldp_topo1/r2/ldpd.conf | 25 + tests/topotests/ldp_topo1/r2/ospfd.conf | 19 + .../ldp_topo1/r2/show_ip_ospf_neighbor.json | 42 + tests/topotests/ldp_topo1/r2/show_ipv4_route.ref | 7 + .../ldp_topo1/r2/show_mpls_ldp_binding.ref | 22 + .../ldp_topo1/r2/show_mpls_ldp_discovery.ref | 4 + .../ldp_topo1/r2/show_mpls_ldp_interface.ref | 3 + .../ldp_topo1/r2/show_mpls_ldp_neighbor.ref | 4 + tests/topotests/ldp_topo1/r2/show_mpls_table.ref | 7 + tests/topotests/ldp_topo1/r2/zebra.conf | 27 + tests/topotests/ldp_topo1/r3/ip_mpls_route.ref | 4 + tests/topotests/ldp_topo1/r3/ldpd.conf | 23 + tests/topotests/ldp_topo1/r3/ospfd.conf | 16 + .../ldp_topo1/r3/show_ip_ospf_neighbor.json | 32 + tests/topotests/ldp_topo1/r3/show_ipv4_route.ref | 7 + .../ldp_topo1/r3/show_mpls_ldp_binding.ref | 15 + .../ldp_topo1/r3/show_mpls_ldp_discovery.ref | 3 + .../ldp_topo1/r3/show_mpls_ldp_interface.ref | 2 + .../ldp_topo1/r3/show_mpls_ldp_neighbor.ref | 3 + tests/topotests/ldp_topo1/r3/show_mpls_table.ref | 10 + tests/topotests/ldp_topo1/r3/zebra.conf | 22 + tests/topotests/ldp_topo1/r4/ip_mpls_route.ref | 5 + tests/topotests/ldp_topo1/r4/ldpd.conf | 23 + tests/topotests/ldp_topo1/r4/ospfd.conf | 11 + .../ldp_topo1/r4/show_ip_ospf_neighbor.json | 24 + tests/topotests/ldp_topo1/r4/show_ipv4_route.ref | 7 + .../ldp_topo1/r4/show_mpls_ldp_binding.ref | 15 + .../ldp_topo1/r4/show_mpls_ldp_discovery.ref | 3 + .../ldp_topo1/r4/show_mpls_ldp_interface.ref | 2 + .../ldp_topo1/r4/show_mpls_ldp_neighbor.ref | 3 + tests/topotests/ldp_topo1/r4/show_mpls_table.ref | 9 + tests/topotests/ldp_topo1/r4/zebra.conf | 17 + tests/topotests/ldp_topo1/test_ldp_topo1.py | 844 +++ tests/topotests/ldp_vpls_topo1/__init__.py | 0 tests/topotests/ldp_vpls_topo1/ce1/zebra.conf | 12 + tests/topotests/ldp_vpls_topo1/ce2/zebra.conf | 12 + tests/topotests/ldp_vpls_topo1/ce3/zebra.conf | 12 + tests/topotests/ldp_vpls_topo1/r1/ldpd.conf | 35 + tests/topotests/ldp_vpls_topo1/r1/ospf-nbrs.txt | 0 tests/topotests/ldp_vpls_topo1/r1/ospfd.conf | 17 + .../ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json | 26 + .../topotests/ldp_vpls_topo1/r1/show_ip_route.ref | 147 + .../r1/show_ip_route_after_link_down.ref | 20 + .../ldp_vpls_topo1/r1/show_l2vpn_binding.ref | 16 + .../topotests/ldp_vpls_topo1/r1/show_l2vpn_vc.ref | 8 + .../ldp_vpls_topo1/r1/show_ldp_binding.ref | 44 + .../ldp_vpls_topo1/r1/show_ldp_discovery.ref | 25 + .../ldp_vpls_topo1/r1/show_ldp_neighbor.ref | 16 + tests/topotests/ldp_vpls_topo1/r1/zebra.conf | 29 + tests/topotests/ldp_vpls_topo1/r2/ldpd.conf | 35 + tests/topotests/ldp_vpls_topo1/r2/ospfd.conf | 17 + .../ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json | 26 + .../topotests/ldp_vpls_topo1/r2/show_ip_route.ref | 147 + .../ldp_vpls_topo1/r2/show_l2vpn_binding.ref | 16 + .../topotests/ldp_vpls_topo1/r2/show_l2vpn_vc.ref | 8 + .../ldp_vpls_topo1/r2/show_ldp_binding.ref | 44 + .../ldp_vpls_topo1/r2/show_ldp_discovery.ref | 25 + .../ldp_vpls_topo1/r2/show_ldp_neighbor.ref | 16 + tests/topotests/ldp_vpls_topo1/r2/zebra.conf | 28 + tests/topotests/ldp_vpls_topo1/r3/ldpd.conf | 27 + tests/topotests/ldp_vpls_topo1/r3/ospfd.conf | 17 + .../ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json | 26 + .../topotests/ldp_vpls_topo1/r3/show_ip_route.ref | 147 + .../ldp_vpls_topo1/r3/show_l2vpn_binding.ref | 2 + .../topotests/ldp_vpls_topo1/r3/show_l2vpn_vc.ref | 2 + .../ldp_vpls_topo1/r3/show_ldp_binding.ref | 44 + .../ldp_vpls_topo1/r3/show_ldp_discovery.ref | 18 + .../ldp_vpls_topo1/r3/show_ldp_neighbor.ref | 16 + tests/topotests/ldp_vpls_topo1/r3/zebra.conf | 28 + .../ldp_vpls_topo1/test_ldp_vpls_topo1.dot | 111 + .../ldp_vpls_topo1/test_ldp_vpls_topo1.pdf | Bin 0 -> 16693 bytes .../ldp_vpls_topo1/test_ldp_vpls_topo1.py | 291 + tests/topotests/lib/__init__.py | 0 tests/topotests/lib/bgp.py | 5596 +++++++++++++++++ tests/topotests/lib/bgprib.py | 165 + tests/topotests/lib/bmp_collector/bgp/__init__.py | 0 .../lib/bmp_collector/bgp/open/__init__.py | 34 + .../lib/bmp_collector/bgp/update/__init__.py | 54 + tests/topotests/lib/bmp_collector/bgp/update/af.py | 53 + .../topotests/lib/bmp_collector/bgp/update/nlri.py | 140 + .../bmp_collector/bgp/update/path_attributes.py | 304 + tests/topotests/lib/bmp_collector/bgp/update/rd.py | 59 + tests/topotests/lib/bmp_collector/bmp.py | 420 ++ tests/topotests/lib/bmp_collector/bmpserver | 45 + tests/topotests/lib/checkping.py | 33 + tests/topotests/lib/common_config.py | 4960 ++++++++++++++++ tests/topotests/lib/exa-receive.py | 43 + tests/topotests/lib/fixtures.py | 30 + tests/topotests/lib/grpc-query.py | 128 + tests/topotests/lib/ltemplate.py | 307 + tests/topotests/lib/lutil.py | 499 ++ tests/topotests/lib/mcast-tester.py | 142 + tests/topotests/lib/micronet.py | 24 + tests/topotests/lib/micronet_compat.py | 370 ++ tests/topotests/lib/ospf.py | 3028 ++++++++++ tests/topotests/lib/pim.py | 5203 ++++++++++++++++ tests/topotests/lib/scapy_sendpkt.py | 44 + tests/topotests/lib/send_bsr_packet.py | 45 + tests/topotests/lib/snmptest.py | 145 + tests/topotests/lib/test/__init__.py | 0 tests/topotests/lib/test/test_json.py | 627 ++ tests/topotests/lib/test/test_run_and_expect.py | 66 + tests/topotests/lib/test/test_version.py | 77 + tests/topotests/lib/topogen.py | 1390 +++++ tests/topotests/lib/topojson.py | 405 ++ tests/topotests/lib/topolog.py | 161 + tests/topotests/lib/topotest.py | 2397 ++++++++ .../topotests/mgmt_config/r1/early-end-zebra.conf | 6 + tests/topotests/mgmt_config/r1/early-end.conf | 8 + .../topotests/mgmt_config/r1/early-end2-zebra.conf | 7 + tests/topotests/mgmt_config/r1/early-end2.conf | 9 + .../topotests/mgmt_config/r1/early-exit-zebra.conf | 6 + tests/topotests/mgmt_config/r1/early-exit.conf | 8 + .../mgmt_config/r1/early-exit2-zebra.conf | 7 + tests/topotests/mgmt_config/r1/early-exit2.conf | 9 + tests/topotests/mgmt_config/r1/frr.conf | 15 + tests/topotests/mgmt_config/r1/mgmtd.conf | 11 + tests/topotests/mgmt_config/r1/normal-exit.conf | 8 + tests/topotests/mgmt_config/r1/one-exit-zebra.conf | 3 + tests/topotests/mgmt_config/r1/one-exit.conf | 3 + .../topotests/mgmt_config/r1/one-exit2-zebra.conf | 4 + tests/topotests/mgmt_config/r1/one-exit2.conf | 4 + tests/topotests/mgmt_config/r1/zebra.conf | 7 + tests/topotests/mgmt_config/test_config.py | 385 ++ tests/topotests/mgmt_config/test_regression.py | 53 + tests/topotests/mgmt_startup/r1/mgmtd.conf | 13 + tests/topotests/mgmt_startup/r1/zebra.conf | 7 + tests/topotests/mgmt_startup/r2/staticd.conf | 7 + tests/topotests/mgmt_startup/r2/zebra.conf | 12 + tests/topotests/mgmt_startup/r3/zebra.conf | 18 + tests/topotests/mgmt_startup/r4/frr.conf | 21 + tests/topotests/mgmt_startup/test_bigconf.py | 80 + tests/topotests/mgmt_startup/test_cfgfile_var.py | 109 + tests/topotests/mgmt_startup/test_late_bigconf.py | 98 + tests/topotests/mgmt_startup/test_late_uniconf.py | 44 + tests/topotests/mgmt_startup/test_latestart.py | 45 + tests/topotests/mgmt_startup/util.py | 98 + tests/topotests/mgmt_tests/test_yang_mgmt.py | 548 ++ tests/topotests/mgmt_tests/yang_mgmt.json | 157 + tests/topotests/msdp_mesh_topo1/__init__.py | 0 tests/topotests/msdp_mesh_topo1/r1/bgpd.conf | 7 + tests/topotests/msdp_mesh_topo1/r1/ospfd.conf | 8 + tests/topotests/msdp_mesh_topo1/r1/pimd.conf | 17 + tests/topotests/msdp_mesh_topo1/r1/zebra.conf | 11 + tests/topotests/msdp_mesh_topo1/r2/bgpd.conf | 10 + tests/topotests/msdp_mesh_topo1/r2/ospfd.conf | 13 + tests/topotests/msdp_mesh_topo1/r2/pimd.conf | 16 + tests/topotests/msdp_mesh_topo1/r2/zebra.conf | 11 + tests/topotests/msdp_mesh_topo1/r3/bgpd.conf | 7 + tests/topotests/msdp_mesh_topo1/r3/ospfd.conf | 8 + tests/topotests/msdp_mesh_topo1/r3/pimd.conf | 17 + tests/topotests/msdp_mesh_topo1/r3/zebra.conf | 11 + .../msdp_mesh_topo1/test_msdp_mesh_topo1.dot | 88 + .../msdp_mesh_topo1/test_msdp_mesh_topo1.png | Bin 0 -> 35201 bytes .../msdp_mesh_topo1/test_msdp_mesh_topo1.py | 230 + tests/topotests/msdp_topo1/__init__.py | 0 tests/topotests/msdp_topo1/r1/bgpd.conf | 8 + tests/topotests/msdp_topo1/r1/pimd.conf | 22 + tests/topotests/msdp_topo1/r1/zebra.conf | 14 + tests/topotests/msdp_topo1/r2/bgpd.conf | 8 + tests/topotests/msdp_topo1/r2/pimd.conf | 18 + tests/topotests/msdp_topo1/r2/zebra.conf | 11 + tests/topotests/msdp_topo1/r3/bgpd.conf | 8 + tests/topotests/msdp_topo1/r3/pimd.conf | 18 + tests/topotests/msdp_topo1/r3/zebra.conf | 11 + tests/topotests/msdp_topo1/r4/bgpd.conf | 9 + tests/topotests/msdp_topo1/r4/pimd.conf | 22 + tests/topotests/msdp_topo1/r4/zebra.conf | 14 + tests/topotests/msdp_topo1/test_msdp_topo1.py | 442 ++ .../multicast_mld_local_join.json | 249 + .../test_multicast_mld_local_join.py | 926 +++ .../multicast_pim6_sm_topo1.json | 300 + .../test_multicast_pim6_sm1.py | 1595 +++++ .../test_multicast_pim6_sm2.py | 591 ++ .../multicast_pim6_static_rp_topo1/__init__.py | 0 .../multicast_pim6_static_rp.json | 197 + .../test_multicast_pim6_static_rp1.py | 1263 ++++ .../test_multicast_pim6_static_rp2.py | 1287 ++++ .../multicast_pim_bsm_topo1/mcast_pim_bsmp_01.json | 238 + .../test_mcast_pim_bsmp_01.py | 1767 ++++++ .../multicast_pim_bsm_topo2/mcast_pim_bsmp_02.json | 238 + .../test_mcast_pim_bsmp_02.py | 1102 ++++ .../multicast_pim_dr_nondr_test/__init__.py | 0 .../pim_dr_nondr_with_ospf_topo2.json | 195 + .../pim_dr_nondr_with_static_routes_topo1.json | 85 + .../pim_dr_nondr_with_transit_router_topo3.json | 241 + .../test_pim_dr_nondr_with_ospf_topo2.py | 1125 ++++ .../test_pim_dr_nondr_with_static_routes_topo1.py | 924 +++ .../test_pim_dr_nondr_with_transit_router_topo3.py | 813 +++ .../multicast_pim_sm_topo1.json | 146 + .../test_multicast_pim_sm_topo1.py | 1589 +++++ .../multicast_pim_sm_topo2.json | 146 + .../test_multicast_pim_sm_topo2.py | 1825 ++++++ .../igmp_group_all_detail.json | 1 + .../igmp_single_if_group_all_brief.json | 51 + .../igmp_single_if_group_all_detail.json | 1 + .../igmp_single_if_single_group_brief.json | 22 + .../igmp_single_if_single_group_detail.json | 1 + .../igmp_source_single_if_group_all.json | 61 + .../igmp_source_single_if_single_group.json | 16 + .../multicast_pim_sm_topo3.json | 146 + .../multicast_pim_sm_topo4.json | 143 + .../test_multicast_pim_sm_topo3.py | 4856 +++++++++++++++ .../test_multicast_pim_sm_topo4.py | 1084 ++++ .../multicast_pim_static_rp_topo1/__init__.py | 0 .../multicast_pim_static_rp.json | 119 + .../test_multicast_pim_static_rp.py | 1376 +++++ .../test_multicast_pim_static_rp1.py | 1406 +++++ .../test_multicast_pim_static_rp2.py | 1783 ++++++ .../multicast_pim_uplink_topo1.json | 226 + .../test_multicast_pim_uplink_topo1.py | 3362 +++++++++++ .../multicast_pim_uplink_topo2.json | 288 + .../test_multicast_pim_uplink_topo2.py | 1381 +++++ .../multicast_pim_uplink_topo3.json | 295 + .../test_multicast_pim_uplink_topo3.py | 940 +++ tests/topotests/munet/__init__.py | 38 + tests/topotests/munet/__main__.py | 236 + tests/topotests/munet/base.py | 3111 ++++++++++ tests/topotests/munet/cleanup.py | 114 + tests/topotests/munet/cli.py | 962 +++ tests/topotests/munet/compat.py | 34 + tests/topotests/munet/config.py | 213 + tests/topotests/munet/kinds.yaml | 84 + tests/topotests/munet/linux.py | 267 + tests/topotests/munet/logconf-mutest.yaml | 84 + tests/topotests/munet/logconf.yaml | 32 + tests/topotests/munet/mucmd.py | 111 + tests/topotests/munet/mulog.py | 122 + tests/topotests/munet/munet-schema.json | 654 ++ tests/topotests/munet/mutest/__main__.py | 445 ++ tests/topotests/munet/mutest/userapi.py | 1111 ++++ tests/topotests/munet/mutestshare.py | 254 + tests/topotests/munet/mutini.py | 432 ++ tests/topotests/munet/native.py | 2941 +++++++++ tests/topotests/munet/parser.py | 374 ++ tests/topotests/munet/testing/__init__.py | 1 + tests/topotests/munet/testing/fixtures.py | 447 ++ tests/topotests/munet/testing/hooks.py | 225 + tests/topotests/munet/testing/util.py | 110 + tests/topotests/nhrp_topo/r1/nhrp4_cache.json | 29 + tests/topotests/nhrp_topo/r1/nhrp_route4.json | 25 + tests/topotests/nhrp_topo/r1/nhrpd.conf | 10 + tests/topotests/nhrp_topo/r1/sharp_route4.json | 46 + tests/topotests/nhrp_topo/r1/zebra.conf | 12 + tests/topotests/nhrp_topo/r2/nhrp4_cache.json | 29 + tests/topotests/nhrp_topo/r2/nhrp_route4.json | 25 + tests/topotests/nhrp_topo/r2/nhrpd.conf | 10 + tests/topotests/nhrp_topo/r2/zebra.conf | 12 + tests/topotests/nhrp_topo/r3/zebra.conf | 11 + tests/topotests/nhrp_topo/test_nhrp_topo.dot | 73 + tests/topotests/nhrp_topo/test_nhrp_topo.py | 253 + .../topotests/ospf6_ecmp_inter_area/r1/ospf6d.conf | 52 + .../topotests/ospf6_ecmp_inter_area/r1/zebra.conf | 5 + .../topotests/ospf6_ecmp_inter_area/r2/ospf6d.conf | 14 + .../topotests/ospf6_ecmp_inter_area/r2/zebra.conf | 5 + .../topotests/ospf6_ecmp_inter_area/r3/ospf6d.conf | 14 + .../topotests/ospf6_ecmp_inter_area/r3/zebra.conf | 5 + .../topotests/ospf6_ecmp_inter_area/r4/ospf6d.conf | 14 + .../topotests/ospf6_ecmp_inter_area/r4/zebra.conf | 5 + .../topotests/ospf6_ecmp_inter_area/r5/ospf6d.conf | 39 + .../topotests/ospf6_ecmp_inter_area/r5/zebra.conf | 5 + .../topotests/ospf6_ecmp_inter_area/r6/ospf6d.conf | 9 + .../topotests/ospf6_ecmp_inter_area/r6/zebra.conf | 5 + .../topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf | 11 + .../topotests/ospf6_ecmp_inter_area/r7/zebra.conf | 5 + .../topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf | 9 + .../topotests/ospf6_ecmp_inter_area/r8/zebra.conf | 5 + .../topotests/ospf6_ecmp_inter_area/r9/ospf6d.conf | 11 + .../topotests/ospf6_ecmp_inter_area/r9/zebra.conf | 5 + .../test_ospf6_ecmp_inter_area.py | 194 + tests/topotests/ospf6_gr_topo1/__init__.py | 0 tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf | 29 + .../rt1/show_ipv6_ospf_database.json | 95 + .../rt1/show_ipv6_ospf_neighbor.json | 12 + .../ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json | 74 + .../ospf6_gr_topo1/rt1/show_ipv6_route.json | 139 + tests/topotests/ospf6_gr_topo1/rt1/zebra.conf | 22 + tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf | 35 + .../rt2/show_ipv6_ospf_database.json | 183 + .../rt2/show_ipv6_ospf_neighbor.json | 20 + .../ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json | 74 + .../ospf6_gr_topo1/rt2/show_ipv6_route.json | 139 + tests/topotests/ospf6_gr_topo1/rt2/zebra.conf | 22 + tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf | 41 + .../rt3/show_ipv6_ospf_database.json | 144 + .../rt3/show_ipv6_ospf_neighbor.json | 28 + .../ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json | 74 + .../ospf6_gr_topo1/rt3/show_ipv6_route.json | 139 + tests/topotests/ospf6_gr_topo1/rt3/zebra.conf | 24 + tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf | 35 + .../rt4/show_ipv6_ospf_database.json | 188 + .../rt4/show_ipv6_ospf_neighbor.json | 20 + .../ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json | 74 + .../ospf6_gr_topo1/rt4/show_ipv6_route.json | 139 + tests/topotests/ospf6_gr_topo1/rt4/zebra.conf | 22 + tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf | 29 + .../rt5/show_ipv6_ospf_database.json | 100 + .../rt5/show_ipv6_ospf_neighbor.json | 12 + .../ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json | 74 + .../ospf6_gr_topo1/rt5/show_ipv6_route.json | 139 + tests/topotests/ospf6_gr_topo1/rt5/zebra.conf | 20 + tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf | 35 + .../rt6/show_ipv6_ospf_database.json | 183 + .../rt6/show_ipv6_ospf_neighbor.json | 20 + .../ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json | 74 + .../ospf6_gr_topo1/rt6/show_ipv6_route.json | 139 + tests/topotests/ospf6_gr_topo1/rt6/zebra.conf | 22 + tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf | 30 + .../rt7/show_ipv6_ospf_database.json | 95 + .../rt7/show_ipv6_ospf_neighbor.json | 12 + .../ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json | 74 + .../ospf6_gr_topo1/rt7/show_ipv6_route.json | 139 + tests/topotests/ospf6_gr_topo1/rt7/zebra.conf | 22 + .../ospf6_gr_topo1/test_ospf6_gr_topo1.py | 583 ++ tests/topotests/ospf6_loopback_cost/__init__.py | 0 tests/topotests/ospf6_loopback_cost/r1/frr.conf | 16 + tests/topotests/ospf6_loopback_cost/r2/frr.conf | 16 + .../test_ospf6_loopback_cost.py | 89 + tests/topotests/ospf6_topo1/README.md | 132 + .../topotests/ospf6_topo1/r1/ip_6_address.nhg.ref | 10 + tests/topotests/ospf6_topo1/r1/ip_6_address.ref | 10 + tests/topotests/ospf6_topo1/r1/ospf6d.conf | 31 + tests/topotests/ospf6_topo1/r1/show_ipv6_route.ref | 9 + tests/topotests/ospf6_topo1/r1/zebra.conf | 20 + .../topotests/ospf6_topo1/r2/ip_6_address.nhg.ref | 10 + tests/topotests/ospf6_topo1/r2/ip_6_address.ref | 10 + tests/topotests/ospf6_topo1/r2/ospf6d.conf | 31 + tests/topotests/ospf6_topo1/r2/show_ipv6_route.ref | 10 + tests/topotests/ospf6_topo1/r2/zebra.conf | 20 + .../topotests/ospf6_topo1/r3/ip_6_address.nhg.ref | 10 + tests/topotests/ospf6_topo1/r3/ip_6_address.ref | 10 + tests/topotests/ospf6_topo1/r3/ospf6d.conf | 37 + tests/topotests/ospf6_topo1/r3/show_ipv6_route.ref | 10 + tests/topotests/ospf6_topo1/r3/zebra.conf | 23 + .../topotests/ospf6_topo1/r4/ip_6_address.nhg.ref | 10 + tests/topotests/ospf6_topo1/r4/ip_6_address.ref | 10 + tests/topotests/ospf6_topo1/r4/ospf6d.conf | 31 + tests/topotests/ospf6_topo1/r4/show_ipv6_route.ref | 9 + tests/topotests/ospf6_topo1/r4/zebra.conf | 20 + tests/topotests/ospf6_topo1/test_ospf6_topo1.py | 422 ++ tests/topotests/ospf6_topo1_vrf/README.md | 132 + .../ospf6_topo1_vrf/r1/ip_6_address.nhg.ref | 10 + .../topotests/ospf6_topo1_vrf/r1/ip_6_address.ref | 10 + tests/topotests/ospf6_topo1_vrf/r1/ospf6d.conf | 31 + .../ospf6_topo1_vrf/r1/show_ipv6_vrf_route.ref | 9 + tests/topotests/ospf6_topo1_vrf/r1/zebra.conf | 20 + .../topotests/ospf6_topo1_vrf/r2/ip_6_address.ref | 10 + tests/topotests/ospf6_topo1_vrf/r2/ospf6d.conf | 31 + .../ospf6_topo1_vrf/r2/show_ipv6_vrf_route.ref | 9 + tests/topotests/ospf6_topo1_vrf/r2/zebra.conf | 20 + .../topotests/ospf6_topo1_vrf/r3/ip_6_address.ref | 10 + tests/topotests/ospf6_topo1_vrf/r3/ospf6d.conf | 37 + .../ospf6_topo1_vrf/r3/show_ipv6_vrf_route.ref | 9 + tests/topotests/ospf6_topo1_vrf/r3/zebra.conf | 23 + .../topotests/ospf6_topo1_vrf/r4/ip_6_address.ref | 10 + tests/topotests/ospf6_topo1_vrf/r4/ospf6d.conf | 31 + .../ospf6_topo1_vrf/r4/show_ipv6_vrf_route.ref | 9 + tests/topotests/ospf6_topo1_vrf/r4/zebra.conf | 20 + .../ospf6_topo1_vrf/test_ospf6_topo1_vrf.py | 462 ++ tests/topotests/ospf6_topo2/r1/ospf6d.conf | 37 + tests/topotests/ospf6_topo2/r1/zebra.conf | 5 + tests/topotests/ospf6_topo2/r2/ospf6d.conf | 51 + tests/topotests/ospf6_topo2/r2/zebra.conf | 11 + tests/topotests/ospf6_topo2/r3/ospf6d.conf | 38 + tests/topotests/ospf6_topo2/r3/zebra.conf | 8 + tests/topotests/ospf6_topo2/r4/ospf6d.conf | 37 + tests/topotests/ospf6_topo2/r4/zebra.conf | 5 + tests/topotests/ospf6_topo2/test_ospf6_topo2.dot | 83 + tests/topotests/ospf6_topo2/test_ospf6_topo2.png | Bin 0 -> 44388 bytes tests/topotests/ospf6_topo2/test_ospf6_topo2.py | 653 ++ .../ospf_asbr_summary_topo1.json | 195 + .../ospf_asbr_summary_type7_lsa.json | 199 + .../ospf_authentication.json | 166 + .../ospf_basic_functionality/ospf_chaos.json | 166 + .../ospf_basic_functionality/ospf_ecmp.json | 342 ++ .../ospf_basic_functionality/ospf_ecmp_lan.json | 234 + .../ospf_flood_reduction.json | 214 + .../ospf_basic_functionality/ospf_lan.json | 138 + .../ospf_basic_functionality/ospf_nssa.json | 188 + .../ospf_basic_functionality/ospf_p2mp.json | 198 + .../ospf_basic_functionality/ospf_routemaps.json | 159 + .../ospf_basic_functionality/ospf_rte_calc.json | 168 + .../ospf_basic_functionality/ospf_single_area.json | 188 + .../test_ospf_asbr_summary_topo1.py | 3276 ++++++++++ .../test_ospf_asbr_summary_type7_lsa.py | 388 ++ .../test_ospf_authentication.py | 1333 +++++ .../ospf_basic_functionality/test_ospf_chaos.py | 513 ++ .../ospf_basic_functionality/test_ospf_ecmp.py | 440 ++ .../ospf_basic_functionality/test_ospf_ecmp_lan.py | 290 + .../test_ospf_flood_reduction.py | 1066 ++++ .../ospf_basic_functionality/test_ospf_lan.py | 650 ++ .../ospf_basic_functionality/test_ospf_nssa.py | 262 + .../ospf_basic_functionality/test_ospf_p2mp.py | 699 +++ .../test_ospf_routemaps.py | 1296 ++++ .../ospf_basic_functionality/test_ospf_rte_calc.py | 778 +++ .../test_ospf_single_area.py | 988 +++ .../ospf_dual_stack/test_ospf_dual_stack.dot | 107 + .../ospf_dual_stack/test_ospf_dual_stack.jpg | Bin 0 -> 98314 bytes .../ospf_dual_stack/test_ospf_dual_stack.json | 263 + .../ospf_dual_stack/test_ospf_dual_stack.py | 118 + tests/topotests/ospf_gr_helper/ospf_gr_helper.json | 119 + .../ospf_gr_helper/test_ospf_gr_helper1.py | 380 ++ .../ospf_gr_helper/test_ospf_gr_helper2.py | 356 ++ .../ospf_gr_helper/test_ospf_gr_helper3.py | 310 + tests/topotests/ospf_gr_topo1/__init__.py | 0 tests/topotests/ospf_gr_topo1/rt1/ospfd.conf | 32 + .../ospf_gr_topo1/rt1/show_ip_ospf_database.json | 98 + .../ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json | 11 + .../ospf_gr_topo1/rt1/show_ip_ospf_route.json | 180 + .../topotests/ospf_gr_topo1/rt1/show_ip_route.json | 210 + tests/topotests/ospf_gr_topo1/rt1/zebra.conf | 23 + tests/topotests/ospf_gr_topo1/rt2/ospfd.conf | 37 + .../ospf_gr_topo1/rt2/show_ip_ospf_database.json | 160 + .../ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json | 18 + .../ospf_gr_topo1/rt2/show_ip_ospf_route.json | 201 + .../topotests/ospf_gr_topo1/rt2/show_ip_route.json | 224 + tests/topotests/ospf_gr_topo1/rt2/zebra.conf | 23 + tests/topotests/ospf_gr_topo1/rt3/ospfd.conf | 43 + .../ospf_gr_topo1/rt3/show_ip_ospf_database.json | 83 + .../ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json | 25 + .../ospf_gr_topo1/rt3/show_ip_ospf_route.json | 214 + .../topotests/ospf_gr_topo1/rt3/show_ip_route.json | 223 + tests/topotests/ospf_gr_topo1/rt3/zebra.conf | 26 + tests/topotests/ospf_gr_topo1/rt4/ospfd.conf | 37 + .../ospf_gr_topo1/rt4/show_ip_ospf_database.json | 164 + .../ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json | 18 + .../ospf_gr_topo1/rt4/show_ip_ospf_route.json | 202 + .../topotests/ospf_gr_topo1/rt4/show_ip_route.json | 224 + tests/topotests/ospf_gr_topo1/rt4/zebra.conf | 23 + tests/topotests/ospf_gr_topo1/rt5/ospfd.conf | 31 + .../ospf_gr_topo1/rt5/show_ip_ospf_database.json | 102 + .../ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json | 11 + .../ospf_gr_topo1/rt5/show_ip_ospf_route.json | 203 + .../topotests/ospf_gr_topo1/rt5/show_ip_route.json | 225 + tests/topotests/ospf_gr_topo1/rt5/zebra.conf | 20 + tests/topotests/ospf_gr_topo1/rt6/ospfd.conf | 38 + .../ospf_gr_topo1/rt6/show_ip_ospf_database.json | 168 + .../ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json | 18 + .../ospf_gr_topo1/rt6/show_ip_ospf_route.json | 214 + .../topotests/ospf_gr_topo1/rt6/show_ip_route.json | 224 + tests/topotests/ospf_gr_topo1/rt6/zebra.conf | 23 + tests/topotests/ospf_gr_topo1/rt7/ospfd.conf | 33 + .../ospf_gr_topo1/rt7/show_ip_ospf_database.json | 99 + .../ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json | 11 + .../ospf_gr_topo1/rt7/show_ip_ospf_route.json | 168 + .../topotests/ospf_gr_topo1/rt7/show_ip_route.json | 210 + tests/topotests/ospf_gr_topo1/rt7/zebra.conf | 23 + .../topotests/ospf_gr_topo1/test_ospf_gr_topo1.py | 587 ++ .../r1/ospf_default_information.json | 33 + .../r1/ospf_instance_lsa.json | 17 + .../r1/ospf_instance_lsa2.json | 25 + .../ospf_instance_redistribute/r1/ospfd-3.conf | 2 + .../r1/sharp_installed.json | 13 + .../ospf_instance_redistribute/r1/zebra.conf | 2 + .../test_ospf_instance_redistribute.py | 175 + .../topotests/ospf_metric_propagation/__init__.py | 0 .../topotests/ospf_metric_propagation/h1/frr.conf | 10 + .../topotests/ospf_metric_propagation/h2/frr.conf | 10 + .../topotests/ospf_metric_propagation/r1/frr.conf | 96 + .../r1/show_ip_route-1.json | 35 + .../r1/show_ip_route-2.json | 35 + .../r1/show_ip_route-3.json | 35 + .../r1/show_ip_route-4.json | 35 + .../r1/show_ip_route-5.json | 35 + .../r1/show_ip_route-6.json | 35 + .../topotests/ospf_metric_propagation/r2/frr.conf | 81 + .../topotests/ospf_metric_propagation/r3/frr.conf | 79 + .../topotests/ospf_metric_propagation/r4/frr.conf | 78 + .../topotests/ospf_metric_propagation/ra/frr.conf | 27 + .../topotests/ospf_metric_propagation/rb/frr.conf | 27 + .../topotests/ospf_metric_propagation/rc/frr.conf | 21 + .../test_ospf_metric_propagation.py | 385 ++ .../ospf_multi_vrf_bgp_route_leak/__init__.py | 0 .../ospf_multi_vrf_bgp_route_leak/r1/frr.conf | 57 + .../r1/ospf-vrf-default.txt | 19 + .../r1/ospf-vrf-neno.txt | 12 + .../r1/zebra-vrf-default.txt | 9 + .../r1/zebra-vrf-neno.txt | 6 + .../ospf_multi_vrf_bgp_route_leak/r2/frr.conf | 62 + .../r2/ospf-vrf-default.txt | 20 + .../r2/ospf-vrf-ray.txt | 15 + .../r2/zebra-vrf-default.txt | 10 + .../r2/zebra-vrf-ray.txt | 9 + .../ospf_multi_vrf_bgp_route_leak/r3/frr.conf | 22 + .../r3/ospf-vrf-default.txt | 17 + .../r3/zebra-vrf-default.txt | 8 + .../ospf_multi_vrf_bgp_route_leak/r4/frr.conf | 22 + .../r4/ospf-vrf-default.txt | 17 + .../r4/zebra-vrf-default.txt | 7 + .../test_ospf_multi_vrf_bgp_route_leak.py | 230 + tests/topotests/ospf_netns_vrf/__init__.py | 0 tests/topotests/ospf_netns_vrf/r1/ospfd.conf | 13 + tests/topotests/ospf_netns_vrf/r1/ospfroute.txt | 18 + .../topotests/ospf_netns_vrf/r1/ospfroute_down.txt | 14 + tests/topotests/ospf_netns_vrf/r1/zebra.conf | 17 + tests/topotests/ospf_netns_vrf/r1/zebraroute.txt | 8 + .../topotests/ospf_netns_vrf/r1/zebraroutedown.txt | 7 + tests/topotests/ospf_netns_vrf/r2/ospfd.conf | 14 + tests/topotests/ospf_netns_vrf/r2/ospfroute.txt | 18 + .../topotests/ospf_netns_vrf/r2/ospfroute_down.txt | 14 + tests/topotests/ospf_netns_vrf/r2/zebra.conf | 13 + tests/topotests/ospf_netns_vrf/r2/zebraroute.txt | 8 + .../topotests/ospf_netns_vrf/r2/zebraroutedown.txt | 7 + tests/topotests/ospf_netns_vrf/r3/ospfd.conf | 15 + tests/topotests/ospf_netns_vrf/r3/ospfroute.txt | 18 + .../topotests/ospf_netns_vrf/r3/ospfroute_down.txt | 8 + tests/topotests/ospf_netns_vrf/r3/zebra.conf | 13 + tests/topotests/ospf_netns_vrf/r3/zebraroute.txt | 8 + .../topotests/ospf_netns_vrf/r3/zebraroutedown.txt | 4 + .../ospf_netns_vrf/test_ospf_netns_vrf.dot | 78 + .../ospf_netns_vrf/test_ospf_netns_vrf.jpg | Bin 0 -> 65859 bytes .../ospf_netns_vrf/test_ospf_netns_vrf.py | 311 + tests/topotests/ospf_nssa_topo1/__init__.py | 0 tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf | 22 + tests/topotests/ospf_nssa_topo1/rt1/staticd.conf | 6 + .../rt1/step1/show_ip_ospf_route.ref | 115 + .../rt1/step10/show_ip_ospf_route.ref | 115 + .../rt1/step2/show_ip_ospf_route.ref | 115 + .../rt1/step3/show_ip_ospf_route.ref | 115 + .../rt1/step4/show_ip_ospf_route.ref | 103 + .../rt1/step5/show_ip_ospf_route.ref | 103 + .../rt1/step6/show_ip_ospf_route.ref | 91 + .../rt1/step7/show_ip_ospf_route.ref | 103 + .../rt1/step8/show_ip_ospf_route.ref | 103 + .../rt1/step9/show_ip_ospf_route.ref | 91 + tests/topotests/ospf_nssa_topo1/rt1/zebra.conf | 18 + tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf | 35 + tests/topotests/ospf_nssa_topo1/rt2/staticd.conf | 6 + .../rt2/step1/show_ip_ospf_route.ref | 127 + .../rt2/step10/show_ip_ospf_route.ref | 127 + .../rt2/step2/show_ip_ospf_route.ref | 139 + .../rt2/step3/show_ip_ospf_route.ref | 127 + .../rt2/step4/show_ip_ospf_route.ref | 129 + .../rt2/step5/show_ip_ospf_route.ref | 117 + .../rt2/step6/show_ip_ospf_route.ref | 103 + .../rt2/step7/show_ip_ospf_route.ref | 129 + .../rt2/step8/show_ip_ospf_route.ref | 129 + .../rt2/step9/show_ip_ospf_route.ref | 127 + tests/topotests/ospf_nssa_topo1/rt2/zebra.conf | 24 + tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf | 24 + tests/topotests/ospf_nssa_topo1/rt3/staticd.conf | 8 + .../rt3/step1/show_ip_ospf_route.ref | 138 + .../rt3/step10/show_ip_ospf_route.ref | 150 + .../rt3/step2/show_ip_ospf_route.ref | 138 + .../rt3/step3/show_ip_ospf_route.ref | 138 + .../rt3/step4/show_ip_ospf_route.ref | 150 + .../rt3/step5/show_ip_ospf_route.ref | 138 + .../rt3/step6/show_ip_ospf_route.ref | 126 + .../rt3/step7/show_ip_ospf_route.ref | 150 + .../rt3/step8/show_ip_ospf_route.ref | 150 + .../rt3/step9/show_ip_ospf_route.ref | 150 + tests/topotests/ospf_nssa_topo1/rt3/zebra.conf | 18 + tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf | 24 + tests/topotests/ospf_nssa_topo1/rt4/staticd.conf | 9 + .../rt4/step1/show_ip_ospf_route.ref | 114 + .../rt4/step10/show_ip_ospf_route.ref | 126 + .../rt4/step2/show_ip_ospf_route.ref | 114 + .../rt4/step3/show_ip_ospf_route.ref | 114 + .../rt4/step4/show_ip_ospf_route.ref | 126 + .../rt4/step5/show_ip_ospf_route.ref | 126 + .../rt4/step6/show_ip_ospf_route.ref | 126 + .../rt4/step7/show_ip_ospf_route.ref | 126 + .../rt4/step8/show_ip_ospf_route.ref | 126 + .../rt4/step9/show_ip_ospf_route.ref | 126 + tests/topotests/ospf_nssa_topo1/rt4/zebra.conf | 18 + .../ospf_nssa_topo1/test_ospf_nssa_topo1.py | 416 ++ .../topotests/ospf_prefix_suppression/r1/frr.conf | 47 + .../topotests/ospf_prefix_suppression/r2/frr.conf | 57 + .../topotests/ospf_prefix_suppression/r3/frr.conf | 25 + .../test_ospf_prefix_suppression.py | 951 +++ tests/topotests/ospf_sr_te_topo1/dst/zebra.conf | 23 + tests/topotests/ospf_sr_te_topo1/rt1/bgpd.conf | 16 + tests/topotests/ospf_sr_te_topo1/rt1/ospfd.conf | 35 + tests/topotests/ospf_sr_te_topo1/rt1/pathd.conf | 27 + .../rt1/step2/show_operational_data.ref | 13 + .../step2/show_operational_data_with_candidate.ref | 20 + ...show_operational_data_with_single_candidate.ref | 20 + .../show_operational_data_with_two_candidates.ref | 25 + tests/topotests/ospf_sr_te_topo1/rt1/zebra.conf | 21 + tests/topotests/ospf_sr_te_topo1/rt2/ospfd.conf | 46 + tests/topotests/ospf_sr_te_topo1/rt2/zebra.conf | 35 + tests/topotests/ospf_sr_te_topo1/rt3/ospfd.conf | 45 + tests/topotests/ospf_sr_te_topo1/rt3/zebra.conf | 33 + tests/topotests/ospf_sr_te_topo1/rt4/ospfd.conf | 52 + tests/topotests/ospf_sr_te_topo1/rt4/zebra.conf | 43 + tests/topotests/ospf_sr_te_topo1/rt5/ospfd.conf | 52 + tests/topotests/ospf_sr_te_topo1/rt5/zebra.conf | 43 + tests/topotests/ospf_sr_te_topo1/rt6/bgpd.conf | 12 + tests/topotests/ospf_sr_te_topo1/rt6/ospfd.conf | 40 + tests/topotests/ospf_sr_te_topo1/rt6/pathd.conf | 25 + .../rt6/step2/show_operational_data.ref | 13 + .../step2/show_operational_data_with_candidate.ref | 19 + ...show_operational_data_with_single_candidate.ref | 19 + .../show_operational_data_with_two_candidates.ref | 23 + tests/topotests/ospf_sr_te_topo1/rt6/zebra.conf | 38 + .../ospf_sr_te_topo1/test_ospf_sr_te_topo1.py | 653 ++ tests/topotests/ospf_sr_topo1/__init__.py | 0 tests/topotests/ospf_sr_topo1/rt1/ospfd.conf | 31 + .../ospf_sr_topo1/rt1/step1/show_ip_route.ref | 289 + .../ospf_sr_topo1/rt1/step1/show_mpls_table.ref | 68 + .../ospf_sr_topo1/rt1/step10/show_ip_route.ref | 282 + .../ospf_sr_topo1/rt1/step10/show_mpls_table.ref | 79 + .../ospf_sr_topo1/rt1/step2/show_ip_route.ref | 282 + .../ospf_sr_topo1/rt1/step2/show_mpls_table.ref | 68 + .../ospf_sr_topo1/rt1/step3/show_ip_route.ref | 249 + .../ospf_sr_topo1/rt1/step3/show_mpls_table.ref | 50 + .../ospf_sr_topo1/rt1/step4/show_ip_route.ref | 282 + .../ospf_sr_topo1/rt1/step4/show_mpls_table.ref | 68 + .../ospf_sr_topo1/rt1/step5/show_ip_route.ref | 276 + .../ospf_sr_topo1/rt1/step5/show_mpls_table.ref | 50 + .../ospf_sr_topo1/rt1/step6/show_ip_route.ref | 282 + .../ospf_sr_topo1/rt1/step6/show_mpls_table.ref | 68 + .../ospf_sr_topo1/rt1/step7/show_ip_route.ref | 282 + .../ospf_sr_topo1/rt1/step7/show_mpls_table.ref | 68 + .../ospf_sr_topo1/rt1/step8/show_ip_route.ref | 282 + .../ospf_sr_topo1/rt1/step8/show_mpls_table.ref | 68 + .../ospf_sr_topo1/rt1/step9/show_ip_route.ref | 282 + .../ospf_sr_topo1/rt1/step9/show_mpls_table.ref | 79 + tests/topotests/ospf_sr_topo1/rt1/zebra.conf | 18 + tests/topotests/ospf_sr_topo1/rt2/ospfd.conf | 41 + .../ospf_sr_topo1/rt2/step1/show_ip_route.ref | 330 + .../ospf_sr_topo1/rt2/step1/show_mpls_table.ref | 97 + .../ospf_sr_topo1/rt2/step10/show_ip_route.ref | 254 + .../ospf_sr_topo1/rt2/step10/show_mpls_table.ref | 73 + .../ospf_sr_topo1/rt2/step2/show_ip_route.ref | 303 + .../ospf_sr_topo1/rt2/step2/show_mpls_table.ref | 85 + .../ospf_sr_topo1/rt2/step3/show_ip_route.ref | 256 + .../ospf_sr_topo1/rt2/step3/show_mpls_table.ref | 67 + .../ospf_sr_topo1/rt2/step4/show_ip_route.ref | 303 + .../ospf_sr_topo1/rt2/step4/show_mpls_table.ref | 85 + .../ospf_sr_topo1/rt2/step5/show_ip_route.ref | 297 + .../ospf_sr_topo1/rt2/step5/show_mpls_table.ref | 67 + .../ospf_sr_topo1/rt2/step6/show_ip_route.ref | 303 + .../ospf_sr_topo1/rt2/step6/show_mpls_table.ref | 85 + .../ospf_sr_topo1/rt2/step7/show_ip_route.ref | 300 + .../ospf_sr_topo1/rt2/step7/show_mpls_table.ref | 73 + .../ospf_sr_topo1/rt2/step8/show_ip_route.ref | 303 + .../ospf_sr_topo1/rt2/step8/show_mpls_table.ref | 85 + .../ospf_sr_topo1/rt2/step9/show_ip_route.ref | 303 + .../ospf_sr_topo1/rt2/step9/show_mpls_table.ref | 85 + tests/topotests/ospf_sr_topo1/rt2/zebra.conf | 24 + tests/topotests/ospf_sr_topo1/rt3/ospfd.conf | 41 + .../ospf_sr_topo1/rt3/step1/show_ip_route.ref | 330 + .../ospf_sr_topo1/rt3/step1/show_mpls_table.ref | 97 + .../ospf_sr_topo1/rt3/step10/show_ip_route.ref | 310 + .../ospf_sr_topo1/rt3/step10/show_mpls_table.ref | 85 + .../ospf_sr_topo1/rt3/step2/show_ip_route.ref | 310 + .../ospf_sr_topo1/rt3/step2/show_mpls_table.ref | 85 + .../ospf_sr_topo1/rt3/step3/show_ip_route.ref | 263 + .../ospf_sr_topo1/rt3/step3/show_mpls_table.ref | 67 + .../ospf_sr_topo1/rt3/step4/show_ip_route.ref | 310 + .../ospf_sr_topo1/rt3/step4/show_mpls_table.ref | 85 + .../ospf_sr_topo1/rt3/step5/show_ip_route.ref | 304 + .../ospf_sr_topo1/rt3/step5/show_mpls_table.ref | 67 + .../ospf_sr_topo1/rt3/step6/show_ip_route.ref | 310 + .../ospf_sr_topo1/rt3/step6/show_mpls_table.ref | 85 + .../ospf_sr_topo1/rt3/step7/show_ip_route.ref | 307 + .../ospf_sr_topo1/rt3/step7/show_mpls_table.ref | 73 + .../ospf_sr_topo1/rt3/step8/show_ip_route.ref | 310 + .../ospf_sr_topo1/rt3/step8/show_mpls_table.ref | 85 + .../ospf_sr_topo1/rt3/step9/show_ip_route.ref | 310 + .../ospf_sr_topo1/rt3/step9/show_mpls_table.ref | 85 + tests/topotests/ospf_sr_topo1/rt3/zebra.conf | 24 + tests/topotests/ospf_sr_topo1/rt4/ospfd.conf | 46 + .../ospf_sr_topo1/rt4/step1/show_ip_route.ref | 311 + .../ospf_sr_topo1/rt4/step1/show_mpls_table.ref | 97 + .../ospf_sr_topo1/rt4/step10/show_ip_route.ref | 278 + .../ospf_sr_topo1/rt4/step10/show_mpls_table.ref | 73 + .../ospf_sr_topo1/rt4/step2/show_ip_route.ref | 324 + .../ospf_sr_topo1/rt4/step2/show_mpls_table.ref | 91 + .../ospf_sr_topo1/rt4/step3/show_ip_route.ref | 296 + .../ospf_sr_topo1/rt4/step3/show_mpls_table.ref | 85 + .../ospf_sr_topo1/rt4/step4/show_ip_route.ref | 324 + .../ospf_sr_topo1/rt4/step4/show_mpls_table.ref | 91 + .../ospf_sr_topo1/rt4/step5/show_ip_route.ref | 318 + .../ospf_sr_topo1/rt4/step5/show_mpls_table.ref | 67 + .../ospf_sr_topo1/rt4/step6/show_ip_route.ref | 324 + .../ospf_sr_topo1/rt4/step6/show_mpls_table.ref | 91 + .../ospf_sr_topo1/rt4/step7/show_ip_route.ref | 318 + .../ospf_sr_topo1/rt4/step7/show_mpls_table.ref | 73 + .../ospf_sr_topo1/rt4/step8/show_ip_route.ref | 324 + .../ospf_sr_topo1/rt4/step8/show_mpls_table.ref | 91 + .../ospf_sr_topo1/rt4/step9/show_ip_route.ref | 324 + .../ospf_sr_topo1/rt4/step9/show_mpls_table.ref | 91 + tests/topotests/ospf_sr_topo1/rt4/zebra.conf | 27 + tests/topotests/ospf_sr_topo1/rt5/ospfd.conf | 46 + .../ospf_sr_topo1/rt5/step1/show_ip_route.ref | 311 + .../ospf_sr_topo1/rt5/step1/show_mpls_table.ref | 97 + .../ospf_sr_topo1/rt5/step10/show_ip_route.ref | 300 + .../ospf_sr_topo1/rt5/step10/show_mpls_table.ref | 91 + .../ospf_sr_topo1/rt5/step2/show_ip_route.ref | 307 + .../ospf_sr_topo1/rt5/step2/show_mpls_table.ref | 91 + .../ospf_sr_topo1/rt5/step3/show_ip_route.ref | 272 + .../ospf_sr_topo1/rt5/step3/show_mpls_table.ref | 85 + .../ospf_sr_topo1/rt5/step4/show_ip_route.ref | 307 + .../ospf_sr_topo1/rt5/step4/show_mpls_table.ref | 91 + .../ospf_sr_topo1/rt5/step5/show_ip_route.ref | 286 + .../ospf_sr_topo1/rt5/step5/show_mpls_table.ref | 67 + .../ospf_sr_topo1/rt5/step6/show_ip_route.ref | 307 + .../ospf_sr_topo1/rt5/step6/show_mpls_table.ref | 91 + .../ospf_sr_topo1/rt5/step7/show_ip_route.ref | 286 + .../ospf_sr_topo1/rt5/step7/show_mpls_table.ref | 73 + .../ospf_sr_topo1/rt5/step8/show_ip_route.ref | 307 + .../ospf_sr_topo1/rt5/step8/show_mpls_table.ref | 91 + .../ospf_sr_topo1/rt5/step9/show_ip_route.ref | 292 + .../ospf_sr_topo1/rt5/step9/show_mpls_table.ref | 91 + tests/topotests/ospf_sr_topo1/rt5/zebra.conf | 27 + tests/topotests/ospf_sr_topo1/rt6/ospfd.conf | 36 + .../ospf_sr_topo1/rt6/step1/show_ip_route.ref | 291 + .../ospf_sr_topo1/rt6/step1/show_mpls_table.ref | 68 + .../ospf_sr_topo1/rt6/step10/show_ip_route.ref | 284 + .../ospf_sr_topo1/rt6/step10/show_mpls_table.ref | 68 + .../ospf_sr_topo1/rt6/step2/show_ip_route.ref | 284 + .../ospf_sr_topo1/rt6/step2/show_mpls_table.ref | 68 + .../ospf_sr_topo1/rt6/step3/show_ip_route.ref | 2 + .../ospf_sr_topo1/rt6/step3/show_mpls_table.ref | 2 + .../ospf_sr_topo1/rt6/step4/show_ip_route.ref | 284 + .../ospf_sr_topo1/rt6/step4/show_mpls_table.ref | 68 + .../ospf_sr_topo1/rt6/step5/show_ip_route.ref | 266 + .../ospf_sr_topo1/rt6/step5/show_mpls_table.ref | 2 + .../ospf_sr_topo1/rt6/step6/show_ip_route.ref | 284 + .../ospf_sr_topo1/rt6/step6/show_mpls_table.ref | 68 + .../ospf_sr_topo1/rt6/step7/show_ip_route.ref | 278 + .../ospf_sr_topo1/rt6/step7/show_mpls_table.ref | 50 + .../ospf_sr_topo1/rt6/step8/show_ip_route.ref | 284 + .../ospf_sr_topo1/rt6/step8/show_mpls_table.ref | 68 + .../ospf_sr_topo1/rt6/step9/show_ip_route.ref | 284 + .../ospf_sr_topo1/rt6/step9/show_mpls_table.ref | 68 + tests/topotests/ospf_sr_topo1/rt6/zebra.conf | 21 + .../topotests/ospf_sr_topo1/test_ospf_sr_topo1.py | 669 +++ tests/topotests/ospf_suppress_fa/__init__.py | 0 tests/topotests/ospf_suppress_fa/r1/ospfd.conf | 9 + tests/topotests/ospf_suppress_fa/r1/zebra.conf | 4 + tests/topotests/ospf_suppress_fa/r2/ospfd.conf | 16 + tests/topotests/ospf_suppress_fa/r2/zebra.conf | 7 + tests/topotests/ospf_suppress_fa/r3/ospfd.conf | 11 + tests/topotests/ospf_suppress_fa/r3/zebra.conf | 8 + .../ospf_suppress_fa/test_ospf_suppress_fa.dot | 66 + .../ospf_suppress_fa/test_ospf_suppress_fa.dot.jpg | Bin 0 -> 33501 bytes .../ospf_suppress_fa/test_ospf_suppress_fa.py | 173 + tests/topotests/ospf_te_topo1/__init__.py | 0 tests/topotests/ospf_te_topo1/r1/ospfd.conf | 23 + tests/topotests/ospf_te_topo1/r1/zebra.conf | 22 + tests/topotests/ospf_te_topo1/r2/ospfd.conf | 34 + tests/topotests/ospf_te_topo1/r2/zebra.conf | 34 + tests/topotests/ospf_te_topo1/r3/ospfd.conf | 24 + tests/topotests/ospf_te_topo1/r3/zebra.conf | 22 + tests/topotests/ospf_te_topo1/r4/ospfd.conf | 21 + tests/topotests/ospf_te_topo1/r4/zebra.conf | 12 + .../ospf_te_topo1/reference/ted_step1.json | 571 ++ .../ospf_te_topo1/reference/ted_step2.json | 473 ++ .../ospf_te_topo1/reference/ted_step3.json | 405 ++ .../ospf_te_topo1/reference/ted_step4.json | 486 ++ .../ospf_te_topo1/reference/ted_step5.json | 608 ++ .../ospf_te_topo1/reference/ted_step6.json | 609 ++ .../ospf_te_topo1/reference/ted_step7.json | 451 ++ .../topotests/ospf_te_topo1/test_ospf_te_topo1.py | 282 + tests/topotests/ospf_tilfa_topo1/__init__.py | 0 tests/topotests/ospf_tilfa_topo1/rt1/ospfd.conf | 27 + .../rt1/step1/show_ip_route_initial.ref | 156 + .../rt1/step2/show_ip_route_initial.ref | 156 + .../rt1/step2/show_ip_route_link_protection.ref | 226 + .../rt1/step3/show_ip_route_initial.ref | 156 + .../rt1/step3/show_ip_route_node_protection.ref | 192 + tests/topotests/ospf_tilfa_topo1/rt1/zebra.conf | 17 + tests/topotests/ospf_tilfa_topo1/rt2/ospfd.conf | 27 + tests/topotests/ospf_tilfa_topo1/rt2/zebra.conf | 17 + tests/topotests/ospf_tilfa_topo1/rt3/ospfd.conf | 27 + tests/topotests/ospf_tilfa_topo1/rt3/zebra.conf | 17 + tests/topotests/ospf_tilfa_topo1/rt4/ospfd.conf | 27 + tests/topotests/ospf_tilfa_topo1/rt4/zebra.conf | 17 + tests/topotests/ospf_tilfa_topo1/rt5/ospfd.conf | 27 + tests/topotests/ospf_tilfa_topo1/rt5/zebra.conf | 17 + .../ospf_tilfa_topo1/test_ospf_tilfa_topo1.py | 226 + tests/topotests/ospf_topo1/__init__.py | 0 tests/topotests/ospf_topo1/r1/ospf6d.conf | 13 + tests/topotests/ospf_topo1/r1/ospf6route.txt | 13 + tests/topotests/ospf_topo1/r1/ospf6route_down.txt | 5 + tests/topotests/ospf_topo1/r1/ospf6route_ecmp.txt | 13 + tests/topotests/ospf_topo1/r1/ospfd.conf | 13 + tests/topotests/ospf_topo1/r1/ospfroute.txt | 23 + tests/topotests/ospf_topo1/r1/ospfroute_down.txt | 13 + tests/topotests/ospf_topo1/r1/zebra.conf | 11 + tests/topotests/ospf_topo1/r2/ospf6d.conf | 17 + tests/topotests/ospf_topo1/r2/ospf6route.txt | 13 + tests/topotests/ospf_topo1/r2/ospf6route_down.txt | 5 + tests/topotests/ospf_topo1/r2/ospf6route_ecmp.txt | 13 + tests/topotests/ospf_topo1/r2/ospfd.conf | 13 + tests/topotests/ospf_topo1/r2/ospfroute.txt | 23 + tests/topotests/ospf_topo1/r2/ospfroute_down.txt | 13 + tests/topotests/ospf_topo1/r2/zebra.conf | 11 + tests/topotests/ospf_topo1/r3/ospf6d.conf | 22 + tests/topotests/ospf_topo1/r3/ospf6route.txt | 12 + tests/topotests/ospf_topo1/r3/ospf6route_down.txt | 5 + tests/topotests/ospf_topo1/r3/ospf6route_ecmp.txt | 12 + tests/topotests/ospf_topo1/r3/ospfd.conf | 18 + tests/topotests/ospf_topo1/r3/ospfroute.txt | 23 + tests/topotests/ospf_topo1/r3/ospfroute_down.txt | 13 + tests/topotests/ospf_topo1/r3/zebra.conf | 15 + tests/topotests/ospf_topo1/r4/ospf6d.conf | 17 + tests/topotests/ospf_topo1/r4/ospf6route.txt | 13 + tests/topotests/ospf_topo1/r4/ospf6route_down.txt | 5 + tests/topotests/ospf_topo1/r4/ospf6route_ecmp.txt | 12 + tests/topotests/ospf_topo1/r4/ospfd.conf | 13 + tests/topotests/ospf_topo1/r4/ospfroute.txt | 23 + tests/topotests/ospf_topo1/r4/ospfroute_down.txt | 13 + tests/topotests/ospf_topo1/r4/zebra.conf | 11 + tests/topotests/ospf_topo1/test_ospf_topo1.dot | 104 + tests/topotests/ospf_topo1/test_ospf_topo1.jpg | Bin 0 -> 123663 bytes tests/topotests/ospf_topo1/test_ospf_topo1.py | 585 ++ tests/topotests/ospf_topo2/__init__.py | 0 tests/topotests/ospf_topo2/r1/frr.conf | 61 + tests/topotests/ospf_topo2/r2/frr.conf | 61 + tests/topotests/ospf_topo2/r3/frr.conf | 61 + tests/topotests/ospf_topo2/r4/frr.conf | 61 + tests/topotests/ospf_topo2/test_ospf_topo2.dot | 44 + tests/topotests/ospf_topo2/test_ospf_topo2.png | Bin 0 -> 88488 bytes tests/topotests/ospf_topo2/test_ospf_topo2.py | 317 + tests/topotests/ospf_unnumbered/r1/ospf-route.json | 1 + tests/topotests/ospf_unnumbered/r1/ospfd.conf | 13 + tests/topotests/ospf_unnumbered/r1/v4_route.json | 84 + tests/topotests/ospf_unnumbered/r1/zebra.conf | 7 + tests/topotests/ospf_unnumbered/r2/ospf-route.json | 1 + tests/topotests/ospf_unnumbered/r2/ospfd.conf | 13 + tests/topotests/ospf_unnumbered/r2/v4_route.json | 84 + tests/topotests/ospf_unnumbered/r2/zebra.conf | 7 + .../ospf_unnumbered/test_ospf_unnumbered.py | 152 + tests/topotests/ospfapi/ctester.py | 154 + tests/topotests/ospfapi/lib | 1 + tests/topotests/ospfapi/r1/ospfd.conf | 15 + tests/topotests/ospfapi/r1/zebra.conf | 6 + tests/topotests/ospfapi/r2/ospfd.conf | 15 + tests/topotests/ospfapi/r2/zebra.conf | 7 + tests/topotests/ospfapi/r3/ospfd.conf | 15 + tests/topotests/ospfapi/r3/zebra.conf | 7 + tests/topotests/ospfapi/r4/ospfd.conf | 15 + tests/topotests/ospfapi/r4/zebra.conf | 6 + tests/topotests/ospfapi/test_ospf_clientapi.py | 1586 +++++ .../ospfv3_asbr_summary_topo1.json | 198 + .../ospfv3_asbr_summary_type7_lsa.json | 202 + .../ospfv3_authentication.json | 169 + .../ospfv3_dual_stack.json | 312 + .../ospfv3_basic_functionality/ospfv3_ecmp.json | 377 ++ .../ospfv3_ecmp_lan.json | 264 + .../ospfv3_basic_functionality/ospfv3_lan.json | 140 + .../ospfv3_basic_functionality/ospfv3_nssa.json | 86 + .../ospfv3_basic_functionality/ospfv3_nssa2.json | 197 + .../ospfv3_routemaps.json | 180 + .../ospfv3_rte_calc.json | 190 + .../ospfv3_single_area.json | 190 + .../test_ospfv3_asbr_summary_topo1.py | 2718 +++++++++ .../test_ospfv3_authentication.py | 1414 +++++ .../ospfv3_basic_functionality/test_ospfv3_ecmp.py | 472 ++ .../test_ospfv3_ecmp_lan.py | 383 ++ .../ospfv3_basic_functionality/test_ospfv3_nssa.py | 158 + .../test_ospfv3_nssa2.py | 583 ++ .../test_ospfv3_routemaps.py | 1229 ++++ .../test_ospfv3_rte_calc.py | 906 +++ .../test_ospfv3_single_area.py | 1266 ++++ tests/topotests/pbr_topo1/__init__.py | 0 tests/topotests/pbr_topo1/r1/linux-rules.json | 19 + tests/topotests/pbr_topo1/r1/pbr-interface.json | 27 + tests/topotests/pbr_topo1/r1/pbr-map.json | 152 + .../topotests/pbr_topo1/r1/pbr-nexthop-groups.json | 95 + tests/topotests/pbr_topo1/r1/pbrd.conf | 100 + tests/topotests/pbr_topo1/r1/zebra.conf | 14 + tests/topotests/pbr_topo1/test_pbr_topo1.py | 435 ++ tests/topotests/pim_acl/h1/zebra.conf | 10 + tests/topotests/pim_acl/h2/zebra.conf | 8 + tests/topotests/pim_acl/r1/acl_1_pim_join.json | 21 + tests/topotests/pim_acl/r1/acl_2_pim_join.json | 21 + tests/topotests/pim_acl/r1/acl_3_pim_join.json | 21 + tests/topotests/pim_acl/r1/acl_4_pim_join.json | 21 + tests/topotests/pim_acl/r1/acl_5_pim_join.json | 21 + tests/topotests/pim_acl/r1/acl_6_pim_join.json | 21 + tests/topotests/pim_acl/r1/ospf_neighbor.json | 59 + tests/topotests/pim_acl/r1/ospfd.conf | 16 + tests/topotests/pim_acl/r1/pim_neighbor.json | 31 + tests/topotests/pim_acl/r1/pimd.conf | 31 + tests/topotests/pim_acl/r1/zebra.conf | 18 + tests/topotests/pim_acl/r11/acl_1_pim_join.json | 19 + tests/topotests/pim_acl/r11/ospfd.conf | 14 + tests/topotests/pim_acl/r11/pimd.conf | 17 + tests/topotests/pim_acl/r11/zebra.conf | 13 + tests/topotests/pim_acl/r12/acl_2_pim_join.json | 19 + tests/topotests/pim_acl/r12/ospfd.conf | 14 + tests/topotests/pim_acl/r12/pimd.conf | 17 + tests/topotests/pim_acl/r12/zebra.conf | 13 + tests/topotests/pim_acl/r13/acl_3_pim_join.json | 19 + tests/topotests/pim_acl/r13/ospfd.conf | 14 + tests/topotests/pim_acl/r13/pimd.conf | 17 + tests/topotests/pim_acl/r13/zebra.conf | 13 + tests/topotests/pim_acl/r14/acl_4_pim_join.json | 19 + tests/topotests/pim_acl/r14/acl_5_pim_join.json | 19 + tests/topotests/pim_acl/r14/ospfd.conf | 14 + tests/topotests/pim_acl/r14/pimd.conf | 18 + tests/topotests/pim_acl/r14/zebra.conf | 13 + tests/topotests/pim_acl/r15/acl_6_pim_join.json | 19 + tests/topotests/pim_acl/r15/ospfd.conf | 14 + tests/topotests/pim_acl/r15/pimd.conf | 17 + tests/topotests/pim_acl/r15/zebra.conf | 13 + tests/topotests/pim_acl/test_pim_acl.py | 332 ++ tests/topotests/pim_basic/mcast-rx.py | 79 + tests/topotests/pim_basic/mcast-tx.py | 84 + tests/topotests/pim_basic/r1/bgpd.conf | 5 + tests/topotests/pim_basic/r1/pimd.conf | 18 + tests/topotests/pim_basic/r1/rp-info.json | 9 + tests/topotests/pim_basic/r1/zebra.conf | 14 + tests/topotests/pim_basic/r2/pimd.conf | 1 + tests/topotests/pim_basic/r2/zebra.conf | 8 + tests/topotests/pim_basic/r3/pimd.conf | 1 + tests/topotests/pim_basic/r3/zebra.conf | 8 + tests/topotests/pim_basic/rp/bgpd.conf | 5 + tests/topotests/pim_basic/rp/pimd.conf | 13 + tests/topotests/pim_basic/rp/upstream.json | 17 + tests/topotests/pim_basic/rp/zebra.conf | 8 + tests/topotests/pim_basic/test_pim.py | 235 + tests/topotests/pim_basic_topo2/__init__.py | 0 tests/topotests/pim_basic_topo2/r1/bfdd.conf | 6 + tests/topotests/pim_basic_topo2/r1/pimd.conf | 4 + tests/topotests/pim_basic_topo2/r1/zebra.conf | 3 + tests/topotests/pim_basic_topo2/r2/bfdd.conf | 2 + tests/topotests/pim_basic_topo2/r2/pimd.conf | 13 + tests/topotests/pim_basic_topo2/r2/zebra.conf | 9 + tests/topotests/pim_basic_topo2/r3/bfdd.conf | 2 + tests/topotests/pim_basic_topo2/r3/pimd.conf | 4 + tests/topotests/pim_basic_topo2/r3/zebra.conf | 3 + tests/topotests/pim_basic_topo2/r4/bfdd.conf | 2 + tests/topotests/pim_basic_topo2/r4/pimd.conf | 4 + tests/topotests/pim_basic_topo2/r4/zebra.conf | 3 + .../pim_basic_topo2/test_pim_basic_topo2.dot | 73 + .../pim_basic_topo2/test_pim_basic_topo2.png | Bin 0 -> 33496 bytes .../pim_basic_topo2/test_pim_basic_topo2.py | 225 + tests/topotests/pim_igmp_vrf/h1/zebra.conf | 10 + tests/topotests/pim_igmp_vrf/h2/zebra.conf | 8 + tests/topotests/pim_igmp_vrf/h3/zebra.conf | 10 + tests/topotests/pim_igmp_vrf/h4/zebra.conf | 8 + .../pim_igmp_vrf/r1/ospf_blue_neighbor.json | 15 + .../pim_igmp_vrf/r1/ospf_red_neighbor.json | 16 + tests/topotests/pim_igmp_vrf/r1/ospfd.conf | 26 + tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json | 22 + .../pim_igmp_vrf/r1/pim_blue_neighbor.json | 13 + .../pim_igmp_vrf/r1/pim_blue_pimreg11.json | 14 + tests/topotests/pim_igmp_vrf/r1/pim_red_join.json | 21 + .../pim_igmp_vrf/r1/pim_red_neighbor.json | 13 + .../pim_igmp_vrf/r1/pim_red_pimreg12.json | 14 + tests/topotests/pim_igmp_vrf/r1/pimd.conf | 27 + tests/topotests/pim_igmp_vrf/r1/zebra.conf | 30 + tests/topotests/pim_igmp_vrf/r11/ospfd.conf | 14 + .../topotests/pim_igmp_vrf/r11/pim_blue_join.json | 19 + tests/topotests/pim_igmp_vrf/r11/pimd.conf | 17 + tests/topotests/pim_igmp_vrf/r11/zebra.conf | 13 + tests/topotests/pim_igmp_vrf/r12/ospfd.conf | 14 + tests/topotests/pim_igmp_vrf/r12/pim_red_join.json | 19 + tests/topotests/pim_igmp_vrf/r12/pimd.conf | 17 + tests/topotests/pim_igmp_vrf/r12/zebra.conf | 13 + tests/topotests/pim_igmp_vrf/test_pim_vrf.py | 389 ++ tests/topotests/pytest.ini | 87 + tests/topotests/rip_allow_ecmp/__init__.py | 0 tests/topotests/rip_allow_ecmp/r1/frr.conf | 9 + tests/topotests/rip_allow_ecmp/r2/frr.conf | 13 + tests/topotests/rip_allow_ecmp/r3/frr.conf | 13 + tests/topotests/rip_allow_ecmp/r4/frr.conf | 13 + tests/topotests/rip_allow_ecmp/r5/frr.conf | 13 + .../rip_allow_ecmp/test_rip_allow_ecmp.py | 156 + tests/topotests/rip_bfd_topo1/__init__.py | 0 tests/topotests/rip_bfd_topo1/r1/bfdd.conf | 6 + tests/topotests/rip_bfd_topo1/r1/ripd.conf | 17 + tests/topotests/rip_bfd_topo1/r1/zebra.conf | 11 + tests/topotests/rip_bfd_topo1/r2/bfdd.conf | 6 + tests/topotests/rip_bfd_topo1/r2/ripd.conf | 11 + tests/topotests/rip_bfd_topo1/r2/staticd.conf | 1 + tests/topotests/rip_bfd_topo1/r2/zebra.conf | 8 + tests/topotests/rip_bfd_topo1/r3/bfdd.conf | 6 + tests/topotests/rip_bfd_topo1/r3/ripd.conf | 11 + tests/topotests/rip_bfd_topo1/r3/staticd.conf | 1 + tests/topotests/rip_bfd_topo1/r3/zebra.conf | 7 + .../topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot | 58 + .../topotests/rip_bfd_topo1/test_rip_bfd_topo1.png | Bin 0 -> 27400 bytes .../topotests/rip_bfd_topo1/test_rip_bfd_topo1.py | 252 + tests/topotests/rip_passive_interface/__init__.py | 0 tests/topotests/rip_passive_interface/r1/frr.conf | 9 + tests/topotests/rip_passive_interface/r2/frr.conf | 13 + tests/topotests/rip_passive_interface/r3/frr.conf | 13 + .../test_rip_passive_interface.py | 102 + tests/topotests/rip_topo1/r1/rip_status.ref | 22 + tests/topotests/rip_topo1/r1/ripd.conf | 13 + tests/topotests/rip_topo1/r1/show_ip_rip.ref | 12 + tests/topotests/rip_topo1/r1/show_ip_route.ref | 3 + tests/topotests/rip_topo1/r1/zebra.conf | 26 + tests/topotests/rip_topo1/r2/rip_status.ref | 19 + tests/topotests/rip_topo1/r2/ripd.conf | 12 + tests/topotests/rip_topo1/r2/show_ip_rip.ref | 12 + tests/topotests/rip_topo1/r2/show_ip_route.ref | 4 + tests/topotests/rip_topo1/r2/zebra.conf | 21 + tests/topotests/rip_topo1/r3/rip_status.ref | 16 + tests/topotests/rip_topo1/r3/ripd.conf | 13 + tests/topotests/rip_topo1/r3/show_ip_rip.ref | 12 + tests/topotests/rip_topo1/r3/show_ip_route.ref | 3 + tests/topotests/rip_topo1/r3/zebra.conf | 22 + tests/topotests/rip_topo1/test_rip_topo1.dot | 61 + tests/topotests/rip_topo1/test_rip_topo1.pdf | Bin 0 -> 18433 bytes tests/topotests/rip_topo1/test_rip_topo1.py | 336 ++ tests/topotests/ripng_allow_ecmp/__init__.py | 0 tests/topotests/ripng_allow_ecmp/r1/frr.conf | 9 + tests/topotests/ripng_allow_ecmp/r2/frr.conf | 13 + tests/topotests/ripng_allow_ecmp/r3/frr.conf | 14 + tests/topotests/ripng_allow_ecmp/r4/frr.conf | 14 + tests/topotests/ripng_allow_ecmp/r5/frr.conf | 14 + .../ripng_allow_ecmp/test_ripng_allow_ecmp.py | 93 + tests/topotests/ripng_route_map/__init__.py | 0 tests/topotests/ripng_route_map/r1/frr.conf | 21 + tests/topotests/ripng_route_map/r2/frr.conf | 14 + tests/topotests/ripng_route_map/r3/frr.conf | 14 + .../ripng_route_map/test_ripng_route_map.py | 79 + tests/topotests/ripng_topo1/r1/ripng_status.ref | 20 + tests/topotests/ripng_topo1/r1/ripngd.conf | 16 + tests/topotests/ripng_topo1/r1/show_ipv6_ripng.ref | 18 + tests/topotests/ripng_topo1/r1/show_ipv6_route.ref | 3 + tests/topotests/ripng_topo1/r1/zebra.conf | 25 + tests/topotests/ripng_topo1/r2/ripng_status.ref | 20 + tests/topotests/ripng_topo1/r2/ripngd.conf | 13 + tests/topotests/ripng_topo1/r2/show_ipv6_ripng.ref | 18 + tests/topotests/ripng_topo1/r2/show_ipv6_route.ref | 4 + tests/topotests/ripng_topo1/r2/zebra.conf | 21 + tests/topotests/ripng_topo1/r3/ripng_status.ref | 16 + tests/topotests/ripng_topo1/r3/ripngd.conf | 15 + tests/topotests/ripng_topo1/r3/show_ipv6_ripng.ref | 18 + tests/topotests/ripng_topo1/r3/show_ipv6_route.ref | 3 + tests/topotests/ripng_topo1/r3/zebra.conf | 22 + tests/topotests/ripng_topo1/test_ripng_topo1.dot | 59 + tests/topotests/ripng_topo1/test_ripng_topo1.pdf | Bin 0 -> 18609 bytes tests/topotests/ripng_topo1/test_ripng_topo1.py | 380 ++ .../topotests/route_scale/r1/installed.routes.json | 16 + tests/topotests/route_scale/r1/no.routes.json | 11 + tests/topotests/route_scale/r1/sharpd.conf | 76 + tests/topotests/route_scale/r1/zebra.conf | 96 + tests/topotests/route_scale/scale_test_common.py | 202 + tests/topotests/route_scale/test_route_scale1.py | 64 + tests/topotests/route_scale/test_route_scale2.py | 64 + tests/topotests/simple_snmp_test/r1/bgpd.conf | 6 + tests/topotests/simple_snmp_test/r1/isisd.conf | 46 + tests/topotests/simple_snmp_test/r1/snmpd.conf | 18 + tests/topotests/simple_snmp_test/r1/zebra.conf | 22 + .../topotests/simple_snmp_test/test_simple_snmp.py | 118 + tests/topotests/srv6_locator/__init__.py | 0 tests/topotests/srv6_locator/expected_chunks1.json | 1 + tests/topotests/srv6_locator/expected_chunks2.json | 8 + tests/topotests/srv6_locator/expected_chunks3.json | 1 + tests/topotests/srv6_locator/expected_chunks4.json | 2 + tests/topotests/srv6_locator/expected_chunks5.json | 2 + tests/topotests/srv6_locator/expected_chunks6.json | 2 + .../srv6_locator/expected_ipv6_routes.json | 29 + .../topotests/srv6_locator/expected_locators1.json | 26 + .../topotests/srv6_locator/expected_locators2.json | 26 + .../topotests/srv6_locator/expected_locators3.json | 26 + .../topotests/srv6_locator/expected_locators4.json | 38 + .../topotests/srv6_locator/expected_locators5.json | 27 + .../topotests/srv6_locator/expected_locators6.json | 5 + tests/topotests/srv6_locator/r1/setup.sh | 2 + tests/topotests/srv6_locator/r1/sharpd.conf | 7 + tests/topotests/srv6_locator/r1/zebra.conf | 22 + tests/topotests/srv6_locator/test_srv6_locator.py | 151 + .../srv6_locator_custom_bits_length/__init__.py | 0 .../expected_chunks1.json | 1 + .../expected_chunks2.json | 8 + .../expected_chunks3.json | 1 + .../expected_chunks4.json | 2 + .../expected_chunks5.json | 2 + .../expected_chunks6.json | 2 + .../expected_locators1.json | 34 + .../expected_locators2.json | 34 + .../expected_locators3.json | 34 + .../expected_locators4.json | 49 + .../expected_locators5.json | 34 + .../expected_locators6.json | 4 + .../srv6_locator_custom_bits_length/r1/setup.sh | 2 + .../srv6_locator_custom_bits_length/r1/sharpd.conf | 7 + .../srv6_locator_custom_bits_length/r1/zebra.conf | 22 + .../test_srv6_locator.py | 147 + tests/topotests/srv6_locator_usid/__init__.py | 0 .../srv6_locator_usid/expected_chunks_1.json | 1 + .../srv6_locator_usid/expected_chunks_2.json | 8 + .../srv6_locator_usid/expected_chunks_3.json | 1 + .../srv6_locator_usid/expected_chunks_4.json | 1 + .../srv6_locator_usid/expected_chunks_5.json | 2 + .../srv6_locator_usid/expected_chunks_6.json | 2 + .../srv6_locator_usid/expected_chunks_7.json | 2 + .../srv6_locator_usid/expected_chunks_8.json | 2 + .../srv6_locator_usid/expected_locators_1.json | 20 + .../srv6_locator_usid/expected_locators_2.json | 20 + .../srv6_locator_usid/expected_locators_3.json | 20 + .../srv6_locator_usid/expected_locators_4.json | 35 + .../srv6_locator_usid/expected_locators_5.json | 36 + .../srv6_locator_usid/expected_locators_6.json | 35 + .../srv6_locator_usid/expected_locators_7.json | 19 + .../srv6_locator_usid/expected_locators_8.json | 4 + tests/topotests/srv6_locator_usid/r1/setup.sh | 2 + tests/topotests/srv6_locator_usid/r1/sharpd.conf | 7 + tests/topotests/srv6_locator_usid/r1/zebra.conf | 20 + .../srv6_locator_usid/test_srv6_locator_usid.py | 255 + tests/topotests/srv6_static_route/__init__.py | 0 .../srv6_static_route/expected_srv6_route.json | 49 + tests/topotests/srv6_static_route/r1/mgmtd.conf | 0 tests/topotests/srv6_static_route/r1/setup.sh | 2 + tests/topotests/srv6_static_route/r1/staticd.conf | 9 + tests/topotests/srv6_static_route/r1/zebra.conf | 10 + .../topotests/srv6_static_route/test_srv6_route.py | 90 + tests/topotests/static_routing_mpls/r1/zebra.conf | 16 + tests/topotests/static_routing_mpls/r2/zebra.conf | 18 + .../test_static_routing_mpls.py | 139 + .../static_routes_topo1_ebgp.json | 157 + .../static_routes_topo2_ebgp.json | 363 ++ .../static_routes_topo3_ebgp.json | 189 + .../static_routes_topo4_ebgp.json | 428 ++ .../test_static_routes_topo1_ebgp.py | 1321 ++++ .../test_static_routes_topo2_ebgp.py | 1731 ++++++ .../test_static_routes_topo3_ebgp.py | 1346 +++++ .../test_static_routes_topo4_ebgp.py | 976 +++ .../static_routes_topo1_ibgp.json | 157 + .../static_routes_topo2_ibgp.json | 371 ++ .../static_routes_topo3_ibgp.json | 189 + .../static_routes_topo4_ibgp.json | 428 ++ .../test_static_routes_topo1_ibgp.py | 1119 ++++ .../test_static_routes_topo2_ibgp.py | 2006 +++++++ .../test_static_routes_topo3_ibgp.py | 876 +++ .../test_static_routes_topo4_ibgp.py | 972 +++ tests/topotests/static_simple/r1/mgmtd.conf | 1 + tests/topotests/static_simple/r1/staticd.conf | 1 + tests/topotests/static_simple/r1/zebra.conf | 11 + .../topotests/static_simple/test_static_simple.py | 214 + tests/topotests/subdir.am | 7 + tests/topotests/tc_basic/test_tc_basic.py | 120 + .../zebra_multiple_connected/r1/ip_route.json | 62 + .../zebra_multiple_connected/r1/ip_route2.json | 102 + .../zebra_multiple_connected/r1/zebra.conf | 9 + .../test_zebra_multiple_connected.py | 149 + tests/topotests/zebra_netlink/__init__.py | 0 tests/topotests/zebra_netlink/r1/zebra.conf | 2 + .../topotests/zebra_netlink/test_zebra_netlink.py | 110 + .../topotests/zebra_nht_resolution/r1/sharpd.conf | 0 tests/topotests/zebra_nht_resolution/r1/zebra.conf | 5 + .../test_verify_nh_resolution.py | 153 + tests/topotests/zebra_opaque/__init__.py | 0 tests/topotests/zebra_opaque/r1/bgpd.conf | 7 + tests/topotests/zebra_opaque/r1/zebra.conf | 4 + tests/topotests/zebra_opaque/r2/bgpd.conf | 15 + tests/topotests/zebra_opaque/r2/zebra.conf | 4 + tests/topotests/zebra_opaque/r3/ospf6d.conf | 9 + tests/topotests/zebra_opaque/r3/ospfd.conf | 9 + tests/topotests/zebra_opaque/r3/zebra.conf | 5 + tests/topotests/zebra_opaque/r4/ospf6d.conf | 9 + tests/topotests/zebra_opaque/r4/ospfd.conf | 9 + tests/topotests/zebra_opaque/r4/zebra.conf | 5 + tests/topotests/zebra_opaque/test_zebra_opaque.py | 117 + tests/topotests/zebra_rib/r1/iproute.ref | 512 ++ tests/topotests/zebra_rib/r1/sharp_rmap.ref | 17 + tests/topotests/zebra_rib/r1/static_rmap.ref | 9 + tests/topotests/zebra_rib/r1/v4_route_1.json | 24 + .../zebra_rib/r1/v4_route_1_static_override.json | 40 + .../zebra_rib/r1/v4_route_1_vrf_before.json | 27 + tests/topotests/zebra_rib/r1/v4_route_2.json | 23 + tests/topotests/zebra_rib/r1/zebra.conf | 26 + tests/topotests/zebra_rib/test_zebra_rib.py | 372 ++ tests/topotests/zebra_seg6_route/r1/routes.json | 25 + tests/topotests/zebra_seg6_route/r1/setup.sh | 5 + tests/topotests/zebra_seg6_route/r1/sharpd.conf | 0 tests/topotests/zebra_seg6_route/r1/zebra.conf | 13 + .../zebra_seg6_route/test_zebra_seg6_route.py | 95 + .../topotests/zebra_seg6local_route/r1/routes.json | 123 + tests/topotests/zebra_seg6local_route/r1/setup.sh | 6 + .../topotests/zebra_seg6local_route/r1/sharpd.conf | 0 .../topotests/zebra_seg6local_route/r1/zebra.conf | 9 + .../test_zebra_seg6local_route.py | 110 + 4791 files changed, 548016 insertions(+) create mode 100644 tests/topotests/.gitignore create mode 100644 tests/topotests/Dockerfile create mode 100644 tests/topotests/README.md create mode 100644 tests/topotests/all_protocol_startup/r1/babeld.conf create mode 100644 tests/topotests/all_protocol_startup/r1/bgpd.conf create mode 100644 tests/topotests/all_protocol_startup/r1/ip_nht.ref create mode 100644 tests/topotests/all_protocol_startup/r1/ipv4_routes.ref create mode 100644 tests/topotests/all_protocol_startup/r1/ipv6_nht.ref create mode 100644 tests/topotests/all_protocol_startup/r1/ipv6_routes.ref create mode 100644 tests/topotests/all_protocol_startup/r1/isisd.conf create mode 100644 tests/topotests/all_protocol_startup/r1/ldpd.conf create mode 100644 tests/topotests/all_protocol_startup/r1/nhrpd.conf create mode 100644 tests/topotests/all_protocol_startup/r1/ospf6d.conf create mode 100644 tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4 create mode 100644 tests/topotests/all_protocol_startup/r1/ospfd.conf create mode 100644 tests/topotests/all_protocol_startup/r1/pbrd.conf create mode 100644 tests/topotests/all_protocol_startup/r1/rip_status.ref create mode 100644 tests/topotests/all_protocol_startup/r1/ripd.conf create mode 100644 tests/topotests/all_protocol_startup/r1/ripng_status.ref create mode 100644 tests/topotests/all_protocol_startup/r1/ripngd.conf create mode 100644 tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post4.1.ref create mode 100644 tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post5.0.ref create mode 100644 tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post6.1.ref create mode 100644 tests/topotests/all_protocol_startup/r1/show_bgp_ipv4.ref create mode 100644 tests/topotests/all_protocol_startup/r1/show_bgp_ipv6-post4.1.ref create mode 100644 tests/topotests/all_protocol_startup/r1/show_bgp_ipv6.ref create mode 100644 tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_post6.1.ref create mode 100644 tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref create mode 100644 tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref create mode 100644 tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref create mode 100644 tests/topotests/all_protocol_startup/r1/show_ipv6_ospf6_interface create mode 100644 tests/topotests/all_protocol_startup/r1/show_ipv6_ospf6_interface.ref create mode 100644 tests/topotests/all_protocol_startup/r1/show_isis_interface_detail.ref create mode 100644 tests/topotests/all_protocol_startup/r1/show_mpls_ldp_interface.ref create mode 100644 tests/topotests/all_protocol_startup/r1/show_route_map.ref create mode 100644 tests/topotests/all_protocol_startup/r1/zebra.conf create mode 100644 tests/topotests/all_protocol_startup/test_all_protocol_startup.dot create mode 100644 tests/topotests/all_protocol_startup/test_all_protocol_startup.pdf create mode 100644 tests/topotests/all_protocol_startup/test_all_protocol_startup.py create mode 100755 tests/topotests/analyze.py create mode 100644 tests/topotests/babel_topo1/r1/babeld.conf create mode 100644 tests/topotests/babel_topo1/r1/show_ip_route.json_ref create mode 100644 tests/topotests/babel_topo1/r1/zebra.conf create mode 100644 tests/topotests/babel_topo1/r2/babeld.conf create mode 100644 tests/topotests/babel_topo1/r2/show_ip_route.json_ref create mode 100644 tests/topotests/babel_topo1/r2/zebra.conf create mode 100644 tests/topotests/babel_topo1/r3/babeld.conf create mode 100644 tests/topotests/babel_topo1/r3/show_ip_route.json_ref create mode 100644 tests/topotests/babel_topo1/r3/zebra.conf create mode 100644 tests/topotests/babel_topo1/test_babel_topo1.py create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/__init__.py create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/r1/bfdd.conf create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/r1/bgp_ipv6_routes_down.json create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/r1/bgpd.conf create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/r1/ipv6_routes.json create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/r1/peers.json create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/r1/peers_down.json create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/r1/zebra.conf create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/r2/zebra.conf create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/r3/bfdd.conf create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/r3/bgp_ipv6_routes_down.json create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/r3/bgpd.conf create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/r3/ipv6_routes.json create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/r3/peers.json create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/r3/peers_down.json create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/r3/zebra.conf create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.dot create mode 100644 tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.py create mode 100644 tests/topotests/bfd_isis_topo1/__init__.py create mode 100644 tests/topotests/bfd_isis_topo1/rt1/bfdd.conf create mode 100644 tests/topotests/bfd_isis_topo1/rt1/isisd.conf create mode 100644 tests/topotests/bfd_isis_topo1/rt1/step1/show_ip_route.ref create mode 100644 tests/topotests/bfd_isis_topo1/rt1/step1/show_ipv6_route.ref create mode 100644 tests/topotests/bfd_isis_topo1/rt1/step2/show_bfd_peers.ref create mode 100644 tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_healthy.ref create mode 100644 tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt2_down.ref create mode 100644 tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt3_down.ref create mode 100644 tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_healthy.ref create mode 100644 tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt2_down.ref create mode 100644 tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt3_down.ref create mode 100644 tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_healthy.ref create mode 100644 tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt2_down.ref create mode 100644 tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt3_down.ref create mode 100644 tests/topotests/bfd_isis_topo1/rt1/zebra.conf create mode 100644 tests/topotests/bfd_isis_topo1/rt2/bfdd.conf create mode 100644 tests/topotests/bfd_isis_topo1/rt2/isisd.conf create mode 100644 tests/topotests/bfd_isis_topo1/rt2/step2/show_bfd_peers.ref create mode 100644 tests/topotests/bfd_isis_topo1/rt2/zebra.conf create mode 100644 tests/topotests/bfd_isis_topo1/rt3/bfdd.conf create mode 100644 tests/topotests/bfd_isis_topo1/rt3/isisd.conf create mode 100644 tests/topotests/bfd_isis_topo1/rt3/step2/show_bfd_peers.ref create mode 100644 tests/topotests/bfd_isis_topo1/rt3/zebra.conf create mode 100644 tests/topotests/bfd_isis_topo1/rt4/bfdd.conf create mode 100644 tests/topotests/bfd_isis_topo1/rt4/isisd.conf create mode 100644 tests/topotests/bfd_isis_topo1/rt4/zebra.conf create mode 100644 tests/topotests/bfd_isis_topo1/rt5/bfdd.conf create mode 100644 tests/topotests/bfd_isis_topo1/rt5/isisd.conf create mode 100644 tests/topotests/bfd_isis_topo1/rt5/zebra.conf create mode 100644 tests/topotests/bfd_isis_topo1/test_bfd_isis_topo1.py create mode 100644 tests/topotests/bfd_ospf_topo1/__init__.py create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/bfdd.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/ospfd.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/step1/show_ip_route.ref create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/step1/show_ipv6_route.ref create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/step2/show_bfd_peers.ref create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_healthy.ref create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt2_down.ref create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt3_down.ref create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_healthy.ref create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt2_down.ref create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt3_down.ref create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_healthy.ref create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt2_down.ref create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt3_down.ref create mode 100644 tests/topotests/bfd_ospf_topo1/rt1/zebra.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt2/bfdd.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt2/ospfd.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt2/step2/show_bfd_peers.ref create mode 100644 tests/topotests/bfd_ospf_topo1/rt2/zebra.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt3/bfdd.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt3/ospfd.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt3/step2/show_bfd_peers.ref create mode 100644 tests/topotests/bfd_ospf_topo1/rt3/zebra.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt4/bfdd.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt4/ospfd.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt4/zebra.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt5/bfdd.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt5/ospfd.conf create mode 100644 tests/topotests/bfd_ospf_topo1/rt5/zebra.conf create mode 100755 tests/topotests/bfd_ospf_topo1/test_bfd_ospf_topo1.py create mode 100644 tests/topotests/bfd_profiles_topo1/__init__.py create mode 100644 tests/topotests/bfd_profiles_topo1/r1/bfd-peers-initial.json create mode 100644 tests/topotests/bfd_profiles_topo1/r1/bfdd.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r1/ospfd.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r1/zebra.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r2/bfd-peers-initial.json create mode 100644 tests/topotests/bfd_profiles_topo1/r2/bfdd.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r2/bgpd.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r2/zebra.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r3/bfd-peers-initial.json create mode 100644 tests/topotests/bfd_profiles_topo1/r3/bfdd.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r3/bgpd.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r3/isisd.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r3/zebra.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r4/bfd-peers-initial.json create mode 100644 tests/topotests/bfd_profiles_topo1/r4/bfdd.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r4/bgpd.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r4/isisd.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r4/zebra.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r5/bfd-peers-initial.json create mode 100644 tests/topotests/bfd_profiles_topo1/r5/bfdd.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r5/zebra.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r6/bfd-peers-initial.json create mode 100644 tests/topotests/bfd_profiles_topo1/r6/bfdd.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r6/ospfd.conf create mode 100644 tests/topotests/bfd_profiles_topo1/r6/zebra.conf create mode 100644 tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.dot create mode 100644 tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.png create mode 100644 tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.py create mode 100644 tests/topotests/bfd_topo1/__init__.py create mode 100644 tests/topotests/bfd_topo1/r1/bfdd.conf create mode 100644 tests/topotests/bfd_topo1/r1/bgp_prefixes.json create mode 100644 tests/topotests/bfd_topo1/r1/bgp_summary.json create mode 100644 tests/topotests/bfd_topo1/r1/bgpd.conf create mode 100644 tests/topotests/bfd_topo1/r1/peers.json create mode 100644 tests/topotests/bfd_topo1/r1/zebra.conf create mode 100644 tests/topotests/bfd_topo1/r2/bfdd.conf create mode 100644 tests/topotests/bfd_topo1/r2/bgp_prefixes.json create mode 100644 tests/topotests/bfd_topo1/r2/bgp_summary.json create mode 100644 tests/topotests/bfd_topo1/r2/bgpd.conf create mode 100644 tests/topotests/bfd_topo1/r2/peers.json create mode 100644 tests/topotests/bfd_topo1/r2/zebra.conf create mode 100644 tests/topotests/bfd_topo1/r3/bfdd.conf create mode 100644 tests/topotests/bfd_topo1/r3/bgp_prefixes.json create mode 100644 tests/topotests/bfd_topo1/r3/bgp_summary.json create mode 100644 tests/topotests/bfd_topo1/r3/bgpd.conf create mode 100644 tests/topotests/bfd_topo1/r3/peers.json create mode 100644 tests/topotests/bfd_topo1/r3/zebra.conf create mode 100644 tests/topotests/bfd_topo1/r4/bfdd.conf create mode 100644 tests/topotests/bfd_topo1/r4/bgp_prefixes.json create mode 100644 tests/topotests/bfd_topo1/r4/bgp_summary.json create mode 100644 tests/topotests/bfd_topo1/r4/bgpd.conf create mode 100644 tests/topotests/bfd_topo1/r4/peers.json create mode 100644 tests/topotests/bfd_topo1/r4/zebra.conf create mode 100644 tests/topotests/bfd_topo1/test_bfd_topo1.dot create mode 100644 tests/topotests/bfd_topo1/test_bfd_topo1.jpg create mode 100644 tests/topotests/bfd_topo1/test_bfd_topo1.py create mode 100644 tests/topotests/bfd_topo2/__init__.py create mode 100644 tests/topotests/bfd_topo2/r1/bfdd.conf create mode 100644 tests/topotests/bfd_topo2/r1/bgpd.conf create mode 100644 tests/topotests/bfd_topo2/r1/ipv4_routes.json create mode 100644 tests/topotests/bfd_topo2/r1/ipv6_routes.json create mode 100644 tests/topotests/bfd_topo2/r1/peers.json create mode 100644 tests/topotests/bfd_topo2/r1/zebra.conf create mode 100644 tests/topotests/bfd_topo2/r2/bfdd.conf create mode 100644 tests/topotests/bfd_topo2/r2/bgpd.conf create mode 100644 tests/topotests/bfd_topo2/r2/ipv4_routes.json create mode 100644 tests/topotests/bfd_topo2/r2/ipv6_routes.json create mode 100644 tests/topotests/bfd_topo2/r2/ospf6d.conf create mode 100644 tests/topotests/bfd_topo2/r2/ospfd.conf create mode 100644 tests/topotests/bfd_topo2/r2/peers.json create mode 100644 tests/topotests/bfd_topo2/r2/zebra.conf create mode 100644 tests/topotests/bfd_topo2/r3/bfdd.conf create mode 100644 tests/topotests/bfd_topo2/r3/ipv4_routes.json create mode 100644 tests/topotests/bfd_topo2/r3/ipv6_routes.json create mode 100644 tests/topotests/bfd_topo2/r3/ospfd.conf create mode 100644 tests/topotests/bfd_topo2/r3/peers.json create mode 100644 tests/topotests/bfd_topo2/r3/zebra.conf create mode 100644 tests/topotests/bfd_topo2/r4/bfdd.conf create mode 100644 tests/topotests/bfd_topo2/r4/ipv4_routes.json create mode 100644 tests/topotests/bfd_topo2/r4/ipv6_routes.json create mode 100644 tests/topotests/bfd_topo2/r4/ospf6d.conf create mode 100644 tests/topotests/bfd_topo2/r4/peers.json create mode 100644 tests/topotests/bfd_topo2/r4/zebra.conf create mode 100644 tests/topotests/bfd_topo2/test_bfd_topo2.dot create mode 100644 tests/topotests/bfd_topo2/test_bfd_topo2.jpg create mode 100644 tests/topotests/bfd_topo2/test_bfd_topo2.py create mode 100644 tests/topotests/bfd_topo3/__init__.py create mode 100644 tests/topotests/bfd_topo3/r1/bfd-peers.json create mode 100644 tests/topotests/bfd_topo3/r1/bfdd.conf create mode 100644 tests/topotests/bfd_topo3/r1/bgpd.conf create mode 100644 tests/topotests/bfd_topo3/r1/zebra.conf create mode 100644 tests/topotests/bfd_topo3/r2/bfd-peers.json create mode 100644 tests/topotests/bfd_topo3/r2/bfdd.conf create mode 100644 tests/topotests/bfd_topo3/r2/bgpd.conf create mode 100644 tests/topotests/bfd_topo3/r2/zebra.conf create mode 100644 tests/topotests/bfd_topo3/r3/bfd-peers.json create mode 100644 tests/topotests/bfd_topo3/r3/bfd-static-down.json create mode 100644 tests/topotests/bfd_topo3/r3/bfd-static.json create mode 100644 tests/topotests/bfd_topo3/r3/bfdd.conf create mode 100644 tests/topotests/bfd_topo3/r3/bgpd.conf create mode 100644 tests/topotests/bfd_topo3/r3/staticd.conf create mode 100644 tests/topotests/bfd_topo3/r3/zebra.conf create mode 100644 tests/topotests/bfd_topo3/r4/bfd-peers.json create mode 100644 tests/topotests/bfd_topo3/r4/bfdd.conf create mode 100644 tests/topotests/bfd_topo3/r4/bgpd.conf create mode 100644 tests/topotests/bfd_topo3/r4/staticd.conf create mode 100644 tests/topotests/bfd_topo3/r4/zebra.conf create mode 100644 tests/topotests/bfd_topo3/r5/bfd-peers.json create mode 100644 tests/topotests/bfd_topo3/r5/bfdd.conf create mode 100644 tests/topotests/bfd_topo3/r5/staticd.conf create mode 100644 tests/topotests/bfd_topo3/r5/zebra.conf create mode 100644 tests/topotests/bfd_topo3/r6/bfd-peers.json create mode 100644 tests/topotests/bfd_topo3/r6/bfd-static-down.json create mode 100644 tests/topotests/bfd_topo3/r6/bfd-static.json create mode 100644 tests/topotests/bfd_topo3/r6/bfdd.conf create mode 100644 tests/topotests/bfd_topo3/r6/staticd.conf create mode 100644 tests/topotests/bfd_topo3/r6/zebra.conf create mode 100644 tests/topotests/bfd_topo3/test_bfd_topo3.dot create mode 100644 tests/topotests/bfd_topo3/test_bfd_topo3.jpg create mode 100644 tests/topotests/bfd_topo3/test_bfd_topo3.py create mode 100644 tests/topotests/bfd_vrf_topo1/__init__.py create mode 100644 tests/topotests/bfd_vrf_topo1/r1/bfdd.conf create mode 100644 tests/topotests/bfd_vrf_topo1/r1/bgp_prefixes.json create mode 100644 tests/topotests/bfd_vrf_topo1/r1/bgp_summary.json create mode 100644 tests/topotests/bfd_vrf_topo1/r1/bgpd.conf create mode 100644 tests/topotests/bfd_vrf_topo1/r1/peers.json create mode 100644 tests/topotests/bfd_vrf_topo1/r1/zebra.conf create mode 100644 tests/topotests/bfd_vrf_topo1/r2/bfdd.conf create mode 100644 tests/topotests/bfd_vrf_topo1/r2/bgp_prefixes.json create mode 100644 tests/topotests/bfd_vrf_topo1/r2/bgp_summary.json create mode 100644 tests/topotests/bfd_vrf_topo1/r2/bgpd.conf create mode 100644 tests/topotests/bfd_vrf_topo1/r2/peers.json create mode 100644 tests/topotests/bfd_vrf_topo1/r2/zebra.conf create mode 100644 tests/topotests/bfd_vrf_topo1/r3/bfdd.conf create mode 100644 tests/topotests/bfd_vrf_topo1/r3/bgp_prefixes.json create mode 100644 tests/topotests/bfd_vrf_topo1/r3/bgp_summary.json create mode 100644 tests/topotests/bfd_vrf_topo1/r3/bgpd.conf create mode 100644 tests/topotests/bfd_vrf_topo1/r3/peers.json create mode 100644 tests/topotests/bfd_vrf_topo1/r3/zebra.conf create mode 100644 tests/topotests/bfd_vrf_topo1/r4/bfdd.conf create mode 100644 tests/topotests/bfd_vrf_topo1/r4/bgp_prefixes.json create mode 100644 tests/topotests/bfd_vrf_topo1/r4/bgp_summary.json create mode 100644 tests/topotests/bfd_vrf_topo1/r4/bgpd.conf create mode 100644 tests/topotests/bfd_vrf_topo1/r4/peers.json create mode 100644 tests/topotests/bfd_vrf_topo1/r4/zebra.conf create mode 100644 tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.dot create mode 100644 tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.jpg create mode 100644 tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py create mode 100644 tests/topotests/bfd_vrflite_topo1/__init__.py create mode 100644 tests/topotests/bfd_vrflite_topo1/r1/bfd_peers_status.json create mode 100644 tests/topotests/bfd_vrflite_topo1/r1/bfdd.conf create mode 100644 tests/topotests/bfd_vrflite_topo1/r1/zebra.conf create mode 100644 tests/topotests/bfd_vrflite_topo1/r2/bfdd.conf create mode 100644 tests/topotests/bfd_vrflite_topo1/r2/zebra.conf create mode 100644 tests/topotests/bfd_vrflite_topo1/test_bfd_vrflite_topo1.py create mode 100644 tests/topotests/bgp_accept_own/__init__.py create mode 100644 tests/topotests/bgp_accept_own/ce1/bgpd.conf create mode 100644 tests/topotests/bgp_accept_own/ce1/zebra.conf create mode 100644 tests/topotests/bgp_accept_own/ce2/bgpd.conf create mode 100644 tests/topotests/bgp_accept_own/ce2/zebra.conf create mode 100644 tests/topotests/bgp_accept_own/pe1/bgpd.conf create mode 100644 tests/topotests/bgp_accept_own/pe1/ldpd.conf create mode 100644 tests/topotests/bgp_accept_own/pe1/ospfd.conf create mode 100644 tests/topotests/bgp_accept_own/pe1/zebra.conf create mode 100644 tests/topotests/bgp_accept_own/rr1/bgpd.conf create mode 100644 tests/topotests/bgp_accept_own/rr1/ldpd.conf create mode 100644 tests/topotests/bgp_accept_own/rr1/ospfd.conf create mode 100644 tests/topotests/bgp_accept_own/rr1/zebra.conf create mode 100644 tests/topotests/bgp_accept_own/test_bgp_accept_own.py create mode 100644 tests/topotests/bgp_addpath_best_selected/__init__.py create mode 100644 tests/topotests/bgp_addpath_best_selected/r1/bgpd.conf create mode 100644 tests/topotests/bgp_addpath_best_selected/r1/zebra.conf create mode 100644 tests/topotests/bgp_addpath_best_selected/r2/bgpd.conf create mode 100644 tests/topotests/bgp_addpath_best_selected/r2/zebra.conf create mode 100644 tests/topotests/bgp_addpath_best_selected/r3/bgpd.conf create mode 100644 tests/topotests/bgp_addpath_best_selected/r3/zebra.conf create mode 100644 tests/topotests/bgp_addpath_best_selected/r4/bgpd.conf create mode 100644 tests/topotests/bgp_addpath_best_selected/r4/zebra.conf create mode 100644 tests/topotests/bgp_addpath_best_selected/r5/bgpd.conf create mode 100644 tests/topotests/bgp_addpath_best_selected/r5/zebra.conf create mode 100644 tests/topotests/bgp_addpath_best_selected/r6/bgpd.conf create mode 100644 tests/topotests/bgp_addpath_best_selected/r6/zebra.conf create mode 100644 tests/topotests/bgp_addpath_best_selected/r7/bgpd.conf create mode 100644 tests/topotests/bgp_addpath_best_selected/r7/zebra.conf create mode 100644 tests/topotests/bgp_addpath_best_selected/test_bgp_addpath_best_selected.py create mode 100644 tests/topotests/bgp_aggregate_address_matching_med/__init__.py create mode 100644 tests/topotests/bgp_aggregate_address_matching_med/r1/bgpd.conf create mode 100644 tests/topotests/bgp_aggregate_address_matching_med/r1/zebra.conf create mode 100644 tests/topotests/bgp_aggregate_address_matching_med/r2/bgpd.conf create mode 100644 tests/topotests/bgp_aggregate_address_matching_med/r2/zebra.conf create mode 100644 tests/topotests/bgp_aggregate_address_matching_med/r3/bgpd.conf create mode 100644 tests/topotests/bgp_aggregate_address_matching_med/r3/zebra.conf create mode 100644 tests/topotests/bgp_aggregate_address_matching_med/test_bgp_aggregate_address_matching_med.py create mode 100644 tests/topotests/bgp_aggregate_address_origin/__init__.py create mode 100644 tests/topotests/bgp_aggregate_address_origin/r1/bgpd.conf create mode 100644 tests/topotests/bgp_aggregate_address_origin/r1/zebra.conf create mode 100644 tests/topotests/bgp_aggregate_address_origin/r2/bgpd.conf create mode 100644 tests/topotests/bgp_aggregate_address_origin/r2/zebra.conf create mode 100644 tests/topotests/bgp_aggregate_address_origin/test_bgp_aggregate-address_origin.py create mode 100644 tests/topotests/bgp_aggregate_address_route_map/__init__.py create mode 100644 tests/topotests/bgp_aggregate_address_route_map/r1/bgpd.conf create mode 100644 tests/topotests/bgp_aggregate_address_route_map/r1/zebra.conf create mode 100644 tests/topotests/bgp_aggregate_address_route_map/r2/bgpd.conf create mode 100644 tests/topotests/bgp_aggregate_address_route_map/r2/zebra.conf create mode 100644 tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py create mode 100644 tests/topotests/bgp_aggregate_address_topo1/__init__.py create mode 100644 tests/topotests/bgp_aggregate_address_topo1/exabgp.env create mode 100644 tests/topotests/bgp_aggregate_address_topo1/peer1/exabgp.cfg create mode 100644 tests/topotests/bgp_aggregate_address_topo1/r1/bgpd.conf create mode 100644 tests/topotests/bgp_aggregate_address_topo1/r1/zebra.conf create mode 100644 tests/topotests/bgp_aggregate_address_topo1/r2/bgpd.conf create mode 100644 tests/topotests/bgp_aggregate_address_topo1/r2/zebra.conf create mode 100644 tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py create mode 100644 tests/topotests/bgp_aggregator_zero/__init__.py create mode 100644 tests/topotests/bgp_aggregator_zero/exabgp.env create mode 100644 tests/topotests/bgp_aggregator_zero/peer1/exabgp.cfg create mode 100644 tests/topotests/bgp_aggregator_zero/r1/bgpd.conf create mode 100644 tests/topotests/bgp_aggregator_zero/r1/zebra.conf create mode 100644 tests/topotests/bgp_aggregator_zero/test_bgp_aggregator_zero.py create mode 100644 tests/topotests/bgp_aigp/__init__.py create mode 100644 tests/topotests/bgp_aigp/r1/bgpd.conf create mode 100644 tests/topotests/bgp_aigp/r1/ospfd.conf create mode 100644 tests/topotests/bgp_aigp/r1/zebra.conf create mode 100644 tests/topotests/bgp_aigp/r2/bgpd.conf create mode 100644 tests/topotests/bgp_aigp/r2/ospfd.conf create mode 100644 tests/topotests/bgp_aigp/r2/zebra.conf create mode 100644 tests/topotests/bgp_aigp/r3/bgpd.conf create mode 100644 tests/topotests/bgp_aigp/r3/ospfd.conf create mode 100644 tests/topotests/bgp_aigp/r3/zebra.conf create mode 100644 tests/topotests/bgp_aigp/r4/bgpd.conf create mode 100644 tests/topotests/bgp_aigp/r4/ospfd.conf create mode 100644 tests/topotests/bgp_aigp/r4/zebra.conf create mode 100644 tests/topotests/bgp_aigp/r5/bgpd.conf create mode 100644 tests/topotests/bgp_aigp/r5/ospfd.conf create mode 100644 tests/topotests/bgp_aigp/r5/zebra.conf create mode 100644 tests/topotests/bgp_aigp/r6/bgpd.conf create mode 100644 tests/topotests/bgp_aigp/r6/ospfd.conf create mode 100644 tests/topotests/bgp_aigp/r6/zebra.conf create mode 100644 tests/topotests/bgp_aigp/r7/bgpd.conf create mode 100644 tests/topotests/bgp_aigp/r7/zebra.conf create mode 100644 tests/topotests/bgp_aigp/test_bgp_aigp.py create mode 100644 tests/topotests/bgp_always_compare_med/bgp_always_compare_med_topo1.json create mode 100644 tests/topotests/bgp_always_compare_med/test_bgp_always_compare_med_topo1.py create mode 100644 tests/topotests/bgp_as_allow_in/bgp_as_allow_in.json create mode 100644 tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py create mode 100644 tests/topotests/bgp_as_override/__init__.py create mode 100644 tests/topotests/bgp_as_override/r1/bgpd.conf create mode 100644 tests/topotests/bgp_as_override/r1/zebra.conf create mode 100644 tests/topotests/bgp_as_override/r2/bgpd.conf create mode 100644 tests/topotests/bgp_as_override/r2/zebra.conf create mode 100644 tests/topotests/bgp_as_override/r3/bgpd.conf create mode 100644 tests/topotests/bgp_as_override/r3/zebra.conf create mode 100644 tests/topotests/bgp_as_override/r4/bgpd.conf create mode 100644 tests/topotests/bgp_as_override/r4/zebra.conf create mode 100644 tests/topotests/bgp_as_override/test_bgp_as_override.py create mode 100644 tests/topotests/bgp_as_wide_bgp_identifier/__init__.py create mode 100644 tests/topotests/bgp_as_wide_bgp_identifier/r1/bgpd.conf create mode 100644 tests/topotests/bgp_as_wide_bgp_identifier/r1/zebra.conf create mode 100644 tests/topotests/bgp_as_wide_bgp_identifier/r2/bgpd.conf create mode 100644 tests/topotests/bgp_as_wide_bgp_identifier/r2/zebra.conf create mode 100644 tests/topotests/bgp_as_wide_bgp_identifier/r3/bgpd.conf create mode 100644 tests/topotests/bgp_as_wide_bgp_identifier/r3/zebra.conf create mode 100644 tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py create mode 100644 tests/topotests/bgp_asdot_regex/__init__.py create mode 100644 tests/topotests/bgp_asdot_regex/r1/bgpd.conf create mode 100644 tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json create mode 100644 tests/topotests/bgp_asdot_regex/r1/zebra.conf create mode 100644 tests/topotests/bgp_asdot_regex/r2/bgpd.conf create mode 100644 tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json create mode 100644 tests/topotests/bgp_asdot_regex/r2/zebra.conf create mode 100644 tests/topotests/bgp_asdot_regex/test_bgp_asdot_regex.py create mode 100644 tests/topotests/bgp_aspath_zero/__init__.py create mode 100644 tests/topotests/bgp_aspath_zero/exabgp.env create mode 100644 tests/topotests/bgp_aspath_zero/peer1/exabgp.cfg create mode 100644 tests/topotests/bgp_aspath_zero/r1/bgpd.conf create mode 100644 tests/topotests/bgp_aspath_zero/r1/zebra.conf create mode 100644 tests/topotests/bgp_aspath_zero/test_bgp_aspath_zero.py create mode 100644 tests/topotests/bgp_auth/R1/bgpd.conf create mode 100644 tests/topotests/bgp_auth/R1/bgpd_multi_vrf.conf create mode 100644 tests/topotests/bgp_auth/R1/bgpd_multi_vrf_prefix.conf create mode 100644 tests/topotests/bgp_auth/R1/bgpd_prefix.conf create mode 100644 tests/topotests/bgp_auth/R1/bgpd_vrf.conf create mode 100644 tests/topotests/bgp_auth/R1/bgpd_vrf_prefix.conf create mode 100644 tests/topotests/bgp_auth/R1/empty.conf create mode 100644 tests/topotests/bgp_auth/R1/ospfd.conf create mode 100644 tests/topotests/bgp_auth/R1/ospfd_multi_vrf.conf create mode 100644 tests/topotests/bgp_auth/R1/ospfd_vrf.conf create mode 100644 tests/topotests/bgp_auth/R1/zebra.conf create mode 100644 tests/topotests/bgp_auth/R2/bgpd.conf create mode 100644 tests/topotests/bgp_auth/R2/bgpd_multi_vrf.conf create mode 100644 tests/topotests/bgp_auth/R2/bgpd_multi_vrf_prefix.conf create mode 100644 tests/topotests/bgp_auth/R2/bgpd_prefix.conf create mode 100644 tests/topotests/bgp_auth/R2/bgpd_vrf.conf create mode 100644 tests/topotests/bgp_auth/R2/bgpd_vrf_prefix.conf create mode 100644 tests/topotests/bgp_auth/R2/empty.conf create mode 100644 tests/topotests/bgp_auth/R2/ospfd.conf create mode 100644 tests/topotests/bgp_auth/R2/ospfd_multi_vrf.conf create mode 100644 tests/topotests/bgp_auth/R2/ospfd_vrf.conf create mode 100644 tests/topotests/bgp_auth/R2/zebra.conf create mode 100644 tests/topotests/bgp_auth/R3/bgpd.conf create mode 100644 tests/topotests/bgp_auth/R3/bgpd_multi_vrf.conf create mode 100644 tests/topotests/bgp_auth/R3/bgpd_multi_vrf_prefix.conf create mode 100644 tests/topotests/bgp_auth/R3/bgpd_prefix.conf create mode 100644 tests/topotests/bgp_auth/R3/bgpd_vrf.conf create mode 100644 tests/topotests/bgp_auth/R3/bgpd_vrf_prefix.conf create mode 100644 tests/topotests/bgp_auth/R3/empty.conf create mode 100644 tests/topotests/bgp_auth/R3/ospfd.conf create mode 100644 tests/topotests/bgp_auth/R3/ospfd_multi_vrf.conf create mode 100644 tests/topotests/bgp_auth/R3/ospfd_vrf.conf create mode 100644 tests/topotests/bgp_auth/R3/zebra.conf create mode 100644 tests/topotests/bgp_auth/bgp_auth_common.py create mode 100644 tests/topotests/bgp_auth/test_bgp_auth1.py create mode 100644 tests/topotests/bgp_auth/test_bgp_auth2.py create mode 100644 tests/topotests/bgp_auth/test_bgp_auth3.py create mode 100644 tests/topotests/bgp_auth/test_bgp_auth4.py create mode 100644 tests/topotests/bgp_basic_functionality_topo1/__init__.py create mode 100644 tests/topotests/bgp_basic_functionality_topo1/bgp_basic_functionality.json create mode 100644 tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py create mode 100644 tests/topotests/bgp_bfd_down_cease_notification/__init__.py create mode 100644 tests/topotests/bgp_bfd_down_cease_notification/r1/bfdd.conf create mode 100644 tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf create mode 100644 tests/topotests/bgp_bfd_down_cease_notification/r1/zebra.conf create mode 100644 tests/topotests/bgp_bfd_down_cease_notification/r2/bfdd.conf create mode 100644 tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf create mode 100644 tests/topotests/bgp_bfd_down_cease_notification/r2/zebra.conf create mode 100644 tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py create mode 100644 tests/topotests/bgp_blackhole_community/__init__.py create mode 100644 tests/topotests/bgp_blackhole_community/r1/bgpd.conf create mode 100644 tests/topotests/bgp_blackhole_community/r1/zebra.conf create mode 100644 tests/topotests/bgp_blackhole_community/r2/bgpd.conf create mode 100644 tests/topotests/bgp_blackhole_community/r2/zebra.conf create mode 100644 tests/topotests/bgp_blackhole_community/r3/bgpd.conf create mode 100644 tests/topotests/bgp_blackhole_community/r3/zebra.conf create mode 100644 tests/topotests/bgp_blackhole_community/r4/bgpd.conf create mode 100644 tests/topotests/bgp_blackhole_community/r4/zebra.conf create mode 100644 tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py create mode 100644 tests/topotests/bgp_bmp/__init__.py create mode 100644 tests/topotests/bgp_bmp/r1/bgpd.conf create mode 100644 tests/topotests/bgp_bmp/r1/zebra.conf create mode 100644 tests/topotests/bgp_bmp/r2/bgpd.conf create mode 100644 tests/topotests/bgp_bmp/r2/zebra.conf create mode 100644 tests/topotests/bgp_bmp/test_bgp_bmp.py create mode 100644 tests/topotests/bgp_color_extcommunities/__init__.py create mode 100644 tests/topotests/bgp_color_extcommunities/r1/bgpd.conf create mode 100644 tests/topotests/bgp_color_extcommunities/r1/zebra.conf create mode 100644 tests/topotests/bgp_color_extcommunities/r2/bgpd.conf create mode 100644 tests/topotests/bgp_color_extcommunities/r2/zebra.conf create mode 100644 tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py create mode 100644 tests/topotests/bgp_comm_list_delete/__init__.py create mode 100644 tests/topotests/bgp_comm_list_delete/r1/bgpd.conf create mode 100644 tests/topotests/bgp_comm_list_delete/r1/zebra.conf create mode 100644 tests/topotests/bgp_comm_list_delete/r2/bgpd.conf create mode 100644 tests/topotests/bgp_comm_list_delete/r2/zebra.conf create mode 100644 tests/topotests/bgp_comm_list_delete/test_bgp_comm-list_delete.py create mode 100644 tests/topotests/bgp_comm_list_match/__init__.py create mode 100644 tests/topotests/bgp_comm_list_match/r1/bgpd.conf create mode 100644 tests/topotests/bgp_comm_list_match/r1/zebra.conf create mode 100644 tests/topotests/bgp_comm_list_match/r2/bgpd.conf create mode 100644 tests/topotests/bgp_comm_list_match/r2/zebra.conf create mode 100644 tests/topotests/bgp_comm_list_match/r3/bgpd.conf create mode 100644 tests/topotests/bgp_comm_list_match/r3/zebra.conf create mode 100644 tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py create mode 100644 tests/topotests/bgp_communities_topo1/bgp_communities.json create mode 100644 tests/topotests/bgp_communities_topo1/bgp_communities_topo2.json create mode 100644 tests/topotests/bgp_communities_topo1/test_bgp_communities.py create mode 100644 tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py create mode 100644 tests/topotests/bgp_community_alias/__init__.py create mode 100644 tests/topotests/bgp_community_alias/r1/bgpd.conf create mode 100644 tests/topotests/bgp_community_alias/r1/zebra.conf create mode 100644 tests/topotests/bgp_community_alias/r2/bgpd.conf create mode 100644 tests/topotests/bgp_community_alias/r2/zebra.conf create mode 100644 tests/topotests/bgp_community_alias/test_bgp-community-alias.py create mode 100644 tests/topotests/bgp_community_change_update/__init__.py create mode 100644 tests/topotests/bgp_community_change_update/c1/bgpd.conf create mode 100644 tests/topotests/bgp_community_change_update/c1/zebra.conf create mode 100644 tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py create mode 100644 tests/topotests/bgp_community_change_update/x1/bgpd.conf create mode 100644 tests/topotests/bgp_community_change_update/x1/zebra.conf create mode 100644 tests/topotests/bgp_community_change_update/y1/bgpd.conf create mode 100644 tests/topotests/bgp_community_change_update/y1/zebra.conf create mode 100644 tests/topotests/bgp_community_change_update/y2/bgpd.conf create mode 100644 tests/topotests/bgp_community_change_update/y2/zebra.conf create mode 100644 tests/topotests/bgp_community_change_update/y3/bgpd.conf create mode 100644 tests/topotests/bgp_community_change_update/y3/zebra.conf create mode 100644 tests/topotests/bgp_community_change_update/z1/bgpd.conf create mode 100644 tests/topotests/bgp_community_change_update/z1/zebra.conf create mode 100644 tests/topotests/bgp_conditional_advertisement/__init__.py create mode 100644 tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf create mode 100644 tests/topotests/bgp_conditional_advertisement/r1/zebra.conf create mode 100644 tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf create mode 100644 tests/topotests/bgp_conditional_advertisement/r2/zebra.conf create mode 100644 tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf create mode 100644 tests/topotests/bgp_conditional_advertisement/r3/zebra.conf create mode 100644 tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py create mode 100644 tests/topotests/bgp_conditional_advertisement_static_route/__init__.py create mode 100644 tests/topotests/bgp_conditional_advertisement_static_route/r1/frr.conf create mode 100644 tests/topotests/bgp_conditional_advertisement_static_route/r2/frr.conf create mode 100644 tests/topotests/bgp_conditional_advertisement_static_route/r3/frr.conf create mode 100644 tests/topotests/bgp_conditional_advertisement_static_route/test_bgp_conditional_advertisement_static_route.py create mode 100644 tests/topotests/bgp_conditional_advertisement_track_peer/__init__.py create mode 100644 tests/topotests/bgp_conditional_advertisement_track_peer/r1/bgpd.conf create mode 100644 tests/topotests/bgp_conditional_advertisement_track_peer/r1/zebra.conf create mode 100644 tests/topotests/bgp_conditional_advertisement_track_peer/r2/bgpd.conf create mode 100644 tests/topotests/bgp_conditional_advertisement_track_peer/r2/staticd.conf create mode 100644 tests/topotests/bgp_conditional_advertisement_track_peer/r2/zebra.conf create mode 100644 tests/topotests/bgp_conditional_advertisement_track_peer/r3/bgpd.conf create mode 100644 tests/topotests/bgp_conditional_advertisement_track_peer/r3/zebra.conf create mode 100644 tests/topotests/bgp_conditional_advertisement_track_peer/test_bgp_conditional_advertisement_track_peer.py create mode 100644 tests/topotests/bgp_confed1/__init__.py create mode 100644 tests/topotests/bgp_confed1/r1/bgp_ipv4_unicast.json create mode 100644 tests/topotests/bgp_confed1/r1/bgp_summary.json create mode 100644 tests/topotests/bgp_confed1/r1/bgpd.conf create mode 100644 tests/topotests/bgp_confed1/r1/isisd.conf create mode 100644 tests/topotests/bgp_confed1/r1/zebra.conf create mode 100644 tests/topotests/bgp_confed1/r2/bgp_ipv4_unicast.json create mode 100644 tests/topotests/bgp_confed1/r2/bgp_summary.json create mode 100644 tests/topotests/bgp_confed1/r2/bgpd.conf create mode 100644 tests/topotests/bgp_confed1/r2/isisd.conf create mode 100644 tests/topotests/bgp_confed1/r2/zebra.conf create mode 100644 tests/topotests/bgp_confed1/r3/bgp_ipv4_unicast.json create mode 100644 tests/topotests/bgp_confed1/r3/bgp_summary.json create mode 100644 tests/topotests/bgp_confed1/r3/bgpd.conf create mode 100644 tests/topotests/bgp_confed1/r3/isisd.conf create mode 100644 tests/topotests/bgp_confed1/r3/zebra.conf create mode 100644 tests/topotests/bgp_confed1/r4/bgp_ipv4_unicast.json create mode 100644 tests/topotests/bgp_confed1/r4/bgp_summary.json create mode 100644 tests/topotests/bgp_confed1/r4/bgpd.conf create mode 100644 tests/topotests/bgp_confed1/r4/isisd.conf create mode 100644 tests/topotests/bgp_confed1/r4/zebra.conf create mode 100644 tests/topotests/bgp_confed1/test_bgp_confed1.py create mode 100644 tests/topotests/bgp_confederation_astype/__init__.py create mode 100644 tests/topotests/bgp_confederation_astype/r1/bgpd.conf create mode 100644 tests/topotests/bgp_confederation_astype/r1/zebra.conf create mode 100644 tests/topotests/bgp_confederation_astype/r2/bgpd.conf create mode 100644 tests/topotests/bgp_confederation_astype/r2/zebra.conf create mode 100644 tests/topotests/bgp_confederation_astype/r3/bgpd.conf create mode 100644 tests/topotests/bgp_confederation_astype/r3/zebra.conf create mode 100644 tests/topotests/bgp_confederation_astype/test_bgp_confederation_astype.py create mode 100644 tests/topotests/bgp_default_afi_safi/__init__.py create mode 100644 tests/topotests/bgp_default_afi_safi/r1/bgpd.conf create mode 100644 tests/topotests/bgp_default_afi_safi/r1/zebra.conf create mode 100644 tests/topotests/bgp_default_afi_safi/r2/bgpd.conf create mode 100644 tests/topotests/bgp_default_afi_safi/r2/zebra.conf create mode 100644 tests/topotests/bgp_default_afi_safi/r3/bgpd.conf create mode 100644 tests/topotests/bgp_default_afi_safi/r3/zebra.conf create mode 100644 tests/topotests/bgp_default_afi_safi/r4/bgpd.conf create mode 100644 tests/topotests/bgp_default_afi_safi/r4/zebra.conf create mode 100644 tests/topotests/bgp_default_afi_safi/test_bgp-default-afi-safi.py create mode 100644 tests/topotests/bgp_default_originate/bgp_default_orginate_vrf.json create mode 100644 tests/topotests/bgp_default_originate/bgp_default_originate_2links.json create mode 100644 tests/topotests/bgp_default_originate/bgp_default_originate_topo1.json create mode 100644 tests/topotests/bgp_default_originate/test_bgp_default_originate_2links.py create mode 100644 tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_1.py create mode 100644 tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_2.py create mode 100644 tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_3.py create mode 100644 tests/topotests/bgp_default_originate/test_default_orginate_vrf.py create mode 100644 tests/topotests/bgp_default_originate/test_default_originate_conditional_routemap.py create mode 100644 tests/topotests/bgp_default_originate_timer/__init__.py create mode 100644 tests/topotests/bgp_default_originate_timer/r1/bgpd.conf create mode 100644 tests/topotests/bgp_default_originate_timer/r1/zebra.conf create mode 100644 tests/topotests/bgp_default_originate_timer/r2/bgpd.conf create mode 100644 tests/topotests/bgp_default_originate_timer/r2/zebra.conf create mode 100644 tests/topotests/bgp_default_originate_timer/r3/bgpd.conf create mode 100644 tests/topotests/bgp_default_originate_timer/r3/zebra.conf create mode 100644 tests/topotests/bgp_default_originate_timer/test_bgp_default_originate_timer.py create mode 100644 tests/topotests/bgp_default_originate_withdraw/__init__.py create mode 100644 tests/topotests/bgp_default_originate_withdraw/r1/bgpd.conf create mode 100644 tests/topotests/bgp_default_originate_withdraw/r1/zebra.conf create mode 100644 tests/topotests/bgp_default_originate_withdraw/r2/bgpd.conf create mode 100644 tests/topotests/bgp_default_originate_withdraw/r2/zebra.conf create mode 100644 tests/topotests/bgp_default_originate_withdraw/r3/bgpd.conf create mode 100644 tests/topotests/bgp_default_originate_withdraw/r3/zebra.conf create mode 100644 tests/topotests/bgp_default_originate_withdraw/test_bgp_default_originate_withdraw.py create mode 100644 tests/topotests/bgp_default_route/__init__.py create mode 100644 tests/topotests/bgp_default_route/r1/bgpd.conf create mode 100644 tests/topotests/bgp_default_route/r1/zebra.conf create mode 100644 tests/topotests/bgp_default_route/r2/bgpd.conf create mode 100644 tests/topotests/bgp_default_route/r2/zebra.conf create mode 100644 tests/topotests/bgp_default_route/test_bgp_default-originate.py create mode 100644 tests/topotests/bgp_default_route_route_map_match/__init__.py create mode 100644 tests/topotests/bgp_default_route_route_map_match/r1/bgpd.conf create mode 100644 tests/topotests/bgp_default_route_route_map_match/r1/zebra.conf create mode 100644 tests/topotests/bgp_default_route_route_map_match/r2/bgpd.conf create mode 100644 tests/topotests/bgp_default_route_route_map_match/r2/zebra.conf create mode 100644 tests/topotests/bgp_default_route_route_map_match/test_bgp_default-originate_route-map_match.py create mode 100644 tests/topotests/bgp_default_route_route_map_match2/__init__.py create mode 100644 tests/topotests/bgp_default_route_route_map_match2/r1/bgpd.conf create mode 100644 tests/topotests/bgp_default_route_route_map_match2/r1/zebra.conf create mode 100644 tests/topotests/bgp_default_route_route_map_match2/r2/bgpd.conf create mode 100644 tests/topotests/bgp_default_route_route_map_match2/r2/zebra.conf create mode 100644 tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py create mode 100644 tests/topotests/bgp_default_route_route_map_match_set/__init__.py create mode 100644 tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf create mode 100644 tests/topotests/bgp_default_route_route_map_match_set/r1/zebra.conf create mode 100644 tests/topotests/bgp_default_route_route_map_match_set/r2/bgpd.conf create mode 100644 tests/topotests/bgp_default_route_route_map_match_set/r2/zebra.conf create mode 100644 tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py create mode 100644 tests/topotests/bgp_default_route_route_map_set/__init__.py create mode 100644 tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf create mode 100644 tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf create mode 100644 tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf create mode 100644 tests/topotests/bgp_default_route_route_map_set/r2/zebra.conf create mode 100644 tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py create mode 100644 tests/topotests/bgp_disable_addpath_rx/__init__.py create mode 100644 tests/topotests/bgp_disable_addpath_rx/r1/bgpd.conf create mode 100644 tests/topotests/bgp_disable_addpath_rx/r1/zebra.conf create mode 100644 tests/topotests/bgp_disable_addpath_rx/r2/bgpd.conf create mode 100644 tests/topotests/bgp_disable_addpath_rx/r2/zebra.conf create mode 100644 tests/topotests/bgp_disable_addpath_rx/r3/bgpd.conf create mode 100644 tests/topotests/bgp_disable_addpath_rx/r3/zebra.conf create mode 100644 tests/topotests/bgp_disable_addpath_rx/r4/bgpd.conf create mode 100644 tests/topotests/bgp_disable_addpath_rx/r4/zebra.conf create mode 100644 tests/topotests/bgp_disable_addpath_rx/test_disable_addpath_rx.py create mode 100644 tests/topotests/bgp_distance_change/__init__.py create mode 100755 tests/topotests/bgp_distance_change/bgp_admin_dist.json create mode 100755 tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json create mode 100644 tests/topotests/bgp_distance_change/r1/bgpd.conf create mode 100644 tests/topotests/bgp_distance_change/r1/zebra.conf create mode 100644 tests/topotests/bgp_distance_change/r2/bgpd.conf create mode 100644 tests/topotests/bgp_distance_change/r2/zebra.conf create mode 100755 tests/topotests/bgp_distance_change/test_bgp_admin_dist.py create mode 100755 tests/topotests/bgp_distance_change/test_bgp_admin_dist_vrf.py create mode 100644 tests/topotests/bgp_distance_change/test_bgp_distance_change.py create mode 100644 tests/topotests/bgp_dont_capability_negotiate/__init__.py create mode 100644 tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf create mode 100644 tests/topotests/bgp_dont_capability_negotiate/r1/zebra.conf create mode 100644 tests/topotests/bgp_dont_capability_negotiate/r2/bgpd.conf create mode 100644 tests/topotests/bgp_dont_capability_negotiate/r2/zebra.conf create mode 100644 tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py create mode 100644 tests/topotests/bgp_dynamic_capability/__init__.py create mode 100644 tests/topotests/bgp_dynamic_capability/r1/frr.conf create mode 100644 tests/topotests/bgp_dynamic_capability/r2/frr.conf create mode 100644 tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py create mode 100644 tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py create mode 100644 tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py create mode 100644 tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/__init__.py create mode 100644 tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/bgpd.conf create mode 100644 tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/zebra.conf create mode 100644 tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/bgpd.conf create mode 100644 tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/zebra.conf create mode 100644 tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/bgpd.conf create mode 100644 tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/zebra.conf create mode 100644 tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py create mode 100644 tests/topotests/bgp_ebgp_requires_policy/__init__.py create mode 100644 tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf create mode 100644 tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf create mode 100644 tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf create mode 100644 tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf create mode 100644 tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf create mode 100644 tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf create mode 100644 tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf create mode 100644 tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf create mode 100644 tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf create mode 100644 tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf create mode 100644 tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf create mode 100644 tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf create mode 100644 tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py create mode 100644 tests/topotests/bgp_ecmp_topo1/__init__.py create mode 100644 tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.dot create mode 100644 tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.pdf create mode 100644 tests/topotests/bgp_ecmp_topo1/exabgp.env create mode 100755 tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg create mode 100755 tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py create mode 100644 tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg create mode 100644 tests/topotests/bgp_ecmp_topo1/r1/bgpd.conf create mode 100644 tests/topotests/bgp_ecmp_topo1/r1/summary.txt create mode 100644 tests/topotests/bgp_ecmp_topo1/r1/summary20.txt create mode 100644 tests/topotests/bgp_ecmp_topo1/r1/zebra.conf create mode 100644 tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py create mode 100755 tests/topotests/bgp_ecmp_topo2/ebgp_ecmp_topo2.json create mode 100755 tests/topotests/bgp_ecmp_topo2/ibgp_ecmp_topo2.json create mode 100644 tests/topotests/bgp_ecmp_topo2/test_ebgp_ecmp_topo2.py create mode 100644 tests/topotests/bgp_ecmp_topo2/test_ibgp_ecmp_topo2.py create mode 100644 tests/topotests/bgp_ecmp_topo3/ibgp_ecmp_topo3.json create mode 100644 tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py create mode 100644 tests/topotests/bgp_evpn_mh/evpn-mh-topo-tests.pdf create mode 100644 tests/topotests/bgp_evpn_mh/hostd11/evpn.conf create mode 100644 tests/topotests/bgp_evpn_mh/hostd11/pim.conf create mode 100644 tests/topotests/bgp_evpn_mh/hostd11/zebra.conf create mode 100644 tests/topotests/bgp_evpn_mh/hostd12/evpn.conf create mode 100644 tests/topotests/bgp_evpn_mh/hostd12/pim.conf create mode 100644 tests/topotests/bgp_evpn_mh/hostd12/zebra.conf create mode 100644 tests/topotests/bgp_evpn_mh/hostd21/evpn.conf create mode 100644 tests/topotests/bgp_evpn_mh/hostd21/pim.conf create mode 100644 tests/topotests/bgp_evpn_mh/hostd21/zebra.conf create mode 100644 tests/topotests/bgp_evpn_mh/hostd22/evpn.conf create mode 100644 tests/topotests/bgp_evpn_mh/hostd22/pim.conf create mode 100644 tests/topotests/bgp_evpn_mh/hostd22/zebra.conf create mode 100644 tests/topotests/bgp_evpn_mh/leaf1/evpn.conf create mode 100644 tests/topotests/bgp_evpn_mh/leaf1/pim.conf create mode 100644 tests/topotests/bgp_evpn_mh/leaf1/zebra.conf create mode 100644 tests/topotests/bgp_evpn_mh/leaf2/evpn.conf create mode 100644 tests/topotests/bgp_evpn_mh/leaf2/pim.conf create mode 100644 tests/topotests/bgp_evpn_mh/leaf2/zebra.conf create mode 100644 tests/topotests/bgp_evpn_mh/spine1/evpn.conf create mode 100644 tests/topotests/bgp_evpn_mh/spine1/pim.conf create mode 100644 tests/topotests/bgp_evpn_mh/spine1/zebra.conf create mode 100644 tests/topotests/bgp_evpn_mh/spine2/evpn.conf create mode 100644 tests/topotests/bgp_evpn_mh/spine2/pim.conf create mode 100644 tests/topotests/bgp_evpn_mh/spine2/zebra.conf create mode 100644 tests/topotests/bgp_evpn_mh/test_evpn_mh.py create mode 100644 tests/topotests/bgp_evpn_mh/torm11/evpn.conf create mode 100644 tests/topotests/bgp_evpn_mh/torm11/pim.conf create mode 100644 tests/topotests/bgp_evpn_mh/torm11/zebra.conf create mode 100644 tests/topotests/bgp_evpn_mh/torm12/evpn.conf create mode 100644 tests/topotests/bgp_evpn_mh/torm12/pim.conf create mode 100644 tests/topotests/bgp_evpn_mh/torm12/zebra.conf create mode 100644 tests/topotests/bgp_evpn_mh/torm21/evpn.conf create mode 100644 tests/topotests/bgp_evpn_mh/torm21/pim.conf create mode 100644 tests/topotests/bgp_evpn_mh/torm21/zebra.conf create mode 100644 tests/topotests/bgp_evpn_mh/torm22/evpn.conf create mode 100644 tests/topotests/bgp_evpn_mh/torm22/pim.conf create mode 100644 tests/topotests/bgp_evpn_mh/torm22/zebra.conf create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_base.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt2.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt5.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_base.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt2.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt5.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_base.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt2.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt5.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra.conf create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_base.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt2.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt5.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_base.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt2.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt5.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_base.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt2.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt5.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_base.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt2.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt5.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_base.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt2.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt5.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra.conf create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_base.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt2.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt5.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_base.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt2.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt5.json create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/__init__.py create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/host1/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/host1/zebra.conf create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/host2/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf create mode 100755 tests/topotests/bgp_evpn_overlay_index_gateway/test_bgp_evpn_overlay_index_gateway.py create mode 100644 tests/topotests/bgp_evpn_rt5/__init__.py create mode 100644 tests/topotests/bgp_evpn_rt5/r1/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_rt5/r1/zebra.conf create mode 100644 tests/topotests/bgp_evpn_rt5/r2/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_rt5/r2/zebra.conf create mode 100644 tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/ospfd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/zebra.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgp.l2vpn.evpn.vni.json create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/evpn.vni.json create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/ospfd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/zebra.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgp.l2vpn.evpn.vni.json create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/evpn.vni.json create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/ospfd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/zebra.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/ospfd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/zebra.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/ospfd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/zebra.conf create mode 100755 tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/test_bgp_evpn_vxlan_macvrf_soo.py create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/ospfd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/zebra.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/evpn.vni.json create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/ospfd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/evpn.vni.json create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/ospfd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/zebra.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/__init__.py create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/ospfd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/zebra.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/ospfd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/zebra.conf create mode 100755 tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/P1/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/P1/ospfd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/P1/zebra.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/PE1/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/PE1/evpn.vni.json create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/PE1/ospfd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/PE1/zebra.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/PE2/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/PE2/evpn.vni.json create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/PE2/ospfd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/PE2/zebra.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/__init__.py create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/host1/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/host1/ospfd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/host1/zebra.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/host2/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/host2/ospfd.conf create mode 100644 tests/topotests/bgp_evpn_vxlan_topo1/host2/zebra.conf create mode 100755 tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py create mode 100644 tests/topotests/bgp_extcomm_list_delete/__init__.py create mode 100644 tests/topotests/bgp_extcomm_list_delete/r1/bgpd.conf create mode 100644 tests/topotests/bgp_extcomm_list_delete/r1/zebra.conf create mode 100644 tests/topotests/bgp_extcomm_list_delete/r2/bgpd.conf create mode 100644 tests/topotests/bgp_extcomm_list_delete/r2/zebra.conf create mode 100644 tests/topotests/bgp_extcomm_list_delete/test_bgp_extcomm-list_delete.py create mode 100644 tests/topotests/bgp_extended_optional_parameters_length/__init__.py create mode 100644 tests/topotests/bgp_extended_optional_parameters_length/r1/bgpd.conf create mode 100644 tests/topotests/bgp_extended_optional_parameters_length/r1/zebra.conf create mode 100644 tests/topotests/bgp_extended_optional_parameters_length/r2/bgpd.conf create mode 100644 tests/topotests/bgp_extended_optional_parameters_length/r2/zebra.conf create mode 100644 tests/topotests/bgp_extended_optional_parameters_length/test_bgp_extended_optional_parameters_length.py create mode 100644 tests/topotests/bgp_features/r1/bgp_delayopen_neighbor.json create mode 100644 tests/topotests/bgp_features/r1/bgp_delayopen_summary_established.json create mode 100644 tests/topotests/bgp_features/r1/bgp_delayopen_summary_shutdown.json create mode 100644 tests/topotests/bgp_features/r1/bgp_shutdown_summary.json create mode 100644 tests/topotests/bgp_features/r1/bgp_summary.json create mode 100644 tests/topotests/bgp_features/r1/bgpd.conf create mode 100644 tests/topotests/bgp_features/r1/ip_route.json create mode 100644 tests/topotests/bgp_features/r1/ip_route_norib.json create mode 100644 tests/topotests/bgp_features/r1/ospf6d.conf create mode 100644 tests/topotests/bgp_features/r1/ospf_neighbor.json create mode 100644 tests/topotests/bgp_features/r1/ospfd.conf create mode 100644 tests/topotests/bgp_features/r1/show_bgp.json create mode 100644 tests/topotests/bgp_features/r1/show_bgp_metric_test.json create mode 100644 tests/topotests/bgp_features/r1/zebra.conf create mode 100644 tests/topotests/bgp_features/r2/bgp_delayopen_neighbor.json create mode 100644 tests/topotests/bgp_features/r2/bgp_delayopen_summary_connect.json create mode 100644 tests/topotests/bgp_features/r2/bgp_delayopen_summary_established.json create mode 100644 tests/topotests/bgp_features/r2/bgp_delayopen_summary_shutdown.json create mode 100644 tests/topotests/bgp_features/r2/bgp_shutdown_summary.json create mode 100644 tests/topotests/bgp_features/r2/bgp_summary.json create mode 100644 tests/topotests/bgp_features/r2/bgpd.conf create mode 100644 tests/topotests/bgp_features/r2/ospf6d.conf create mode 100644 tests/topotests/bgp_features/r2/ospf_neighbor.json create mode 100644 tests/topotests/bgp_features/r2/ospfd.conf create mode 100644 tests/topotests/bgp_features/r2/show_bgp.json create mode 100644 tests/topotests/bgp_features/r2/show_bgp_metric_test.json create mode 100644 tests/topotests/bgp_features/r2/zebra.conf create mode 100644 tests/topotests/bgp_features/r3/bgp_summary.json create mode 100644 tests/topotests/bgp_features/r3/ospf6d.conf create mode 100644 tests/topotests/bgp_features/r3/ospf_neighbor.json create mode 100644 tests/topotests/bgp_features/r3/ospfd.conf create mode 100644 tests/topotests/bgp_features/r3/zebra.conf create mode 100644 tests/topotests/bgp_features/r4/bgp_delayopen_summary_established.json create mode 100644 tests/topotests/bgp_features/r4/bgp_delayopen_summary_shutdown.json create mode 100644 tests/topotests/bgp_features/r4/bgp_shutdown_summary.json create mode 100644 tests/topotests/bgp_features/r4/bgp_summary.json create mode 100644 tests/topotests/bgp_features/r4/bgpd.conf create mode 100644 tests/topotests/bgp_features/r4/show_bgp_metric_test.json create mode 100644 tests/topotests/bgp_features/r4/zebra.conf create mode 100644 tests/topotests/bgp_features/r5/bgp_delayopen_neighbor.json create mode 100644 tests/topotests/bgp_features/r5/bgp_delayopen_summary_connect.json create mode 100644 tests/topotests/bgp_features/r5/bgp_delayopen_summary_established.json create mode 100644 tests/topotests/bgp_features/r5/bgp_delayopen_summary_shutdown.json create mode 100644 tests/topotests/bgp_features/r5/bgp_summary.json create mode 100644 tests/topotests/bgp_features/r5/bgpd.conf create mode 100644 tests/topotests/bgp_features/r5/show_bgp_metric_test.json create mode 100644 tests/topotests/bgp_features/r5/zebra.conf create mode 100644 tests/topotests/bgp_features/test_bgp_features.dot create mode 100644 tests/topotests/bgp_features/test_bgp_features.pdf create mode 100644 tests/topotests/bgp_features/test_bgp_features.py create mode 100644 tests/topotests/bgp_flowspec/__init__.py create mode 100644 tests/topotests/bgp_flowspec/exabgp.env create mode 100644 tests/topotests/bgp_flowspec/peer1/exabgp.cfg create mode 100644 tests/topotests/bgp_flowspec/r1/bgpd.conf create mode 100644 tests/topotests/bgp_flowspec/r1/summary.txt create mode 100644 tests/topotests/bgp_flowspec/r1/zebra.conf create mode 100644 tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py create mode 100644 tests/topotests/bgp_gr_functionality_topo1/bgp_gr_topojson_topo1.json create mode 100644 tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-1.py create mode 100644 tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-2.py create mode 100644 tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py create mode 100644 tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py create mode 100644 tests/topotests/bgp_gr_functionality_topo2/bgp_gr_topojson_topo2.json create mode 100644 tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-1.py create mode 100644 tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-2.py create mode 100644 tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-3.py create mode 100644 tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-4.py create mode 100644 tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.json create mode 100644 tests/topotests/bgp_gr_functionality_topo3/test_bgp_gr_functionality_topo3.py create mode 100644 tests/topotests/bgp_gr_notification/__init__.py create mode 100644 tests/topotests/bgp_gr_notification/r1/bgpd.conf create mode 100644 tests/topotests/bgp_gr_notification/r1/zebra.conf create mode 100644 tests/topotests/bgp_gr_notification/r2/bgpd.conf create mode 100644 tests/topotests/bgp_gr_notification/r2/zebra.conf create mode 100644 tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py create mode 100644 tests/topotests/bgp_gr_restart_retain_routes/__init__.py create mode 100644 tests/topotests/bgp_gr_restart_retain_routes/r1/bgpd.conf create mode 100644 tests/topotests/bgp_gr_restart_retain_routes/r1/zebra.conf create mode 100644 tests/topotests/bgp_gr_restart_retain_routes/r2/bgpd.conf create mode 100644 tests/topotests/bgp_gr_restart_retain_routes/r2/zebra.conf create mode 100644 tests/topotests/bgp_gr_restart_retain_routes/test_bgp_gr_restart_retain_routes.py create mode 100644 tests/topotests/bgp_gshut/__init__.py create mode 100644 tests/topotests/bgp_gshut/r1/bgp_route_1.json create mode 100644 tests/topotests/bgp_gshut/r1/bgp_route_2.json create mode 100644 tests/topotests/bgp_gshut/r1/bgpd.conf create mode 100644 tests/topotests/bgp_gshut/r1/zebra.conf create mode 100644 tests/topotests/bgp_gshut/r2/bgp_sum_1.json create mode 100644 tests/topotests/bgp_gshut/r2/bgp_sum_2.json create mode 100644 tests/topotests/bgp_gshut/r2/bgpd.conf create mode 100644 tests/topotests/bgp_gshut/r2/zebra.conf create mode 100644 tests/topotests/bgp_gshut/r3/bgp_route_1.json create mode 100644 tests/topotests/bgp_gshut/r3/bgp_route_2.json create mode 100644 tests/topotests/bgp_gshut/r3/bgpd.conf create mode 100644 tests/topotests/bgp_gshut/r3/zebra.conf create mode 100644 tests/topotests/bgp_gshut/r4/bgpd.conf create mode 100644 tests/topotests/bgp_gshut/r4/zebra.conf create mode 100644 tests/topotests/bgp_gshut/r5/bgp_route_1.json create mode 100644 tests/topotests/bgp_gshut/r5/bgp_route_2.json create mode 100644 tests/topotests/bgp_gshut/r5/bgpd.conf create mode 100644 tests/topotests/bgp_gshut/r5/zebra.conf create mode 100644 tests/topotests/bgp_gshut/test_bgp_gshut.py create mode 100644 tests/topotests/bgp_gshut_topo1/ebgp_gshut_topo1.json create mode 100644 tests/topotests/bgp_gshut_topo1/ibgp_gshut_topo1.json create mode 100644 tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py create mode 100644 tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py create mode 100644 tests/topotests/bgp_instance_del_test/__init__.py create mode 120000 tests/topotests/bgp_instance_del_test/ce1 create mode 120000 tests/topotests/bgp_instance_del_test/ce2 create mode 120000 tests/topotests/bgp_instance_del_test/ce3 create mode 120000 tests/topotests/bgp_instance_del_test/ce4 create mode 120000 tests/topotests/bgp_instance_del_test/customize.py create mode 120000 tests/topotests/bgp_instance_del_test/r1 create mode 120000 tests/topotests/bgp_instance_del_test/r2 create mode 120000 tests/topotests/bgp_instance_del_test/r3 create mode 120000 tests/topotests/bgp_instance_del_test/r4 create mode 120000 tests/topotests/bgp_instance_del_test/scripts create mode 100755 tests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py create mode 100644 tests/topotests/bgp_ipv4_class_e_peer/__init__.py create mode 100644 tests/topotests/bgp_ipv4_class_e_peer/r1/bgpd.conf create mode 100644 tests/topotests/bgp_ipv4_class_e_peer/r1/zebra.conf create mode 100644 tests/topotests/bgp_ipv4_class_e_peer/r2/bgpd.conf create mode 100644 tests/topotests/bgp_ipv4_class_e_peer/r2/zebra.conf create mode 100644 tests/topotests/bgp_ipv4_class_e_peer/test_bgp_ipv4_class_e_peer.py create mode 100644 tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json create mode 100644 tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json create mode 100644 tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json create mode 100644 tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json create mode 100644 tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json create mode 100644 tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py create mode 100644 tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py create mode 100644 tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py create mode 100644 tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py create mode 100644 tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py create mode 100644 tests/topotests/bgp_ipv6_ll_peering/__init__.py create mode 100644 tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf create mode 100644 tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf create mode 100644 tests/topotests/bgp_ipv6_ll_peering/r2/bgpd.conf create mode 100644 tests/topotests/bgp_ipv6_ll_peering/r2/zebra.conf create mode 100644 tests/topotests/bgp_ipv6_ll_peering/test_bgp_ipv6_ll_peering.py create mode 100644 tests/topotests/bgp_ipv6_rtadv/__init__.py create mode 100644 tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf create mode 100644 tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json create mode 100644 tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json create mode 100644 tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf create mode 100644 tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf create mode 100644 tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json create mode 100644 tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json create mode 100644 tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf create mode 100644 tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot create mode 100644 tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py create mode 100755 tests/topotests/bgp_l3vpn_to_bgp_direct/__init__.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/customize.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ldpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r1/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ldpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r2/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ldpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r3/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ldpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/r4/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/check_routes.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/cleanup_all.py create mode 100755 tests/topotests/bgp_l3vpn_to_bgp_direct/test_bgp_l3vpn_to_bgp_direct.py create mode 100755 tests/topotests/bgp_l3vpn_to_bgp_vrf/__init__.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ldpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ldpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ldpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ldpd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/add_routes.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/cleanup_all.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/del_bgp_instances.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/notification_check.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py create mode 100644 tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py create mode 100755 tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py create mode 100644 tests/topotests/bgp_labeled_unicast_addpath/__init__.py create mode 100644 tests/topotests/bgp_labeled_unicast_addpath/r1/bgpd.conf create mode 100644 tests/topotests/bgp_labeled_unicast_addpath/r1/zebra.conf create mode 100644 tests/topotests/bgp_labeled_unicast_addpath/r2/bgpd.conf create mode 100644 tests/topotests/bgp_labeled_unicast_addpath/r2/zebra.conf create mode 100644 tests/topotests/bgp_labeled_unicast_addpath/r3/bgpd.conf create mode 100644 tests/topotests/bgp_labeled_unicast_addpath/r3/zebra.conf create mode 100644 tests/topotests/bgp_labeled_unicast_addpath/r4/bgpd.conf create mode 100644 tests/topotests/bgp_labeled_unicast_addpath/r4/zebra.conf create mode 100644 tests/topotests/bgp_labeled_unicast_addpath/r5/bgpd.conf create mode 100644 tests/topotests/bgp_labeled_unicast_addpath/r5/zebra.conf create mode 100644 tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py create mode 100644 tests/topotests/bgp_labeled_unicast_default_originate/__init__.py create mode 100644 tests/topotests/bgp_labeled_unicast_default_originate/r1/bgpd.conf create mode 100644 tests/topotests/bgp_labeled_unicast_default_originate/r1/zebra.conf create mode 100644 tests/topotests/bgp_labeled_unicast_default_originate/r2/bgpd.conf create mode 100644 tests/topotests/bgp_labeled_unicast_default_originate/r2/zebra.conf create mode 100644 tests/topotests/bgp_labeled_unicast_default_originate/test_bgp_labeled_unicast_default_originate.py create mode 100644 tests/topotests/bgp_large_comm_list_match/__init__.py create mode 100644 tests/topotests/bgp_large_comm_list_match/r1/bgpd.conf create mode 100644 tests/topotests/bgp_large_comm_list_match/r1/zebra.conf create mode 100644 tests/topotests/bgp_large_comm_list_match/r2/bgpd.conf create mode 100644 tests/topotests/bgp_large_comm_list_match/r2/zebra.conf create mode 100644 tests/topotests/bgp_large_comm_list_match/r3/bgpd.conf create mode 100644 tests/topotests/bgp_large_comm_list_match/r3/zebra.conf create mode 100644 tests/topotests/bgp_large_comm_list_match/test_bgp_large_comm_list_match.py create mode 100644 tests/topotests/bgp_large_community/__init__.py create mode 100644 tests/topotests/bgp_large_community/bgp_large_community_topo_1.json create mode 100644 tests/topotests/bgp_large_community/bgp_large_community_topo_2.json create mode 100644 tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py create mode 100644 tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py create mode 100755 tests/topotests/bgp_link_bw_ip/__init__.py create mode 100644 tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/v4_route.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r10/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r10/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json create mode 100644 tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json create mode 100644 tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json create mode 100644 tests/topotests/bgp_link_bw_ip/r2/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json create mode 100644 tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json create mode 100644 tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json create mode 100644 tests/topotests/bgp_link_bw_ip/r2/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r3/bgp-route-1.json create mode 100644 tests/topotests/bgp_link_bw_ip/r3/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r3/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json create mode 100644 tests/topotests/bgp_link_bw_ip/r4/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json create mode 100644 tests/topotests/bgp_link_bw_ip/r4/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r5/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r5/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r6/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r6/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r7/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r7/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r8/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r8/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r9/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r9/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py create mode 100644 tests/topotests/bgp_listen_on_multiple_addresses/bgp_listen_on_multiple_addresses.json create mode 100755 tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py create mode 100644 tests/topotests/bgp_llgr/__init__.py create mode 100644 tests/topotests/bgp_llgr/r0/bgpd.conf create mode 100644 tests/topotests/bgp_llgr/r0/zebra.conf create mode 100644 tests/topotests/bgp_llgr/r1/bgpd.conf create mode 100644 tests/topotests/bgp_llgr/r1/zebra.conf create mode 100644 tests/topotests/bgp_llgr/r2/bgpd.conf create mode 100644 tests/topotests/bgp_llgr/r2/zebra.conf create mode 100644 tests/topotests/bgp_llgr/r3/bgpd.conf create mode 100644 tests/topotests/bgp_llgr/r3/zebra.conf create mode 100644 tests/topotests/bgp_llgr/r4/bgpd.conf create mode 100644 tests/topotests/bgp_llgr/r4/zebra.conf create mode 100644 tests/topotests/bgp_llgr/test_bgp_llgr.py create mode 100644 tests/topotests/bgp_local_as/__init__.py create mode 100644 tests/topotests/bgp_local_as/r1/bgpd.conf create mode 100644 tests/topotests/bgp_local_as/r1/zebra.conf create mode 100644 tests/topotests/bgp_local_as/r2/bgpd.conf create mode 100644 tests/topotests/bgp_local_as/r2/zebra.conf create mode 100644 tests/topotests/bgp_local_as/r3/bgpd.conf create mode 100644 tests/topotests/bgp_local_as/r3/zebra.conf create mode 100644 tests/topotests/bgp_local_as/test_bgp_local_as.py create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/__init__.py create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r1/bgpd.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r1/zebra.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r2/bgpd.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r2/zebra.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r3/bgpd.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r3/zebra.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r4/bgpd.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r4/zebra.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py create mode 100644 tests/topotests/bgp_local_as_private_remove/__init__.py create mode 100644 tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf create mode 100644 tests/topotests/bgp_local_as_private_remove/r1/zebra.conf create mode 100644 tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf create mode 100644 tests/topotests/bgp_local_as_private_remove/r2/zebra.conf create mode 100644 tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf create mode 100644 tests/topotests/bgp_local_as_private_remove/r3/zebra.conf create mode 100644 tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf create mode 100644 tests/topotests/bgp_local_as_private_remove/r4/zebra.conf create mode 100644 tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py create mode 100644 tests/topotests/bgp_local_asn/bgp_local_asn_agg.json create mode 100644 tests/topotests/bgp_local_asn/bgp_local_asn_ecmp.json create mode 100644 tests/topotests/bgp_local_asn/bgp_local_asn_topo1.json create mode 100644 tests/topotests/bgp_local_asn/bgp_local_asn_topo2.json create mode 100644 tests/topotests/bgp_local_asn/bgp_local_asn_vrf_topo1.json create mode 100644 tests/topotests/bgp_local_asn/bgp_local_asn_vrf_topo2.json create mode 100644 tests/topotests/bgp_local_asn/test_bgp_local_asn_agg.py create mode 100644 tests/topotests/bgp_local_asn/test_bgp_local_asn_ecmp.py create mode 100644 tests/topotests/bgp_local_asn/test_bgp_local_asn_topo1.py create mode 100644 tests/topotests/bgp_local_asn/test_bgp_local_asn_topo2.py create mode 100644 tests/topotests/bgp_local_asn/test_bgp_local_asn_vrf_topo1.py create mode 100644 tests/topotests/bgp_local_asn/test_bgp_local_asn_vrf_topo2.py create mode 100644 tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_agg.json create mode 100644 tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json create mode 100644 tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_topo1.json create mode 100644 tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_agg.py create mode 100644 tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_ecmp.py create mode 100644 tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_topo1.py create mode 100644 tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf create mode 100644 tests/topotests/bgp_lu_explicitnull/r1/zebra.conf create mode 100644 tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf create mode 100644 tests/topotests/bgp_lu_explicitnull/r2/zebra.conf create mode 100644 tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py create mode 100644 tests/topotests/bgp_lu_topo1/R1/bgpd.conf create mode 100644 tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json create mode 100644 tests/topotests/bgp_lu_topo1/R1/zebra.conf create mode 100644 tests/topotests/bgp_lu_topo1/R2/bgpd.conf create mode 100644 tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json create mode 100644 tests/topotests/bgp_lu_topo1/R2/zebra.conf create mode 100644 tests/topotests/bgp_lu_topo1/R3/bgpd.conf create mode 100644 tests/topotests/bgp_lu_topo1/R3/zebra.conf create mode 100644 tests/topotests/bgp_lu_topo1/test_bgp_lu.py create mode 100644 tests/topotests/bgp_lu_topo2/R1/bgpd.conf create mode 100644 tests/topotests/bgp_lu_topo2/R1/labelpool.summ.json create mode 100644 tests/topotests/bgp_lu_topo2/R1/zebra.conf create mode 100644 tests/topotests/bgp_lu_topo2/R2/bgpd.conf create mode 100644 tests/topotests/bgp_lu_topo2/R2/labelpool.summ.json create mode 100644 tests/topotests/bgp_lu_topo2/R2/zebra.conf create mode 100644 tests/topotests/bgp_lu_topo2/R3/bgpd.conf create mode 100644 tests/topotests/bgp_lu_topo2/R3/staticd.conf create mode 100644 tests/topotests/bgp_lu_topo2/R3/zebra.conf create mode 100644 tests/topotests/bgp_lu_topo2/R4/bgpd.conf create mode 100644 tests/topotests/bgp_lu_topo2/R4/zebra.conf create mode 100644 tests/topotests/bgp_lu_topo2/test_bgp_lu2.py create mode 100644 tests/topotests/bgp_max_med_on_startup/__init__.py create mode 100644 tests/topotests/bgp_max_med_on_startup/r1/bgpd.conf create mode 100644 tests/topotests/bgp_max_med_on_startup/r1/zebra.conf create mode 100644 tests/topotests/bgp_max_med_on_startup/r2/bgpd.conf create mode 100644 tests/topotests/bgp_max_med_on_startup/r2/zebra.conf create mode 100644 tests/topotests/bgp_max_med_on_startup/test_bgp_max_med_on_startup.py create mode 100644 tests/topotests/bgp_maximum_prefix_invalid_update/__init__.py create mode 100644 tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf create mode 100644 tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf create mode 100644 tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf create mode 100644 tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf create mode 100644 tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py create mode 100644 tests/topotests/bgp_maximum_prefix_out/__init__.py create mode 100644 tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf create mode 100644 tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf create mode 100644 tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf create mode 100644 tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf create mode 100644 tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py create mode 100644 tests/topotests/bgp_minimum_holdtime/__init__.py create mode 100644 tests/topotests/bgp_minimum_holdtime/r1/bgpd.conf create mode 100644 tests/topotests/bgp_minimum_holdtime/r1/zebra.conf create mode 100644 tests/topotests/bgp_minimum_holdtime/r2/bgpd.conf create mode 100644 tests/topotests/bgp_minimum_holdtime/r2/zebra.conf create mode 100755 tests/topotests/bgp_minimum_holdtime/test_bgp_minimum_holdtime.py create mode 100644 tests/topotests/bgp_multi_vrf_topo1/bgp_multi_vrf_topo1.json create mode 100644 tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py create mode 100644 tests/topotests/bgp_multi_vrf_topo2/bgp_multi_vrf_topo2.json create mode 100644 tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py create mode 100644 tests/topotests/bgp_multiview_topo1/README.md create mode 100644 tests/topotests/bgp_multiview_topo1/exabgp.env create mode 100755 tests/topotests/bgp_multiview_topo1/peer1/exa-send.py create mode 100644 tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg create mode 100755 tests/topotests/bgp_multiview_topo1/peer2/exa-send.py create mode 100644 tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg create mode 100755 tests/topotests/bgp_multiview_topo1/peer3/exa-send.py create mode 100644 tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg create mode 100755 tests/topotests/bgp_multiview_topo1/peer4/exa-send.py create mode 100644 tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg create mode 100755 tests/topotests/bgp_multiview_topo1/peer5/exa-send.py create mode 100644 tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg create mode 100755 tests/topotests/bgp_multiview_topo1/peer6/exa-send.py create mode 100644 tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg create mode 100755 tests/topotests/bgp_multiview_topo1/peer7/exa-send.py create mode 100644 tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg create mode 100755 tests/topotests/bgp_multiview_topo1/peer8/exa-send.py create mode 100644 tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg create mode 100644 tests/topotests/bgp_multiview_topo1/r1/bgpd.conf create mode 100644 tests/topotests/bgp_multiview_topo1/r1/view_1.json create mode 100644 tests/topotests/bgp_multiview_topo1/r1/view_2.json create mode 100644 tests/topotests/bgp_multiview_topo1/r1/view_3.json create mode 100644 tests/topotests/bgp_multiview_topo1/r1/zebra.conf create mode 100644 tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py create mode 100644 tests/topotests/bgp_node_target_extcommunities/__init__.py create mode 100644 tests/topotests/bgp_node_target_extcommunities/r1/frr.conf create mode 100644 tests/topotests/bgp_node_target_extcommunities/r2/frr.conf create mode 100644 tests/topotests/bgp_node_target_extcommunities/r3/frr.conf create mode 100644 tests/topotests/bgp_node_target_extcommunities/r4/frr.conf create mode 100644 tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py create mode 100644 tests/topotests/bgp_orf/__init__.py create mode 100644 tests/topotests/bgp_orf/r1/bgpd.conf create mode 100644 tests/topotests/bgp_orf/r1/zebra.conf create mode 100644 tests/topotests/bgp_orf/r2/bgpd.conf create mode 100644 tests/topotests/bgp_orf/r2/zebra.conf create mode 100644 tests/topotests/bgp_orf/test_bgp_orf.py create mode 100644 tests/topotests/bgp_path_attribute_discard/__init__.py create mode 100644 tests/topotests/bgp_path_attribute_discard/exabgp.env create mode 100644 tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg create mode 100644 tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf create mode 100644 tests/topotests/bgp_path_attribute_discard/r1/zebra.conf create mode 100644 tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py create mode 100644 tests/topotests/bgp_path_attribute_treat_as_withdraw/__init__.py create mode 100644 tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/bgpd.conf create mode 100644 tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/zebra.conf create mode 100644 tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/bgpd.conf create mode 100644 tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/zebra.conf create mode 100644 tests/topotests/bgp_path_attribute_treat_as_withdraw/test_bgp_path_attribute_treat_as_withdraw.py create mode 100644 tests/topotests/bgp_path_attributes_topo1/__init__.py create mode 100644 tests/topotests/bgp_path_attributes_topo1/bgp_path_attributes.json create mode 100644 tests/topotests/bgp_path_attributes_topo1/test_bgp_path_attributes.py create mode 100644 tests/topotests/bgp_path_selection/__init__.py create mode 100644 tests/topotests/bgp_path_selection/r1/bgpd.conf create mode 100644 tests/topotests/bgp_path_selection/r1/ldpd.conf create mode 100644 tests/topotests/bgp_path_selection/r1/staticd.conf create mode 100644 tests/topotests/bgp_path_selection/r1/zebra.conf create mode 100644 tests/topotests/bgp_path_selection/r2/bgpd.conf create mode 100644 tests/topotests/bgp_path_selection/r2/ldpd.conf create mode 100644 tests/topotests/bgp_path_selection/r2/staticd.conf create mode 100644 tests/topotests/bgp_path_selection/r2/zebra.conf create mode 100644 tests/topotests/bgp_path_selection/r3/bgpd.conf create mode 100644 tests/topotests/bgp_path_selection/r3/ldpd.conf create mode 100644 tests/topotests/bgp_path_selection/r3/staticd.conf create mode 100644 tests/topotests/bgp_path_selection/r3/zebra.conf create mode 100644 tests/topotests/bgp_path_selection/test_bgp_path_selection.py create mode 100644 tests/topotests/bgp_peer_graceful_shutdown/__init__.py create mode 100644 tests/topotests/bgp_peer_graceful_shutdown/r1/bgpd.conf create mode 100644 tests/topotests/bgp_peer_graceful_shutdown/r1/zebra.conf create mode 100644 tests/topotests/bgp_peer_graceful_shutdown/r2/bgpd.conf create mode 100644 tests/topotests/bgp_peer_graceful_shutdown/r2/zebra.conf create mode 100644 tests/topotests/bgp_peer_graceful_shutdown/r3/bgpd.conf create mode 100644 tests/topotests/bgp_peer_graceful_shutdown/r3/zebra.conf create mode 100644 tests/topotests/bgp_peer_graceful_shutdown/test_bgp_peer_graceful_shutdown.py create mode 100644 tests/topotests/bgp_peer_group/__init__.py create mode 100644 tests/topotests/bgp_peer_group/r1/bgpd.conf create mode 100644 tests/topotests/bgp_peer_group/r1/zebra.conf create mode 100644 tests/topotests/bgp_peer_group/r2/bgpd.conf create mode 100644 tests/topotests/bgp_peer_group/r2/zebra.conf create mode 100644 tests/topotests/bgp_peer_group/r3/bgpd.conf create mode 100644 tests/topotests/bgp_peer_group/r3/zebra.conf create mode 100644 tests/topotests/bgp_peer_group/test_bgp_peer-group.py create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/exabgp.env create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/r1/bgpd.conf create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/r1/multipath.json create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/r1/not-multipath.json create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-confed.json create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-iBGP.json create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-no-recursive.json create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-recursive.json create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1.json create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-ip-route.json create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-no-recursive.json create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-recursive.json create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/r1/zebra.conf create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/r2/bgpd.conf create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/r2/staticd.conf create mode 100644 tests/topotests/bgp_peer_type_multipath_relax/r2/zebra.conf create mode 100755 tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py create mode 100644 tests/topotests/bgp_prefix_list_any/__init__.py create mode 100644 tests/topotests/bgp_prefix_list_any/r1/bgpd.conf create mode 100644 tests/topotests/bgp_prefix_list_any/r1/zebra.conf create mode 100644 tests/topotests/bgp_prefix_list_any/r2/bgpd.conf create mode 100644 tests/topotests/bgp_prefix_list_any/r2/zebra.conf create mode 100644 tests/topotests/bgp_prefix_list_any/test_bgp_prefix_list_any.py create mode 100644 tests/topotests/bgp_prefix_list_topo1/__init__.py create mode 100644 tests/topotests/bgp_prefix_list_topo1/prefix_lists.json create mode 100644 tests/topotests/bgp_prefix_list_topo1/prefix_modify.json create mode 100644 tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py create mode 100644 tests/topotests/bgp_prefix_list_topo1/test_prefix_modify.py create mode 100644 tests/topotests/bgp_prefix_sid/__init__.py create mode 100644 tests/topotests/bgp_prefix_sid/exabgp.env create mode 100644 tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg create mode 100644 tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg create mode 100644 tests/topotests/bgp_prefix_sid/r1/bgpd.conf create mode 100644 tests/topotests/bgp_prefix_sid/r1/zebra.conf create mode 100644 tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py create mode 100644 tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg create mode 100644 tests/topotests/bgp_prefix_sid2/peer1/exabgp.env create mode 100644 tests/topotests/bgp_prefix_sid2/r1/bgpd.conf create mode 100644 tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json create mode 100644 tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json create mode 100644 tests/topotests/bgp_prefix_sid2/r1/zebra.conf create mode 100755 tests/topotests/bgp_prefix_sid2/test_bgp_prefix_sid2.py create mode 100644 tests/topotests/bgp_recursive_route_ebgp_multi_hop/bgp_recursive_route_ebgp_multi_hop.json create mode 100644 tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py create mode 100644 tests/topotests/bgp_reject_as_sets/__init__.py create mode 100644 tests/topotests/bgp_reject_as_sets/r1/bgpd.conf create mode 100644 tests/topotests/bgp_reject_as_sets/r1/zebra.conf create mode 100644 tests/topotests/bgp_reject_as_sets/r2/bgpd.conf create mode 100644 tests/topotests/bgp_reject_as_sets/r2/zebra.conf create mode 100644 tests/topotests/bgp_reject_as_sets/r3/bgpd.conf create mode 100644 tests/topotests/bgp_reject_as_sets/r3/zebra.conf create mode 100644 tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py create mode 100644 tests/topotests/bgp_remove_private_as/r1/bgpd.conf create mode 100644 tests/topotests/bgp_remove_private_as/r1/zebra.conf create mode 100644 tests/topotests/bgp_remove_private_as/r2/bgpd.conf create mode 100644 tests/topotests/bgp_remove_private_as/r2/zebra.conf create mode 100644 tests/topotests/bgp_remove_private_as/r3/bgpd.conf create mode 100644 tests/topotests/bgp_remove_private_as/r3/zebra.conf create mode 100644 tests/topotests/bgp_remove_private_as/r4/bgpd.conf create mode 100644 tests/topotests/bgp_remove_private_as/r4/zebra.conf create mode 100644 tests/topotests/bgp_remove_private_as/r5/bgpd.conf create mode 100644 tests/topotests/bgp_remove_private_as/r5/zebra.conf create mode 100644 tests/topotests/bgp_remove_private_as/test_bgp_remove_private_as.py create mode 100644 tests/topotests/bgp_remove_private_as_route_map/__init__.py create mode 100644 tests/topotests/bgp_remove_private_as_route_map/r1/frr.conf create mode 100644 tests/topotests/bgp_remove_private_as_route_map/r2/frr.conf create mode 100644 tests/topotests/bgp_remove_private_as_route_map/test_bgp_remove_private_as_route_map.py create mode 100755 tests/topotests/bgp_rfapi_basic_sanity/__init__.py create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/customize.py create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/r1/zebra.conf create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/r2/zebra.conf create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/r3/zebra.conf create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/r4/zebra.conf create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/scripts/add_routes.py create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/scripts/check_close.py create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/scripts/check_routes.py create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/scripts/check_timeout.py create mode 100644 tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py create mode 100755 tests/topotests/bgp_rfapi_basic_sanity/test_bgp_rfapi_basic_sanity.py create mode 100755 tests/topotests/bgp_rfapi_basic_sanity_config2/__init__.py create mode 120000 tests/topotests/bgp_rfapi_basic_sanity_config2/customize.py create mode 100644 tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf create mode 120000 tests/topotests/bgp_rfapi_basic_sanity_config2/r1/ospfd.conf create mode 120000 tests/topotests/bgp_rfapi_basic_sanity_config2/r1/zebra.conf create mode 100644 tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf create mode 120000 tests/topotests/bgp_rfapi_basic_sanity_config2/r2/ospfd.conf create mode 120000 tests/topotests/bgp_rfapi_basic_sanity_config2/r2/zebra.conf create mode 100644 tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf create mode 120000 tests/topotests/bgp_rfapi_basic_sanity_config2/r3/ospfd.conf create mode 120000 tests/topotests/bgp_rfapi_basic_sanity_config2/r3/zebra.conf create mode 100644 tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf create mode 120000 tests/topotests/bgp_rfapi_basic_sanity_config2/r4/ospfd.conf create mode 120000 tests/topotests/bgp_rfapi_basic_sanity_config2/r4/zebra.conf create mode 120000 tests/topotests/bgp_rfapi_basic_sanity_config2/scripts create mode 120000 tests/topotests/bgp_rfapi_basic_sanity_config2/test_bgp_rfapi_basic_sanity_config2.py create mode 100644 tests/topotests/bgp_rmap_extcommunity_none/__init__.py create mode 100644 tests/topotests/bgp_rmap_extcommunity_none/r1/bgpd.conf create mode 100644 tests/topotests/bgp_rmap_extcommunity_none/r1/zebra.conf create mode 100644 tests/topotests/bgp_rmap_extcommunity_none/r2/bgpd.conf create mode 100644 tests/topotests/bgp_rmap_extcommunity_none/r2/zebra.conf create mode 100644 tests/topotests/bgp_rmap_extcommunity_none/test_bgp_rmap_extcommunity_none.py create mode 100644 tests/topotests/bgp_roles_capability/__init__.py create mode 100644 tests/topotests/bgp_roles_capability/r1/bgpd.conf create mode 100644 tests/topotests/bgp_roles_capability/r1/zebra.conf create mode 100644 tests/topotests/bgp_roles_capability/r2/bgpd.conf create mode 100644 tests/topotests/bgp_roles_capability/r2/zebra.conf create mode 100644 tests/topotests/bgp_roles_capability/r3/bgpd.conf create mode 100644 tests/topotests/bgp_roles_capability/r3/zebra.conf create mode 100644 tests/topotests/bgp_roles_capability/r4/bgpd.conf create mode 100644 tests/topotests/bgp_roles_capability/r4/zebra.conf create mode 100644 tests/topotests/bgp_roles_capability/r5/bgpd.conf create mode 100644 tests/topotests/bgp_roles_capability/r5/zebra.conf create mode 100644 tests/topotests/bgp_roles_capability/r6/bgpd.conf create mode 100644 tests/topotests/bgp_roles_capability/r6/zebra.conf create mode 100644 tests/topotests/bgp_roles_capability/roles_capability_stand.dot create mode 100644 tests/topotests/bgp_roles_capability/roles_capability_stand.jpg create mode 100644 tests/topotests/bgp_roles_capability/test_bgp_roles_capability.py create mode 100644 tests/topotests/bgp_roles_filtering/__init__.py create mode 100644 tests/topotests/bgp_roles_filtering/r1/bgpd.conf create mode 100644 tests/topotests/bgp_roles_filtering/r1/zebra.conf create mode 100644 tests/topotests/bgp_roles_filtering/r10/bgpd.conf create mode 100644 tests/topotests/bgp_roles_filtering/r10/zebra.conf create mode 100644 tests/topotests/bgp_roles_filtering/r2/bgpd.conf create mode 100644 tests/topotests/bgp_roles_filtering/r2/zebra.conf create mode 100644 tests/topotests/bgp_roles_filtering/r3/bgpd.conf create mode 100644 tests/topotests/bgp_roles_filtering/r3/zebra.conf create mode 100644 tests/topotests/bgp_roles_filtering/r4/bgpd.conf create mode 100644 tests/topotests/bgp_roles_filtering/r4/zebra.conf create mode 100644 tests/topotests/bgp_roles_filtering/r5/bgpd.conf create mode 100644 tests/topotests/bgp_roles_filtering/r5/zebra.conf create mode 100644 tests/topotests/bgp_roles_filtering/r6/bgpd.conf create mode 100644 tests/topotests/bgp_roles_filtering/r6/zebra.conf create mode 100644 tests/topotests/bgp_roles_filtering/r7/bgpd.conf create mode 100644 tests/topotests/bgp_roles_filtering/r7/zebra.conf create mode 100644 tests/topotests/bgp_roles_filtering/roles_filtering_stand.dot create mode 100644 tests/topotests/bgp_roles_filtering/roles_filtering_stand.jpg create mode 100644 tests/topotests/bgp_roles_filtering/test_bgp_roles_filtering.py create mode 100644 tests/topotests/bgp_route_aggregation/bgp_aggregation.json create mode 100644 tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py create mode 100644 tests/topotests/bgp_route_map/bgp_route_map_topo1.json create mode 100755 tests/topotests/bgp_route_map/bgp_route_map_topo2.json create mode 100644 tests/topotests/bgp_route_map/test_route_map_topo1.py create mode 100644 tests/topotests/bgp_route_map/test_route_map_topo2.py create mode 100644 tests/topotests/bgp_route_map_delay_timer/__init__.py create mode 100644 tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf create mode 100644 tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf create mode 100644 tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf create mode 100644 tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf create mode 100644 tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py create mode 100644 tests/topotests/bgp_route_map_match_ipv6_nexthop/__init__.py create mode 100644 tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/bgpd.conf create mode 100644 tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/zebra.conf create mode 100644 tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/bgpd.conf create mode 100644 tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/zebra.conf create mode 100644 tests/topotests/bgp_route_map_match_ipv6_nexthop/test_bgp_route_map_match_ipv6_nexthop.py create mode 100644 tests/topotests/bgp_route_map_match_source_protocol/__init__.py create mode 100644 tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf create mode 100644 tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf create mode 100644 tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf create mode 100644 tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py create mode 100644 tests/topotests/bgp_route_map_on_match_next/__init__.py create mode 100644 tests/topotests/bgp_route_map_on_match_next/r1/bgpd.conf create mode 100644 tests/topotests/bgp_route_map_on_match_next/r1/zebra.conf create mode 100644 tests/topotests/bgp_route_map_on_match_next/r2/bgpd.conf create mode 100644 tests/topotests/bgp_route_map_on_match_next/r2/zebra.conf create mode 100644 tests/topotests/bgp_route_map_on_match_next/test_bgp_route_map_on_match_next.py create mode 100644 tests/topotests/bgp_route_map_vpn_import/__init__.py create mode 100644 tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf create mode 100644 tests/topotests/bgp_route_map_vpn_import/r1/zebra.conf create mode 100644 tests/topotests/bgp_route_map_vpn_import/test_bgp_route_map_vpn_import.py create mode 100644 tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf create mode 100644 tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py create mode 100644 tests/topotests/bgp_route_server_client/__init__.py create mode 100644 tests/topotests/bgp_route_server_client/r1/bgpd.conf create mode 100644 tests/topotests/bgp_route_server_client/r1/zebra.conf create mode 100644 tests/topotests/bgp_route_server_client/r2/bgpd.conf create mode 100644 tests/topotests/bgp_route_server_client/r2/zebra.conf create mode 100644 tests/topotests/bgp_route_server_client/r3/bgpd.conf create mode 100644 tests/topotests/bgp_route_server_client/r3/zebra.conf create mode 100644 tests/topotests/bgp_route_server_client/test_bgp_route_server_client.py create mode 100644 tests/topotests/bgp_rr_ibgp/spine1/bgpd.conf create mode 100644 tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref create mode 100644 tests/topotests/bgp_rr_ibgp/spine1/zebra.conf create mode 100644 tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py create mode 100644 tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf create mode 100644 tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref create mode 100644 tests/topotests/bgp_rr_ibgp/tor1/zebra.conf create mode 100644 tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf create mode 100644 tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref create mode 100644 tests/topotests/bgp_rr_ibgp/tor2/zebra.conf create mode 100644 tests/topotests/bgp_sender_as_path_loop_detection/__init__.py create mode 100644 tests/topotests/bgp_sender_as_path_loop_detection/r1/bgpd.conf create mode 100644 tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf create mode 100644 tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf create mode 100644 tests/topotests/bgp_sender_as_path_loop_detection/r2/zebra.conf create mode 100644 tests/topotests/bgp_sender_as_path_loop_detection/r3/bgpd.conf create mode 100644 tests/topotests/bgp_sender_as_path_loop_detection/r3/zebra.conf create mode 100644 tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py create mode 100644 tests/topotests/bgp_set_aspath_exclude/__init__.py create mode 100644 tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf create mode 100644 tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf create mode 100644 tests/topotests/bgp_set_aspath_exclude/r2/bgpd.conf create mode 100644 tests/topotests/bgp_set_aspath_exclude/r2/zebra.conf create mode 100644 tests/topotests/bgp_set_aspath_exclude/r3/bgpd.conf create mode 100644 tests/topotests/bgp_set_aspath_exclude/r3/zebra.conf create mode 100644 tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py create mode 100644 tests/topotests/bgp_set_aspath_replace/__init__.py create mode 100644 tests/topotests/bgp_set_aspath_replace/r1/bgpd.conf create mode 100644 tests/topotests/bgp_set_aspath_replace/r1/zebra.conf create mode 100644 tests/topotests/bgp_set_aspath_replace/r2/bgpd.conf create mode 100644 tests/topotests/bgp_set_aspath_replace/r2/zebra.conf create mode 100644 tests/topotests/bgp_set_aspath_replace/r3/bgpd.conf create mode 100644 tests/topotests/bgp_set_aspath_replace/r3/zebra.conf create mode 100644 tests/topotests/bgp_set_aspath_replace/test_bgp_set_aspath_replace.py create mode 100644 tests/topotests/bgp_set_local_preference_add_subtract/__init__.py create mode 100644 tests/topotests/bgp_set_local_preference_add_subtract/r1/bgpd.conf create mode 100644 tests/topotests/bgp_set_local_preference_add_subtract/r1/zebra.conf create mode 100644 tests/topotests/bgp_set_local_preference_add_subtract/r2/bgpd.conf create mode 100644 tests/topotests/bgp_set_local_preference_add_subtract/r2/zebra.conf create mode 100644 tests/topotests/bgp_set_local_preference_add_subtract/r3/bgpd.conf create mode 100644 tests/topotests/bgp_set_local_preference_add_subtract/r3/zebra.conf create mode 100644 tests/topotests/bgp_set_local_preference_add_subtract/test_bgp_set_local-preference_add_subtract.py create mode 100644 tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf create mode 100644 tests/topotests/bgp_snmp_bgp4v2mib/r1/zebra.conf create mode 100644 tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf create mode 100644 tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf create mode 100644 tests/topotests/bgp_snmp_bgp4v2mib/r2/zebra.conf create mode 100755 tests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/ce1/bgpd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/ce1/snmpd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/ce1/zebra.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/ce2/bgpd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/ce2/snmpd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/ce2/zebra.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/ce3/bgpd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/ce3/snmpd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/ce3/zebra.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/ce4/bgpd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/ce4/snmpd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/ce4/zebra.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/r1/bgpd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/r1/isisd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/r1/snmpd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/r1/zebra.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/r2/isisd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/r2/snmpd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/r2/zebra.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/r3/isisd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/r3/snmpd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/r3/zebra.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/r4/bgpd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/r4/isisd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/r4/snmpd.conf create mode 100644 tests/topotests/bgp_snmp_mplsl3vpn/r4/zebra.conf create mode 100755 tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py create mode 100644 tests/topotests/bgp_software_version/__init__.py create mode 100644 tests/topotests/bgp_software_version/r1/bgpd.conf create mode 100644 tests/topotests/bgp_software_version/r1/zebra.conf create mode 100644 tests/topotests/bgp_software_version/r2/bgpd.conf create mode 100644 tests/topotests/bgp_software_version/r2/zebra.conf create mode 100644 tests/topotests/bgp_software_version/test_bgp_software_version.py create mode 100644 tests/topotests/bgp_soo/__init__.py create mode 100644 tests/topotests/bgp_soo/cpe1/bgpd.conf create mode 100644 tests/topotests/bgp_soo/cpe1/zebra.conf create mode 100644 tests/topotests/bgp_soo/cpe2/bgpd.conf create mode 100644 tests/topotests/bgp_soo/cpe2/zebra.conf create mode 100644 tests/topotests/bgp_soo/pe1/bgpd.conf create mode 100644 tests/topotests/bgp_soo/pe1/ldpd.conf create mode 100644 tests/topotests/bgp_soo/pe1/ospfd.conf create mode 100644 tests/topotests/bgp_soo/pe1/zebra.conf create mode 100644 tests/topotests/bgp_soo/pe2/bgpd.conf create mode 100644 tests/topotests/bgp_soo/pe2/ldpd.conf create mode 100644 tests/topotests/bgp_soo/pe2/ospfd.conf create mode 100644 tests/topotests/bgp_soo/pe2/zebra.conf create mode 100644 tests/topotests/bgp_soo/test_bgp_soo.py create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/staticd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/staticd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/staticd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/staticd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/staticd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/staticd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/staticd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/staticd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/staticd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/zebra.conf create mode 100755 tests/topotests/bgp_srv6l3vpn_over_ipv6/test_bgp_srv6l3vpn_over_ipv6.py create mode 100644 tests/topotests/bgp_srv6l3vpn_route_leak/ce1/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_route_leak/ce1/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_route_leak/pe1/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/default_ipv4_vpn.json create mode 100644 tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf10_ipv4_unicast.json create mode 100644 tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json create mode 100644 tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4_unicast.json create mode 100644 tests/topotests/bgp_srv6l3vpn_route_leak/pe1/zebra.conf create mode 100755 tests/topotests/bgp_srv6l3vpn_route_leak/test_bgp_srv6l3vpn_route_leak.py create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf create mode 100755 tests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf create mode 100755 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/ip_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/ip_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/ip_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/ip_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/ip_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/ip_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf10_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf20_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf10_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf20_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf create mode 100755 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/test_bgp_srv6l3vpn_to_bgp_vrf2.py create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/ip_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/ip_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/ip_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/ip_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/ip_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/ip_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/ipv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_locator_deleted.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_locator_recreated.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_locator_deleted.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_locator_recreated.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v4_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v4_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_locator_deleted.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_locator_recreated.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_locator_deleted.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_locator_recreated.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v4_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v4_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v6_rib.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/test_bgp_srv6l3vpn_to_bgp_vrf3.py create mode 100644 tests/topotests/bgp_suppress_fib/r1/bgp_ipv4_allowas.json create mode 100644 tests/topotests/bgp_suppress_fib/r1/bgpd.conf create mode 100644 tests/topotests/bgp_suppress_fib/r1/zebra.conf create mode 100644 tests/topotests/bgp_suppress_fib/r2/bgp_ipv4_allowas.json create mode 100644 tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf create mode 100644 tests/topotests/bgp_suppress_fib/r2/bgpd.conf create mode 100644 tests/topotests/bgp_suppress_fib/r2/no_bgp_ipv4_allowas.json create mode 100644 tests/topotests/bgp_suppress_fib/r2/v4_override.json create mode 100644 tests/topotests/bgp_suppress_fib/r2/zebra.conf create mode 100644 tests/topotests/bgp_suppress_fib/r3/bgpd.conf create mode 100644 tests/topotests/bgp_suppress_fib/r3/v4_override.json create mode 100644 tests/topotests/bgp_suppress_fib/r3/v4_route.json create mode 100644 tests/topotests/bgp_suppress_fib/r3/v4_route2.json create mode 100644 tests/topotests/bgp_suppress_fib/r3/v4_route3.json create mode 100644 tests/topotests/bgp_suppress_fib/r3/zebra.conf create mode 100644 tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py create mode 100644 tests/topotests/bgp_tcp_mss/__init__.py create mode 100644 tests/topotests/bgp_tcp_mss/bgp_vrf_tcp_mss.json create mode 100644 tests/topotests/bgp_tcp_mss/r1/bgpd.conf create mode 100644 tests/topotests/bgp_tcp_mss/r1/zebra.conf create mode 100644 tests/topotests/bgp_tcp_mss/r2/bgpd.conf create mode 100644 tests/topotests/bgp_tcp_mss/r2/zebra.conf create mode 100644 tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py create mode 100644 tests/topotests/bgp_tcp_mss/test_bgp_vrf_tcp_mss.py create mode 100644 tests/topotests/bgp_tcp_mss_passive/__init__.py create mode 100644 tests/topotests/bgp_tcp_mss_passive/r1/frr.conf create mode 100644 tests/topotests/bgp_tcp_mss_passive/r2/frr.conf create mode 100644 tests/topotests/bgp_tcp_mss_passive/test_bgp_tcp_mss_passive.py create mode 100644 tests/topotests/bgp_unique_rid/bgp_unique_rid.json create mode 100644 tests/topotests/bgp_unique_rid/bgp_unique_rid_vrf.json create mode 100644 tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py create mode 100644 tests/topotests/bgp_unique_rid/test_bgp_unique_rid_vrf.py create mode 100644 tests/topotests/bgp_unnumbered/__init__.py create mode 100644 tests/topotests/bgp_unnumbered/r1/bgpd.conf create mode 100644 tests/topotests/bgp_unnumbered/r1/zebra.conf create mode 100644 tests/topotests/bgp_unnumbered/r2/bgpd.conf create mode 100644 tests/topotests/bgp_unnumbered/r2/zebra.conf create mode 100644 tests/topotests/bgp_unnumbered/test_bgp_unnumbered.py create mode 100644 tests/topotests/bgp_update_delay/__init__.py create mode 100644 tests/topotests/bgp_update_delay/r1/bgpd.conf create mode 100644 tests/topotests/bgp_update_delay/r1/zebra.conf create mode 100644 tests/topotests/bgp_update_delay/r2/bgpd.conf create mode 100644 tests/topotests/bgp_update_delay/r2/zebra.conf create mode 100644 tests/topotests/bgp_update_delay/r3/bgpd.conf create mode 100644 tests/topotests/bgp_update_delay/r3/zebra.conf create mode 100644 tests/topotests/bgp_update_delay/r4/bgpd.conf create mode 100644 tests/topotests/bgp_update_delay/r4/zebra.conf create mode 100644 tests/topotests/bgp_update_delay/r5/bgpd.conf create mode 100644 tests/topotests/bgp_update_delay/r5/zebra.conf create mode 100644 tests/topotests/bgp_update_delay/test_bgp_update_delay.py create mode 100644 tests/topotests/bgp_vpn_5549_route_map/__init__.py create mode 100644 tests/topotests/bgp_vpn_5549_route_map/cpe1/bgpd.conf create mode 100644 tests/topotests/bgp_vpn_5549_route_map/cpe1/zebra.conf create mode 100644 tests/topotests/bgp_vpn_5549_route_map/cpe2/bgpd.conf create mode 100644 tests/topotests/bgp_vpn_5549_route_map/cpe2/zebra.conf create mode 100644 tests/topotests/bgp_vpn_5549_route_map/pe1/bgpd.conf create mode 100644 tests/topotests/bgp_vpn_5549_route_map/pe1/ldpd.conf create mode 100644 tests/topotests/bgp_vpn_5549_route_map/pe1/ospf6d.conf create mode 100644 tests/topotests/bgp_vpn_5549_route_map/pe1/zebra.conf create mode 100644 tests/topotests/bgp_vpn_5549_route_map/pe2/bgpd.conf create mode 100644 tests/topotests/bgp_vpn_5549_route_map/pe2/ldpd.conf create mode 100644 tests/topotests/bgp_vpn_5549_route_map/pe2/ospf6d.conf create mode 100644 tests/topotests/bgp_vpn_5549_route_map/pe2/zebra.conf create mode 100644 tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py create mode 100644 tests/topotests/bgp_vpnv4_asbr/__init__.py create mode 100644 tests/topotests/bgp_vpnv4_asbr/h1/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_asbr/h2/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_asbr/h3/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_asbr/r1/bgp_ipv4_routes.json create mode 100644 tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_asbr/r1/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_asbr/r2/ipv4_vpn_summary.json create mode 100644 tests/topotests/bgp_vpnv4_asbr/r2/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_asbr/r3/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_asbr/rr100/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_asbr/rr100/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_asbr/rs200/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_asbr/rs200/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py create mode 100644 tests/topotests/bgp_vpnv4_ebgp/__init__.py create mode 100644 tests/topotests/bgp_vpnv4_ebgp/r1/bgp_ipv4_routes.json create mode 100644 tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json create mode 100644 tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json create mode 100644 tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_ebgp/r3/bgp_ipv4_routes.json create mode 100644 tests/topotests/bgp_vpnv4_ebgp/r3/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_ebgp/r3/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py create mode 100644 tests/topotests/bgp_vpnv4_gre/__init__.py create mode 100644 tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_gre/r1/ipv4_routes.json create mode 100644 tests/topotests/bgp_vpnv4_gre/r1/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_gre/r2/bgp_ipv4_routes.json create mode 100644 tests/topotests/bgp_vpnv4_gre/r2/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_gre/r2/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py create mode 100644 tests/topotests/bgp_vpnv4_noretain/__init__.py create mode 100644 tests/topotests/bgp_vpnv4_noretain/r1/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_all.json create mode 100644 tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init.json create mode 100644 tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json create mode 100644 tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json create mode 100644 tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_init.json create mode 100644 tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r1_vrf1.json create mode 100644 tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf2.json create mode 100644 tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf3.json create mode 100644 tests/topotests/bgp_vpnv4_noretain/r1/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_noretain/r2/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_routes_all.json create mode 100644 tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_summary.json create mode 100644 tests/topotests/bgp_vpnv4_noretain/r2/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/__init__.py create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgp_ipv4_routes_vrf1.json create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/r1/ipv4_routes.json create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/r1/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/r11/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/r11/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/r12/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/r12/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/r13/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/r13/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_ipv4_routes.json create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_vpnv4_routes.json create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/r2/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/rr/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/rr/zebra.conf create mode 100644 tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/__init__.py create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/r1/zebra.conf create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/r11/zebra.conf create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/r12/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/r12/zebra.conf create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/r13/zebra.conf create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgp_vpnv6_routes.json create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/r2/zebra.conf create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/rr/bgpd.conf create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/rr/zebra.conf create mode 100644 tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py create mode 100644 tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json create mode 100644 tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json create mode 100644 tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py create mode 100644 tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py create mode 100644 tests/topotests/bgp_vrf_dynamic_route_leak_topo3/bgp_vrf_dynamic_route_leak_topo3.json create mode 100644 tests/topotests/bgp_vrf_dynamic_route_leak_topo3/test_bgp_vrf_dynamic_route_leak_topo3.py create mode 100644 tests/topotests/bgp_vrf_dynamic_route_leak_topo4/bgp_vrf_dynamic_route_leak_topo4.json create mode 100644 tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-1.py create mode 100644 tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-2.py create mode 100644 tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-3.py create mode 100644 tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf create mode 100644 tests/topotests/bgp_vrf_leaking_5549_routes/ce1/zebra.conf create mode 100644 tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf create mode 100644 tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/default_ipv4_vpn.json create mode 100644 tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf10_ipv4_unicast.json create mode 100644 tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf20_ipv4_unicast.json create mode 100644 tests/topotests/bgp_vrf_leaking_5549_routes/pe1/zebra.conf create mode 100755 tests/topotests/bgp_vrf_leaking_5549_routes/test_bgp_vrf_leaking.py create mode 100644 tests/topotests/bgp_vrf_leaking_rt_change_route_maps/__init__.py create mode 100644 tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/bgpd.conf create mode 100644 tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/zebra.conf create mode 100644 tests/topotests/bgp_vrf_leaking_rt_change_route_maps/test_bgp_vrf_leaking_rt_change_route_maps.py create mode 100644 tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo1.json create mode 100644 tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo2.json create mode 100644 tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo1.py create mode 100644 tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo2.py create mode 100644 tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py create mode 100644 tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf create mode 100644 tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json create mode 100644 tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json create mode 100644 tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf create mode 100644 tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf create mode 100644 tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json create mode 100644 tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json create mode 100644 tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf create mode 100644 tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot create mode 100644 tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py create mode 100644 tests/topotests/bgp_vrf_md5_peering/__init__.py create mode 100644 tests/topotests/bgp_vrf_md5_peering/exabgp.env create mode 100644 tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg create mode 100644 tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf create mode 100644 tests/topotests/bgp_vrf_md5_peering/r1/zebra.conf create mode 100644 tests/topotests/bgp_vrf_md5_peering/test_bgp_vrf_md5_peering.py create mode 100644 tests/topotests/bgp_vrf_netns/__init__.py create mode 100644 tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.dot create mode 100644 tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.pdf create mode 100644 tests/topotests/bgp_vrf_netns/exabgp.env create mode 100755 tests/topotests/bgp_vrf_netns/peer1/exa-send.py create mode 100644 tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg create mode 100644 tests/topotests/bgp_vrf_netns/r1/bgpd.conf create mode 100644 tests/topotests/bgp_vrf_netns/r1/summary.txt create mode 100644 tests/topotests/bgp_vrf_netns/r1/summary20.txt create mode 100644 tests/topotests/bgp_vrf_netns/r1/zebra.conf create mode 100644 tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py create mode 100644 tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf create mode 100644 tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf create mode 100644 tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs create mode 100644 tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py create mode 100644 tests/topotests/config_timing/r1/staticd.conf create mode 100644 tests/topotests/config_timing/r1/zebra.conf create mode 100644 tests/topotests/config_timing/test_config_timing.py create mode 100755 tests/topotests/conftest.py create mode 100644 tests/topotests/cspf_topo1/r1/isisd.conf create mode 100644 tests/topotests/cspf_topo1/r1/sharpd.conf create mode 100644 tests/topotests/cspf_topo1/r1/zebra.conf create mode 100644 tests/topotests/cspf_topo1/r2/isisd.conf create mode 100644 tests/topotests/cspf_topo1/r2/zebra.conf create mode 100644 tests/topotests/cspf_topo1/r3/isisd.conf create mode 100644 tests/topotests/cspf_topo1/r3/zebra.conf create mode 100644 tests/topotests/cspf_topo1/r4/isisd.conf create mode 100644 tests/topotests/cspf_topo1/r4/zebra.conf create mode 100644 tests/topotests/cspf_topo1/reference/cspf-failed-dst.txt create mode 100644 tests/topotests/cspf_topo1/reference/cspf-failed-same.txt create mode 100644 tests/topotests/cspf_topo1/reference/cspf-failed-src.txt create mode 100644 tests/topotests/cspf_topo1/reference/cspf-failed.txt create mode 100644 tests/topotests/cspf_topo1/reference/cspf-ipv4-delay.txt create mode 100644 tests/topotests/cspf_topo1/reference/cspf-ipv4-metric.txt create mode 100644 tests/topotests/cspf_topo1/reference/cspf-ipv4-te-metric.txt create mode 100644 tests/topotests/cspf_topo1/reference/cspf-ipv6-delay.txt create mode 100644 tests/topotests/cspf_topo1/reference/cspf-ipv6-metric.txt create mode 100644 tests/topotests/cspf_topo1/reference/cspf-ipv6-te-metric.txt create mode 100644 tests/topotests/cspf_topo1/reference/sharp-ted.json create mode 100644 tests/topotests/cspf_topo1/test_cspf_topo1.py create mode 100644 tests/topotests/docker/README.md create mode 100755 tests/topotests/docker/build.sh create mode 100755 tests/topotests/docker/frr-topotests.sh create mode 100755 tests/topotests/docker/inner/compile_frr.sh create mode 100755 tests/topotests/docker/inner/entrypoint.sh create mode 100755 tests/topotests/docker/inner/funcs.sh create mode 100644 tests/topotests/docker/inner/motd.txt create mode 100755 tests/topotests/docker/inner/openvswitch.sh create mode 100644 tests/topotests/eigrp_topo1/r1/eigrpd.conf create mode 100644 tests/topotests/eigrp_topo1/r1/show_ip_eigrp.json create mode 100644 tests/topotests/eigrp_topo1/r1/show_ip_eigrp.ref create mode 100644 tests/topotests/eigrp_topo1/r1/show_ip_route.json_ref create mode 100644 tests/topotests/eigrp_topo1/r1/zebra.conf create mode 100644 tests/topotests/eigrp_topo1/r2/eigrpd.conf create mode 100644 tests/topotests/eigrp_topo1/r2/show_ip_eigrp.json create mode 100644 tests/topotests/eigrp_topo1/r2/show_ip_eigrp.ref create mode 100644 tests/topotests/eigrp_topo1/r2/show_ip_route.json_ref create mode 100644 tests/topotests/eigrp_topo1/r2/zebra.conf create mode 100644 tests/topotests/eigrp_topo1/r3/eigrpd.conf create mode 100644 tests/topotests/eigrp_topo1/r3/show_ip_eigrp.json create mode 100644 tests/topotests/eigrp_topo1/r3/show_ip_eigrp.ref create mode 100644 tests/topotests/eigrp_topo1/r3/show_ip_route.json_ref create mode 100644 tests/topotests/eigrp_topo1/r3/zebra.conf create mode 100644 tests/topotests/eigrp_topo1/test_eigrp_topo1.dot create mode 100644 tests/topotests/eigrp_topo1/test_eigrp_topo1.py create mode 100644 tests/topotests/evpn_pim_1/host1/bgpd.conf create mode 100644 tests/topotests/evpn_pim_1/host1/pimd.conf create mode 100644 tests/topotests/evpn_pim_1/host1/zebra.conf create mode 100644 tests/topotests/evpn_pim_1/host2/bgpd.conf create mode 100644 tests/topotests/evpn_pim_1/host2/pimd.conf create mode 100644 tests/topotests/evpn_pim_1/host2/zebra.conf create mode 100644 tests/topotests/evpn_pim_1/leaf1/bgpd.conf create mode 100644 tests/topotests/evpn_pim_1/leaf1/pimd.conf create mode 100644 tests/topotests/evpn_pim_1/leaf1/zebra.conf create mode 100644 tests/topotests/evpn_pim_1/leaf2/bgpd.conf create mode 100644 tests/topotests/evpn_pim_1/leaf2/pimd.conf create mode 100644 tests/topotests/evpn_pim_1/leaf2/zebra.conf create mode 100644 tests/topotests/evpn_pim_1/spine/bgp.summ.json create mode 100644 tests/topotests/evpn_pim_1/spine/bgpd.conf create mode 100644 tests/topotests/evpn_pim_1/spine/join-info.json create mode 100644 tests/topotests/evpn_pim_1/spine/pimd.conf create mode 100644 tests/topotests/evpn_pim_1/spine/zebra.conf create mode 100644 tests/topotests/evpn_pim_1/test_evpn_pim_topo1.py create mode 100644 tests/topotests/evpn_type5_test_topo1/__init__.py create mode 100644 tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json create mode 100644 tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json create mode 100644 tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py create mode 100644 tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py create mode 100644 tests/topotests/example_munet/munet.yaml create mode 100644 tests/topotests/example_munet/r1/daemons create mode 100644 tests/topotests/example_munet/r1/frr.conf create mode 100644 tests/topotests/example_munet/r1/vtysh.conf create mode 100644 tests/topotests/example_munet/r2/daemons create mode 100644 tests/topotests/example_munet/r2/frr.conf create mode 100644 tests/topotests/example_munet/r2/vtysh.conf create mode 100644 tests/topotests/example_munet/r3/daemons create mode 100644 tests/topotests/example_munet/r3/frr.conf create mode 100644 tests/topotests/example_munet/r3/vtysh.conf create mode 100644 tests/topotests/example_munet/test_munet.py create mode 100755 tests/topotests/example_test/__init__.py create mode 100644 tests/topotests/example_test/r1/zebra.conf create mode 100644 tests/topotests/example_test/r2/zebra.conf create mode 100755 tests/topotests/example_test/test_example.py create mode 100644 tests/topotests/example_test/test_template.dot create mode 100644 tests/topotests/example_test/test_template.jpg create mode 100644 tests/topotests/example_test/test_template.py create mode 100644 tests/topotests/example_test/test_template_json.json create mode 100644 tests/topotests/example_test/test_template_json.py create mode 100755 tests/topotests/example_topojson_test/__init__.py create mode 100755 tests/topotests/example_topojson_test/test_topo_json_multiple_links/__init__.py create mode 100644 tests/topotests/example_topojson_test/test_topo_json_multiple_links/example_topojson_multiple_links.json create mode 100755 tests/topotests/example_topojson_test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py create mode 100755 tests/topotests/example_topojson_test/test_topo_json_single_link/__init__.py create mode 100644 tests/topotests/example_topojson_test/test_topo_json_single_link/example_topojson.json create mode 100755 tests/topotests/example_topojson_test/test_topo_json_single_link/test_example_topojson.py create mode 100755 tests/topotests/example_topojson_test/test_topo_json_single_link_loopback/__init__.py create mode 100644 tests/topotests/example_topojson_test/test_topo_json_single_link_loopback/example_topojson.json create mode 100755 tests/topotests/example_topojson_test/test_topo_json_single_link_loopback/test_example_topojson.py create mode 120000 tests/topotests/grpc_basic/lib create mode 100644 tests/topotests/grpc_basic/r1/zebra.conf create mode 100644 tests/topotests/grpc_basic/r2/zebra.conf create mode 100644 tests/topotests/grpc_basic/test_basic_grpc.py create mode 100644 tests/topotests/isis_advertise_high_metrics/__init__.py create mode 100644 tests/topotests/isis_advertise_high_metrics/r1/isisd.conf create mode 100644 tests/topotests/isis_advertise_high_metrics/r1/zebra.conf create mode 100644 tests/topotests/isis_advertise_high_metrics/r2/isisd.conf create mode 100644 tests/topotests/isis_advertise_high_metrics/r2/zebra.conf create mode 100644 tests/topotests/isis_advertise_high_metrics/r3/isisd.conf create mode 100644 tests/topotests/isis_advertise_high_metrics/r3/zebra.conf create mode 100644 tests/topotests/isis_advertise_high_metrics/r4/isisd.conf create mode 100644 tests/topotests/isis_advertise_high_metrics/r4/zebra.conf create mode 100644 tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py create mode 100644 tests/topotests/isis_lfa_topo1/__init__.py create mode 100644 tests/topotests/isis_lfa_topo1/rt1/bfdd.conf create mode 100644 tests/topotests/isis_lfa_topo1/rt1/isisd.conf create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step10/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step11/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step12/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step13/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step14/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step15/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step16/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step2/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step3/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step4/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step5/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step6/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step7/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step8/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_lfa_topo1/rt1/step9/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_lfa_topo1/rt1/zebra.conf create mode 100644 tests/topotests/isis_lfa_topo1/rt2/bfdd.conf create mode 100644 tests/topotests/isis_lfa_topo1/rt2/isisd.conf create mode 100644 tests/topotests/isis_lfa_topo1/rt2/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lfa_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_lfa_topo1/rt2/zebra.conf create mode 100644 tests/topotests/isis_lfa_topo1/rt3/isisd.conf create mode 100644 tests/topotests/isis_lfa_topo1/rt3/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lfa_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_lfa_topo1/rt3/zebra.conf create mode 100644 tests/topotests/isis_lfa_topo1/rt4/isisd.conf create mode 100644 tests/topotests/isis_lfa_topo1/rt4/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lfa_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_lfa_topo1/rt4/zebra.conf create mode 100644 tests/topotests/isis_lfa_topo1/rt5/isisd.conf create mode 100644 tests/topotests/isis_lfa_topo1/rt5/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lfa_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_lfa_topo1/rt5/zebra.conf create mode 100644 tests/topotests/isis_lfa_topo1/rt6/isisd.conf create mode 100644 tests/topotests/isis_lfa_topo1/rt6/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lfa_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_lfa_topo1/rt6/zebra.conf create mode 100644 tests/topotests/isis_lfa_topo1/rt7/isisd.conf create mode 100644 tests/topotests/isis_lfa_topo1/rt7/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lfa_topo1/rt7/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_lfa_topo1/rt7/zebra.conf create mode 100755 tests/topotests/isis_lfa_topo1/test_isis_lfa_topo1.py create mode 100644 tests/topotests/isis_lsp_bits_topo1/__init__.py create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt1/isisd.conf create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt1/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt1/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt1/step2/show_ip_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt1/step2/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt1/step3/show_ip_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt1/step3/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt1/step4/show_ip_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt1/step4/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt1/zebra.conf create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt2/isisd.conf create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt2/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt2/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt2/zebra.conf create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt3/isisd.conf create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt3/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt3/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt3/zebra.conf create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt4/isisd.conf create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt4/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt4/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt4/zebra.conf create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt5/isisd.conf create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt5/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt5/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt5/zebra.conf create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt6/isisd.conf create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt6/step2/show_ip_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt6/step2/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt6/step3/show_ip_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt6/step3/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt6/step4/show_ip_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt6/step4/show_ipv6_route.ref create mode 100644 tests/topotests/isis_lsp_bits_topo1/rt6/zebra.conf create mode 100755 tests/topotests/isis_lsp_bits_topo1/test_isis_lsp_bits_topo1.py create mode 100644 tests/topotests/isis_rlfa_topo1/__init__.py create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/isisd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/ldpd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step10/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step10/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step2/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step2/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step3/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step3/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step4/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step4/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step5/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step5/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step6/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step6/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step7/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step7/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step8/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step8/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step9/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/step9/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_rlfa_topo1/rt1/zebra.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt2/isisd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt2/ldpd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt2/zebra.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt3/isisd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt3/ldpd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt3/zebra.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt4/isisd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt4/ldpd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt4/zebra.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt5/isisd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt5/ldpd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt5/zebra.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt6/isisd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt6/ldpd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt6/zebra.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt7/isisd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt7/ldpd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt7/zebra.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt8/isisd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt8/ldpd.conf create mode 100644 tests/topotests/isis_rlfa_topo1/rt8/zebra.conf create mode 100755 tests/topotests/isis_rlfa_topo1/test_isis_rlfa_topo1.py create mode 100644 tests/topotests/isis_snmp/ce3/zebra.conf create mode 100644 tests/topotests/isis_snmp/r1/isisd.conf create mode 100644 tests/topotests/isis_snmp/r1/ldpd.conf create mode 100644 tests/topotests/isis_snmp/r1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_snmp/r1/snmpd.conf create mode 100644 tests/topotests/isis_snmp/r1/zebra.conf create mode 100644 tests/topotests/isis_snmp/r2/isisd.conf create mode 100644 tests/topotests/isis_snmp/r2/ldpd.conf create mode 100644 tests/topotests/isis_snmp/r2/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_snmp/r2/snmpd.conf create mode 100644 tests/topotests/isis_snmp/r2/zebra.conf create mode 100644 tests/topotests/isis_snmp/r3/isisd.conf create mode 100644 tests/topotests/isis_snmp/r3/ldpd.conf create mode 100644 tests/topotests/isis_snmp/r3/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_snmp/r3/snmpd.conf create mode 100644 tests/topotests/isis_snmp/r3/zebra.conf create mode 100644 tests/topotests/isis_snmp/r4/isisd.conf create mode 100644 tests/topotests/isis_snmp/r4/ldpd.conf create mode 100644 tests/topotests/isis_snmp/r4/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_snmp/r4/snmpd.conf create mode 100644 tests/topotests/isis_snmp/r4/zebra.conf create mode 100644 tests/topotests/isis_snmp/r5/isisd.conf create mode 100644 tests/topotests/isis_snmp/r5/ldpd.conf create mode 100644 tests/topotests/isis_snmp/r5/ldpdconf create mode 100644 tests/topotests/isis_snmp/r5/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_snmp/r5/snmpd.conf create mode 100644 tests/topotests/isis_snmp/r5/zebra.conf create mode 100644 tests/topotests/isis_snmp/test_isis_snmp.dot create mode 100755 tests/topotests/isis_snmp/test_isis_snmp.py create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf create mode 100755 tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/README.md create mode 100755 tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json create mode 100644 tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf create mode 100755 tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py create mode 100644 tests/topotests/isis_sr_te_topo1/dst/zebra.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/bgpd.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/isisd.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/pathd.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/step1/show_mpls_table_with_candidate.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/step1/show_mpls_table_without_candidate.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/step2/show_operational_data.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/step2/show_operational_data_with_candidate.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/step3/show_operational_data_with_single_candidate.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/step3/show_operational_data_with_two_candidates.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/step4/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/step4/show_mpls_table_add_segment.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/step4/show_mpls_table_change_segment.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/step5/show_ip_route_bgp_active_srte.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/step5/show_ip_route_bgp_empty.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/step5/show_operational_data_active.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/step5/show_operational_data_inactive.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt1/zebra.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt2/isisd.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt2/zebra.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt3/isisd.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt3/zebra.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt4/isisd.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt4/zebra.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt5/isisd.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt5/zebra.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt6/bgpd.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt6/isisd.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt6/pathd.conf create mode 100644 tests/topotests/isis_sr_te_topo1/rt6/step1/show_mpls_table_with_candidate.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt6/step1/show_mpls_table_without_candidate.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt6/step2/show_operational_data.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt6/step2/show_operational_data_with_candidate.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt6/step3/show_operational_data_with_single_candidate.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt6/step3/show_operational_data_with_two_candidates.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt6/step4/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_te_topo1/rt6/zebra.conf create mode 100755 tests/topotests/isis_sr_te_topo1/test_isis_sr_te_topo1.py create mode 100644 tests/topotests/isis_sr_topo1/__init__.py create mode 100644 tests/topotests/isis_sr_topo1/rt1/isisd.conf create mode 100644 tests/topotests/isis_sr_topo1/rt1/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step1/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step10/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step10/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step10/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt1/step10/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step2/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step2/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step2/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt1/step2/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step3/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step3/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step3/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt1/step3/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step4/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step4/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step4/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt1/step4/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step5/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step5/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step5/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt1/step5/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step6/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step6/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step6/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt1/step6/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step7/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step7/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step7/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt1/step7/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step8/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step8/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step8/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt1/step8/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step9/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step9/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/step9/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt1/step9/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt1/zebra.conf create mode 100644 tests/topotests/isis_sr_topo1/rt2/isisd.conf create mode 100644 tests/topotests/isis_sr_topo1/rt2/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step1/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step10/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step10/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step10/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt2/step10/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step2/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step2/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step2/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt2/step2/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step3/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step3/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step3/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt2/step3/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step4/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step4/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step4/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt2/step4/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step5/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step5/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step5/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt2/step5/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step6/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step6/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step6/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt2/step6/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step7/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step7/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step7/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt2/step7/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step8/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step8/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step8/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt2/step8/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step9/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step9/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/step9/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt2/step9/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt2/zebra.conf create mode 100644 tests/topotests/isis_sr_topo1/rt3/isisd.conf create mode 100644 tests/topotests/isis_sr_topo1/rt3/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step1/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step10/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step10/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step10/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt3/step10/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step2/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step2/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step2/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt3/step2/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step3/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step3/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step3/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt3/step3/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step4/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step4/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step4/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt3/step4/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step5/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step5/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step5/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt3/step5/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step6/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step6/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step6/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt3/step6/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step7/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step7/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step7/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt3/step7/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step8/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step8/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step8/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt3/step8/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step9/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step9/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/step9/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt3/step9/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt3/zebra.conf create mode 100644 tests/topotests/isis_sr_topo1/rt4/isisd.conf create mode 100644 tests/topotests/isis_sr_topo1/rt4/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step1/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step10/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step10/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step10/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt4/step10/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step2/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step2/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step2/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step2/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step3/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step3/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step3/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step3/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step4/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step4/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step4/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt4/step4/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step5/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step5/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step5/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt4/step5/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step6/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step6/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step6/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt4/step6/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step7/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step7/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step7/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt4/step7/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step8/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step8/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step8/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt4/step8/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step9/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step9/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/step9/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt4/step9/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt4/zebra.conf create mode 100644 tests/topotests/isis_sr_topo1/rt5/isisd.conf create mode 100644 tests/topotests/isis_sr_topo1/rt5/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step1/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step10/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step10/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step10/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt5/step10/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step2/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step2/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step2/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step2/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step3/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step3/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step3/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step3/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step4/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step4/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step4/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step4/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step5/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step5/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step5/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt5/step5/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step6/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step6/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step6/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt5/step6/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step7/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step7/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step7/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt5/step7/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step8/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step8/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step8/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt5/step8/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step9/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step9/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/step9/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt5/step9/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt5/zebra.conf create mode 100644 tests/topotests/isis_sr_topo1/rt6/isisd.conf create mode 100644 tests/topotests/isis_sr_topo1/rt6/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step1/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step10/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step10/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step10/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt6/step10/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step2/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step2/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step2/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt6/step2/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step3/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step3/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step3/show_mpls_table.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step3/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step4/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step4/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step4/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt6/step4/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step5/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step5/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step5/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt6/step5/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step6/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step6/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step6/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt6/step6/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step7/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step7/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step7/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt6/step7/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step8/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step8/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step8/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt6/step8/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step9/show_ip_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step9/show_ipv6_route.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/step9/show_mpls_table.ref create mode 120000 tests/topotests/isis_sr_topo1/rt6/step9/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_sr_topo1/rt6/zebra.conf create mode 100644 tests/topotests/isis_sr_topo1/test_isis_sr_topo1.py create mode 100644 tests/topotests/isis_srv6_topo1/dst/sharpd.conf create mode 100644 tests/topotests/isis_srv6_topo1/dst/zebra.conf create mode 100644 tests/topotests/isis_srv6_topo1/rt1/isisd.conf create mode 100644 tests/topotests/isis_srv6_topo1/rt1/sharpd.conf create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step1/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step2/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step2/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step2/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step2/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step3/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step3/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step3/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step3/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step4/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step4/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step4/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step4/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step5/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step5/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step5/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step5/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step6/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step6/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step6/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step6/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step7/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step7/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step7/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step7/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step8/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step8/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step8/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step8/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step9/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step9/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step9/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/step9/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt1/zebra.conf create mode 100644 tests/topotests/isis_srv6_topo1/rt2/isisd.conf create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step1/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step2/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step2/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step2/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step2/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step3/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step3/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step3/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step3/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step4/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step4/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step4/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step4/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step5/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step5/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step5/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step5/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step6/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step6/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step6/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step6/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step7/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step7/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step7/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step7/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step8/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step8/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step8/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step8/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step9/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step9/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step9/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/step9/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt2/zebra.conf create mode 100644 tests/topotests/isis_srv6_topo1/rt3/isisd.conf create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step1/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step2/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step2/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step2/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step2/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step3/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step3/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step3/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step3/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step4/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step4/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step4/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step4/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step5/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step5/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step5/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step5/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step6/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step6/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step6/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step6/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step7/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step7/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step7/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step7/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step8/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step8/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step8/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step8/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step9/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step9/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step9/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/step9/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt3/zebra.conf create mode 100644 tests/topotests/isis_srv6_topo1/rt4/isisd.conf create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step1/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step2/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step2/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step2/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step2/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step3/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step3/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step3/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step3/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step4/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step4/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step4/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step4/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step5/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step5/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step5/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step5/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step6/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step6/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step6/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step6/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step7/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step7/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step7/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step7/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step8/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step8/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step8/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step8/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step9/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step9/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step9/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/step9/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt4/zebra.conf create mode 100644 tests/topotests/isis_srv6_topo1/rt5/isisd.conf create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step1/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step2/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step2/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step2/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step2/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step3/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step3/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step3/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step3/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step4/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step4/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step4/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step4/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step5/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step5/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step5/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step5/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step6/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step6/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step6/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step6/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step7/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step7/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step7/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step7/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step8/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step8/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step8/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step8/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step9/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step9/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step9/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/step9/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt5/zebra.conf create mode 100644 tests/topotests/isis_srv6_topo1/rt6/isisd.conf create mode 100644 tests/topotests/isis_srv6_topo1/rt6/sharpd.conf create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step1/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step2/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step2/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step2/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step2/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step3/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step3/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step3/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step3/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step4/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step4/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step4/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step4/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step5/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step5/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step5/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step5/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step6/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step6/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step6/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step6/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step7/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step7/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step7/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step7/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step8/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step8/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step8/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step8/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step9/show_ip_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step9/show_ipv6_route.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step9/show_srv6_locator_table.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/step9/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_srv6_topo1/rt6/zebra.conf create mode 100644 tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py create mode 100755 tests/topotests/isis_te_topo1/__init__.py create mode 100644 tests/topotests/isis_te_topo1/r1/isisd.conf create mode 100644 tests/topotests/isis_te_topo1/r1/zebra.conf create mode 100644 tests/topotests/isis_te_topo1/r2/isisd.conf create mode 100644 tests/topotests/isis_te_topo1/r2/zebra.conf create mode 100644 tests/topotests/isis_te_topo1/r3/isisd.conf create mode 100644 tests/topotests/isis_te_topo1/r3/zebra.conf create mode 100644 tests/topotests/isis_te_topo1/r4/isisd.conf create mode 100644 tests/topotests/isis_te_topo1/r4/zebra.conf create mode 100644 tests/topotests/isis_te_topo1/reference/ted_step1.json create mode 100644 tests/topotests/isis_te_topo1/reference/ted_step10.json create mode 100644 tests/topotests/isis_te_topo1/reference/ted_step2.json create mode 100644 tests/topotests/isis_te_topo1/reference/ted_step3.json create mode 100644 tests/topotests/isis_te_topo1/reference/ted_step4.json create mode 100644 tests/topotests/isis_te_topo1/reference/ted_step5.json create mode 100644 tests/topotests/isis_te_topo1/reference/ted_step6.json create mode 100644 tests/topotests/isis_te_topo1/reference/ted_step7.json create mode 100644 tests/topotests/isis_te_topo1/reference/ted_step8.json create mode 120000 tests/topotests/isis_te_topo1/reference/ted_step9.json create mode 100644 tests/topotests/isis_te_topo1/test_isis_te_topo1.py create mode 100644 tests/topotests/isis_tilfa_topo1/__init__.py create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/isisd.conf create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step1/show_mpls_table.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step10/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step10/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step10/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step11/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step11/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step11/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step12/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step12/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step12/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step2/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step2/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step2/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step3/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step3/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step3/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step4/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step4/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step4/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step5/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step5/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step5/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step6/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step6/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step6/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step7/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step7/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step7/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step8/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step8/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step8/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step9/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step9/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/step9/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt1/zebra.conf create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/isisd.conf create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step1/show_mpls_table.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step10/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step10/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step10/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step11/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step11/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step11/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step12/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step12/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step12/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step2/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step2/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step2/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step3/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step3/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step3/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step4/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step4/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step4/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step5/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step5/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step5/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step6/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step6/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step6/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step7/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step7/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step7/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step8/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step8/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step8/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step9/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step9/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/step9/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt2/zebra.conf create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/isisd.conf create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step1/show_mpls_table.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step10/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step10/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step10/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step11/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step11/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step11/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step12/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step12/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step12/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step2/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step2/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step2/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step3/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step3/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step3/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step4/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step4/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step4/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step5/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step5/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step5/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step6/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step6/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step6/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step7/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step7/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step7/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step8/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step8/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step8/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step9/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step9/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/step9/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt3/zebra.conf create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/isisd.conf create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step1/show_mpls_table.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step10/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step10/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step10/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step11/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step11/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step11/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step12/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step12/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step12/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step2/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step2/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step2/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step3/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step3/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step3/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step4/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step4/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step4/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step5/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step5/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step5/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step6/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step6/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step6/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step7/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step7/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step7/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step8/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step8/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step8/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step9/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step9/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/step9/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt4/zebra.conf create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/bfdd.conf create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/isisd.conf create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step1/show_mpls_table.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step10/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step10/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step10/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step11/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step11/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step11/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step12/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step12/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step12/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step2/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step2/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step2/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step3/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step3/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step3/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step4/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step4/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step4/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step5/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step5/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step5/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step6/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step6/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step6/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step7/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step7/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step7/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step8/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step8/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step8/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step9/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step9/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/step9/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt5/zebra.conf create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/bfdd.conf create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/isisd.conf create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step1/show_ip_route.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step1/show_ipv6_route.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step1/show_mpls_table.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step10/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step10/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step10/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step11/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step11/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step11/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step12/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step12/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step12/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step2/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step2/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step2/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step3/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step3/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step3/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step4/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step4/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step4/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step5/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step5/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step5/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step6/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step6/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step6/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step7/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step7/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step7/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step8/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step8/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step8/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step9/show_ip_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step9/show_ipv6_route.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/step9/show_mpls_table.ref.diff create mode 100644 tests/topotests/isis_tilfa_topo1/rt6/zebra.conf create mode 100755 tests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py create mode 100644 tests/topotests/isis_topo1/__init__.py create mode 100644 tests/topotests/isis_topo1/r1/isisd.conf create mode 100644 tests/topotests/isis_topo1/r1/r1_route.json create mode 100644 tests/topotests/isis_topo1/r1/r1_route6.json create mode 100644 tests/topotests/isis_topo1/r1/r1_route6_linux.json create mode 100644 tests/topotests/isis_topo1/r1/r1_route_linux.json create mode 100644 tests/topotests/isis_topo1/r1/r1_topology.json create mode 100644 tests/topotests/isis_topo1/r1/zebra.conf create mode 100644 tests/topotests/isis_topo1/r2/isisd.conf create mode 100644 tests/topotests/isis_topo1/r2/r2_route.json create mode 100644 tests/topotests/isis_topo1/r2/r2_route6.json create mode 100644 tests/topotests/isis_topo1/r2/r2_route6_linux.json create mode 100644 tests/topotests/isis_topo1/r2/r2_route_linux.json create mode 100644 tests/topotests/isis_topo1/r2/r2_topology.json create mode 100644 tests/topotests/isis_topo1/r2/zebra.conf create mode 100644 tests/topotests/isis_topo1/r3/isisd.conf create mode 100644 tests/topotests/isis_topo1/r3/r3_route.json create mode 100644 tests/topotests/isis_topo1/r3/r3_route6.json create mode 100644 tests/topotests/isis_topo1/r3/r3_route6_linux.json create mode 100644 tests/topotests/isis_topo1/r3/r3_route_linux.json create mode 100644 tests/topotests/isis_topo1/r3/r3_topology.json create mode 100644 tests/topotests/isis_topo1/r3/zebra.conf create mode 100644 tests/topotests/isis_topo1/r4/isisd.conf create mode 100644 tests/topotests/isis_topo1/r4/r4_route.json create mode 100644 tests/topotests/isis_topo1/r4/r4_route6.json create mode 100644 tests/topotests/isis_topo1/r4/r4_route6_linux.json create mode 100644 tests/topotests/isis_topo1/r4/r4_route_linux.json create mode 100644 tests/topotests/isis_topo1/r4/r4_topology.json create mode 100644 tests/topotests/isis_topo1/r4/zebra.conf create mode 100644 tests/topotests/isis_topo1/r5/isisd.conf create mode 100644 tests/topotests/isis_topo1/r5/r5_route.json create mode 100644 tests/topotests/isis_topo1/r5/r5_route6.json create mode 100644 tests/topotests/isis_topo1/r5/r5_route6_linux.json create mode 100644 tests/topotests/isis_topo1/r5/r5_route_linux.json create mode 100644 tests/topotests/isis_topo1/r5/r5_topology.json create mode 100644 tests/topotests/isis_topo1/r5/zebra.conf create mode 100644 tests/topotests/isis_topo1/test_isis_topo1.dot create mode 100644 tests/topotests/isis_topo1/test_isis_topo1.jpg create mode 100644 tests/topotests/isis_topo1/test_isis_topo1.py create mode 100644 tests/topotests/isis_topo1_vrf/__init__.py create mode 100755 tests/topotests/isis_topo1_vrf/r1/isisd.conf create mode 100644 tests/topotests/isis_topo1_vrf/r1/r1_route.json create mode 100644 tests/topotests/isis_topo1_vrf/r1/r1_route6.json create mode 100755 tests/topotests/isis_topo1_vrf/r1/r1_route6_linux.json create mode 100755 tests/topotests/isis_topo1_vrf/r1/r1_route_linux.json create mode 100644 tests/topotests/isis_topo1_vrf/r1/r1_topology.json create mode 100755 tests/topotests/isis_topo1_vrf/r1/zebra.conf create mode 100755 tests/topotests/isis_topo1_vrf/r2/isisd.conf create mode 100644 tests/topotests/isis_topo1_vrf/r2/r2_route.json create mode 100644 tests/topotests/isis_topo1_vrf/r2/r2_route6.json create mode 100755 tests/topotests/isis_topo1_vrf/r2/r2_route6_linux.json create mode 100755 tests/topotests/isis_topo1_vrf/r2/r2_route_linux.json create mode 100644 tests/topotests/isis_topo1_vrf/r2/r2_topology.json create mode 100755 tests/topotests/isis_topo1_vrf/r2/zebra.conf create mode 100755 tests/topotests/isis_topo1_vrf/r3/isisd.conf create mode 100644 tests/topotests/isis_topo1_vrf/r3/r3_route.json create mode 100644 tests/topotests/isis_topo1_vrf/r3/r3_route6.json create mode 100755 tests/topotests/isis_topo1_vrf/r3/r3_route6_linux.json create mode 100755 tests/topotests/isis_topo1_vrf/r3/r3_route_linux.json create mode 100644 tests/topotests/isis_topo1_vrf/r3/r3_topology.json create mode 100755 tests/topotests/isis_topo1_vrf/r3/zebra.conf create mode 100755 tests/topotests/isis_topo1_vrf/r4/isisd.conf create mode 100644 tests/topotests/isis_topo1_vrf/r4/r4_route.json create mode 100644 tests/topotests/isis_topo1_vrf/r4/r4_route6.json create mode 100755 tests/topotests/isis_topo1_vrf/r4/r4_route6_linux.json create mode 100755 tests/topotests/isis_topo1_vrf/r4/r4_route_linux.json create mode 100644 tests/topotests/isis_topo1_vrf/r4/r4_topology.json create mode 100755 tests/topotests/isis_topo1_vrf/r4/zebra.conf create mode 100755 tests/topotests/isis_topo1_vrf/r5/isisd.conf create mode 100644 tests/topotests/isis_topo1_vrf/r5/r5_route.json create mode 100644 tests/topotests/isis_topo1_vrf/r5/r5_route6.json create mode 100755 tests/topotests/isis_topo1_vrf/r5/r5_route6_linux.json create mode 100755 tests/topotests/isis_topo1_vrf/r5/r5_route_linux.json create mode 100644 tests/topotests/isis_topo1_vrf/r5/r5_topology.json create mode 100755 tests/topotests/isis_topo1_vrf/r5/zebra.conf create mode 100755 tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.dot create mode 100755 tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.jpg create mode 100644 tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.py create mode 100644 tests/topotests/kinds.yaml create mode 100644 tests/topotests/ldp_oc_acl_topo1/r1/ldpd.conf create mode 100644 tests/topotests/ldp_oc_acl_topo1/r1/ospfd.conf create mode 100644 tests/topotests/ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_oc_acl_topo1/r1/show_ip_route.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_all_binding.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r1/zebra.conf create mode 100644 tests/topotests/ldp_oc_acl_topo1/r2/ldpd.conf create mode 100644 tests/topotests/ldp_oc_acl_topo1/r2/ospfd.conf create mode 100644 tests/topotests/ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_oc_acl_topo1/r2/show_ip_route.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_all_binding.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r2/zebra.conf create mode 100644 tests/topotests/ldp_oc_acl_topo1/r3/ldpd.conf create mode 100644 tests/topotests/ldp_oc_acl_topo1/r3/ospfd.conf create mode 100644 tests/topotests/ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_oc_acl_topo1/r3/show_ip_route.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_all_binding.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r3/zebra.conf create mode 100644 tests/topotests/ldp_oc_acl_topo1/r4/ldpd.conf create mode 100644 tests/topotests/ldp_oc_acl_topo1/r4/ospfd.conf create mode 100644 tests/topotests/ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_oc_acl_topo1/r4/show_ip_route.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_all_binding.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_oc_acl_topo1/r4/zebra.conf create mode 100644 tests/topotests/ldp_oc_acl_topo1/test_ldp_oc_acl_topo1.dot create mode 100644 tests/topotests/ldp_oc_acl_topo1/test_ldp_oc_acl_topo1.py create mode 100644 tests/topotests/ldp_oc_topo1/r1/ldpd.conf create mode 100644 tests/topotests/ldp_oc_topo1/r1/ospfd.conf create mode 100644 tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_oc_topo1/r1/show_ip_route.ref create mode 100644 tests/topotests/ldp_oc_topo1/r1/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_oc_topo1/r1/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_oc_topo1/r1/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_oc_topo1/r1/zebra.conf create mode 100644 tests/topotests/ldp_oc_topo1/r2/ldpd.conf create mode 100644 tests/topotests/ldp_oc_topo1/r2/ospfd.conf create mode 100644 tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_oc_topo1/r2/show_ip_route.ref create mode 100644 tests/topotests/ldp_oc_topo1/r2/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_oc_topo1/r2/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_oc_topo1/r2/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_oc_topo1/r2/zebra.conf create mode 100644 tests/topotests/ldp_oc_topo1/r3/ldpd.conf create mode 100644 tests/topotests/ldp_oc_topo1/r3/ospfd.conf create mode 100644 tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_oc_topo1/r3/show_ip_route.ref create mode 100644 tests/topotests/ldp_oc_topo1/r3/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_oc_topo1/r3/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_oc_topo1/r3/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_oc_topo1/r3/zebra.conf create mode 100644 tests/topotests/ldp_oc_topo1/r4/ldpd.conf create mode 100644 tests/topotests/ldp_oc_topo1/r4/ospfd.conf create mode 100644 tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_oc_topo1/r4/show_ip_route.ref create mode 100644 tests/topotests/ldp_oc_topo1/r4/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_oc_topo1/r4/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_oc_topo1/r4/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_oc_topo1/r4/zebra.conf create mode 100644 tests/topotests/ldp_oc_topo1/test_ldp_oc_topo1.dot create mode 100644 tests/topotests/ldp_oc_topo1/test_ldp_oc_topo1.py create mode 100644 tests/topotests/ldp_snmp/ce1/zebra.conf create mode 100644 tests/topotests/ldp_snmp/ce2/zebra.conf create mode 100644 tests/topotests/ldp_snmp/ce3/zebra.conf create mode 100644 tests/topotests/ldp_snmp/r1/isisd.conf create mode 100644 tests/topotests/ldp_snmp/r1/ldpd.conf create mode 100644 tests/topotests/ldp_snmp/r1/show_ip_route.ref create mode 100644 tests/topotests/ldp_snmp/r1/show_isis_interface_detail.ref create mode 100644 tests/topotests/ldp_snmp/r1/show_isis_interface_detail_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r1/show_isis_interface_detail_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r1/show_isis_ldp_sync.ref create mode 100644 tests/topotests/ldp_snmp/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r1/show_l2vpn_binding.ref create mode 100644 tests/topotests/ldp_snmp/r1/show_l2vpn_vc.ref create mode 100644 tests/topotests/ldp_snmp/r1/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_snmp/r1/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_snmp/r1/show_ldp_igp_sync.ref create mode 100644 tests/topotests/ldp_snmp/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r1/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_snmp/r1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/ldp_snmp/r1/snmpd.conf create mode 100644 tests/topotests/ldp_snmp/r1/zebra.conf create mode 100644 tests/topotests/ldp_snmp/r2/isisd.conf create mode 100644 tests/topotests/ldp_snmp/r2/ldpd.conf create mode 100644 tests/topotests/ldp_snmp/r2/ospfd.conf create mode 100644 tests/topotests/ldp_snmp/r2/show_ip_route.ref create mode 100644 tests/topotests/ldp_snmp/r2/show_isis_interface_detail.ref create mode 100644 tests/topotests/ldp_snmp/r2/show_isis_interface_detail_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r2/show_isis_interface_detail_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r2/show_isis_ldp_sync.ref create mode 100644 tests/topotests/ldp_snmp/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r2/show_l2vpn_binding.ref create mode 100644 tests/topotests/ldp_snmp/r2/show_l2vpn_vc.ref create mode 100644 tests/topotests/ldp_snmp/r2/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_snmp/r2/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_snmp/r2/show_ldp_igp_sync.ref create mode 100644 tests/topotests/ldp_snmp/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r2/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_snmp/r2/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/ldp_snmp/r2/snmpd.conf create mode 100644 tests/topotests/ldp_snmp/r2/zebra.conf create mode 100644 tests/topotests/ldp_snmp/r3/isisd.conf create mode 100644 tests/topotests/ldp_snmp/r3/ldpd.conf create mode 100644 tests/topotests/ldp_snmp/r3/show_ip_route.ref create mode 100644 tests/topotests/ldp_snmp/r3/show_isis_interface_detail.ref create mode 100644 tests/topotests/ldp_snmp/r3/show_isis_interface_detail_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r3/show_isis_interface_detail_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r3/show_isis_ldp_sync.ref create mode 100644 tests/topotests/ldp_snmp/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r3/show_l2vpn_binding.ref create mode 100644 tests/topotests/ldp_snmp/r3/show_l2vpn_vc.ref create mode 100644 tests/topotests/ldp_snmp/r3/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_snmp/r3/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_snmp/r3/show_ldp_igp_sync.ref create mode 100644 tests/topotests/ldp_snmp/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_snmp/r3/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_snmp/r3/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/ldp_snmp/r3/zebra.conf create mode 100644 tests/topotests/ldp_snmp/test_ldp_snmp_topo1.py create mode 100644 tests/topotests/ldp_sync_isis_topo1/ce1/zebra.conf create mode 100644 tests/topotests/ldp_sync_isis_topo1/ce2/zebra.conf create mode 100644 tests/topotests/ldp_sync_isis_topo1/ce3/zebra.conf create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/isisd.conf create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/ldpd.conf create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/show_ip_route.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/show_isis_interface_detail.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/show_isis_interface_detail_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/show_isis_interface_detail_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/show_isis_ldp_sync.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/show_l2vpn_binding.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/show_l2vpn_vc.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_igp_sync.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r1/zebra.conf create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/isisd.conf create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/ldpd.conf create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_ip_route.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_isis_interface_detail.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_isis_interface_detail_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_isis_interface_detail_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_isis_ldp_sync.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_l2vpn_binding.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_l2vpn_vc.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_igp_sync.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r2/zebra.conf create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/isisd.conf create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/ldpd.conf create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/show_ip_route.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/show_isis_interface_detail.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/show_isis_interface_detail_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/show_isis_interface_detail_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/show_isis_ldp_sync.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/show_l2vpn_binding.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/show_l2vpn_vc.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_igp_sync.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/show_yang_interface_isis_adjacencies.ref create mode 100644 tests/topotests/ldp_sync_isis_topo1/r3/zebra.conf create mode 100644 tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.dot create mode 100644 tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.py create mode 100644 tests/topotests/ldp_sync_ospf_topo1/ce1/zebra.conf create mode 100644 tests/topotests/ldp_sync_ospf_topo1/ce2/zebra.conf create mode 100644 tests/topotests/ldp_sync_ospf_topo1/ce3/zebra.conf create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/ldpd.conf create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/ospf-nbrs.txt create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/ospfd.conf create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_interface.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_interface_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_interface_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_route.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/show_l2vpn_binding.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/show_l2vpn_vc.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_igp_sync.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r1/zebra.conf create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/ldpd.conf create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/ospfd.conf create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_interface.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_interface_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_interface_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_route.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/show_l2vpn_binding.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/show_l2vpn_vc.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_igp_sync.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r2/zebra.conf create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/ldpd.conf create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/ospfd.conf create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_interface.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_interface_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_interface_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_route.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/show_l2vpn_binding.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/show_l2vpn_vc.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_igp_sync.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync_r1_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync_r2_eth1_shutdown.ref create mode 100644 tests/topotests/ldp_sync_ospf_topo1/r3/zebra.conf create mode 100644 tests/topotests/ldp_sync_ospf_topo1/test_ldp_sync_ospf_topo1.dot create mode 100644 tests/topotests/ldp_sync_ospf_topo1/test_ldp_sync_ospf_topo1.py create mode 100644 tests/topotests/ldp_topo1/r1/ip_mpls_route.ref create mode 100644 tests/topotests/ldp_topo1/r1/ldpd.conf create mode 100644 tests/topotests/ldp_topo1/r1/ospfd.conf create mode 100644 tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_topo1/r1/show_ipv4_route.ref create mode 100644 tests/topotests/ldp_topo1/r1/show_mpls_ldp_binding.ref create mode 100644 tests/topotests/ldp_topo1/r1/show_mpls_ldp_discovery.ref create mode 100644 tests/topotests/ldp_topo1/r1/show_mpls_ldp_interface.ref create mode 100644 tests/topotests/ldp_topo1/r1/show_mpls_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_topo1/r1/show_mpls_table.ref create mode 100644 tests/topotests/ldp_topo1/r1/zebra.conf create mode 100644 tests/topotests/ldp_topo1/r2/ip_mpls_route.ref create mode 100644 tests/topotests/ldp_topo1/r2/ldpd.conf create mode 100644 tests/topotests/ldp_topo1/r2/ospfd.conf create mode 100644 tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_topo1/r2/show_ipv4_route.ref create mode 100644 tests/topotests/ldp_topo1/r2/show_mpls_ldp_binding.ref create mode 100644 tests/topotests/ldp_topo1/r2/show_mpls_ldp_discovery.ref create mode 100644 tests/topotests/ldp_topo1/r2/show_mpls_ldp_interface.ref create mode 100644 tests/topotests/ldp_topo1/r2/show_mpls_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_topo1/r2/show_mpls_table.ref create mode 100644 tests/topotests/ldp_topo1/r2/zebra.conf create mode 100644 tests/topotests/ldp_topo1/r3/ip_mpls_route.ref create mode 100644 tests/topotests/ldp_topo1/r3/ldpd.conf create mode 100644 tests/topotests/ldp_topo1/r3/ospfd.conf create mode 100644 tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_topo1/r3/show_ipv4_route.ref create mode 100644 tests/topotests/ldp_topo1/r3/show_mpls_ldp_binding.ref create mode 100644 tests/topotests/ldp_topo1/r3/show_mpls_ldp_discovery.ref create mode 100644 tests/topotests/ldp_topo1/r3/show_mpls_ldp_interface.ref create mode 100644 tests/topotests/ldp_topo1/r3/show_mpls_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_topo1/r3/show_mpls_table.ref create mode 100644 tests/topotests/ldp_topo1/r3/zebra.conf create mode 100644 tests/topotests/ldp_topo1/r4/ip_mpls_route.ref create mode 100644 tests/topotests/ldp_topo1/r4/ldpd.conf create mode 100644 tests/topotests/ldp_topo1/r4/ospfd.conf create mode 100644 tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_topo1/r4/show_ipv4_route.ref create mode 100644 tests/topotests/ldp_topo1/r4/show_mpls_ldp_binding.ref create mode 100644 tests/topotests/ldp_topo1/r4/show_mpls_ldp_discovery.ref create mode 100644 tests/topotests/ldp_topo1/r4/show_mpls_ldp_interface.ref create mode 100644 tests/topotests/ldp_topo1/r4/show_mpls_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_topo1/r4/show_mpls_table.ref create mode 100644 tests/topotests/ldp_topo1/r4/zebra.conf create mode 100644 tests/topotests/ldp_topo1/test_ldp_topo1.py create mode 100644 tests/topotests/ldp_vpls_topo1/__init__.py create mode 100644 tests/topotests/ldp_vpls_topo1/ce1/zebra.conf create mode 100644 tests/topotests/ldp_vpls_topo1/ce2/zebra.conf create mode 100644 tests/topotests/ldp_vpls_topo1/ce3/zebra.conf create mode 100644 tests/topotests/ldp_vpls_topo1/r1/ldpd.conf create mode 100644 tests/topotests/ldp_vpls_topo1/r1/ospf-nbrs.txt create mode 100644 tests/topotests/ldp_vpls_topo1/r1/ospfd.conf create mode 100644 tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_vpls_topo1/r1/show_ip_route.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r1/show_ip_route_after_link_down.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r1/show_l2vpn_binding.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r1/show_l2vpn_vc.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r1/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r1/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r1/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r1/zebra.conf create mode 100644 tests/topotests/ldp_vpls_topo1/r2/ldpd.conf create mode 100644 tests/topotests/ldp_vpls_topo1/r2/ospfd.conf create mode 100644 tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_vpls_topo1/r2/show_ip_route.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r2/show_l2vpn_binding.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r2/show_l2vpn_vc.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r2/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r2/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r2/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r2/zebra.conf create mode 100644 tests/topotests/ldp_vpls_topo1/r3/ldpd.conf create mode 100644 tests/topotests/ldp_vpls_topo1/r3/ospfd.conf create mode 100644 tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ldp_vpls_topo1/r3/show_ip_route.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r3/show_l2vpn_binding.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r3/show_l2vpn_vc.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r3/show_ldp_binding.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r3/show_ldp_discovery.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r3/show_ldp_neighbor.ref create mode 100644 tests/topotests/ldp_vpls_topo1/r3/zebra.conf create mode 100644 tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.dot create mode 100644 tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.pdf create mode 100644 tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.py create mode 100644 tests/topotests/lib/__init__.py create mode 100644 tests/topotests/lib/bgp.py create mode 100644 tests/topotests/lib/bgprib.py create mode 100644 tests/topotests/lib/bmp_collector/bgp/__init__.py create mode 100644 tests/topotests/lib/bmp_collector/bgp/open/__init__.py create mode 100644 tests/topotests/lib/bmp_collector/bgp/update/__init__.py create mode 100644 tests/topotests/lib/bmp_collector/bgp/update/af.py create mode 100644 tests/topotests/lib/bmp_collector/bgp/update/nlri.py create mode 100644 tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py create mode 100644 tests/topotests/lib/bmp_collector/bgp/update/rd.py create mode 100644 tests/topotests/lib/bmp_collector/bmp.py create mode 100755 tests/topotests/lib/bmp_collector/bmpserver create mode 100644 tests/topotests/lib/checkping.py create mode 100644 tests/topotests/lib/common_config.py create mode 100755 tests/topotests/lib/exa-receive.py create mode 100644 tests/topotests/lib/fixtures.py create mode 100755 tests/topotests/lib/grpc-query.py create mode 100644 tests/topotests/lib/ltemplate.py create mode 100644 tests/topotests/lib/lutil.py create mode 100755 tests/topotests/lib/mcast-tester.py create mode 100644 tests/topotests/lib/micronet.py create mode 100644 tests/topotests/lib/micronet_compat.py create mode 100644 tests/topotests/lib/ospf.py create mode 100644 tests/topotests/lib/pim.py create mode 100755 tests/topotests/lib/scapy_sendpkt.py create mode 100755 tests/topotests/lib/send_bsr_packet.py create mode 100644 tests/topotests/lib/snmptest.py create mode 100644 tests/topotests/lib/test/__init__.py create mode 100755 tests/topotests/lib/test/test_json.py create mode 100755 tests/topotests/lib/test/test_run_and_expect.py create mode 100755 tests/topotests/lib/test/test_version.py create mode 100644 tests/topotests/lib/topogen.py create mode 100644 tests/topotests/lib/topojson.py create mode 100644 tests/topotests/lib/topolog.py create mode 100644 tests/topotests/lib/topotest.py create mode 100644 tests/topotests/mgmt_config/r1/early-end-zebra.conf create mode 100644 tests/topotests/mgmt_config/r1/early-end.conf create mode 100644 tests/topotests/mgmt_config/r1/early-end2-zebra.conf create mode 100644 tests/topotests/mgmt_config/r1/early-end2.conf create mode 100644 tests/topotests/mgmt_config/r1/early-exit-zebra.conf create mode 100644 tests/topotests/mgmt_config/r1/early-exit.conf create mode 100644 tests/topotests/mgmt_config/r1/early-exit2-zebra.conf create mode 100644 tests/topotests/mgmt_config/r1/early-exit2.conf create mode 100644 tests/topotests/mgmt_config/r1/frr.conf create mode 100644 tests/topotests/mgmt_config/r1/mgmtd.conf create mode 100644 tests/topotests/mgmt_config/r1/normal-exit.conf create mode 100644 tests/topotests/mgmt_config/r1/one-exit-zebra.conf create mode 100644 tests/topotests/mgmt_config/r1/one-exit.conf create mode 100644 tests/topotests/mgmt_config/r1/one-exit2-zebra.conf create mode 100644 tests/topotests/mgmt_config/r1/one-exit2.conf create mode 100644 tests/topotests/mgmt_config/r1/zebra.conf create mode 100644 tests/topotests/mgmt_config/test_config.py create mode 100644 tests/topotests/mgmt_config/test_regression.py create mode 100644 tests/topotests/mgmt_startup/r1/mgmtd.conf create mode 100644 tests/topotests/mgmt_startup/r1/zebra.conf create mode 100644 tests/topotests/mgmt_startup/r2/staticd.conf create mode 100644 tests/topotests/mgmt_startup/r2/zebra.conf create mode 100644 tests/topotests/mgmt_startup/r3/zebra.conf create mode 100644 tests/topotests/mgmt_startup/r4/frr.conf create mode 100644 tests/topotests/mgmt_startup/test_bigconf.py create mode 100644 tests/topotests/mgmt_startup/test_cfgfile_var.py create mode 100644 tests/topotests/mgmt_startup/test_late_bigconf.py create mode 100644 tests/topotests/mgmt_startup/test_late_uniconf.py create mode 100644 tests/topotests/mgmt_startup/test_latestart.py create mode 100644 tests/topotests/mgmt_startup/util.py create mode 100644 tests/topotests/mgmt_tests/test_yang_mgmt.py create mode 100644 tests/topotests/mgmt_tests/yang_mgmt.json create mode 100644 tests/topotests/msdp_mesh_topo1/__init__.py create mode 100644 tests/topotests/msdp_mesh_topo1/r1/bgpd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r1/ospfd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r1/pimd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r1/zebra.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r2/bgpd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r2/ospfd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r2/pimd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r2/zebra.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r3/bgpd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r3/ospfd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r3/pimd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r3/zebra.conf create mode 100644 tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot create mode 100644 tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.png create mode 100644 tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py create mode 100644 tests/topotests/msdp_topo1/__init__.py create mode 100644 tests/topotests/msdp_topo1/r1/bgpd.conf create mode 100644 tests/topotests/msdp_topo1/r1/pimd.conf create mode 100644 tests/topotests/msdp_topo1/r1/zebra.conf create mode 100644 tests/topotests/msdp_topo1/r2/bgpd.conf create mode 100644 tests/topotests/msdp_topo1/r2/pimd.conf create mode 100644 tests/topotests/msdp_topo1/r2/zebra.conf create mode 100644 tests/topotests/msdp_topo1/r3/bgpd.conf create mode 100644 tests/topotests/msdp_topo1/r3/pimd.conf create mode 100644 tests/topotests/msdp_topo1/r3/zebra.conf create mode 100644 tests/topotests/msdp_topo1/r4/bgpd.conf create mode 100644 tests/topotests/msdp_topo1/r4/pimd.conf create mode 100644 tests/topotests/msdp_topo1/r4/zebra.conf create mode 100755 tests/topotests/msdp_topo1/test_msdp_topo1.py create mode 100644 tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json create mode 100644 tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py create mode 100644 tests/topotests/multicast_pim6_sm_topo1/multicast_pim6_sm_topo1.json create mode 100644 tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py create mode 100644 tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py create mode 100644 tests/topotests/multicast_pim6_static_rp_topo1/__init__.py create mode 100644 tests/topotests/multicast_pim6_static_rp_topo1/multicast_pim6_static_rp.json create mode 100755 tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py create mode 100755 tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py create mode 100644 tests/topotests/multicast_pim_bsm_topo1/mcast_pim_bsmp_01.json create mode 100644 tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py create mode 100644 tests/topotests/multicast_pim_bsm_topo2/mcast_pim_bsmp_02.json create mode 100644 tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py create mode 100644 tests/topotests/multicast_pim_dr_nondr_test/__init__.py create mode 100644 tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_ospf_topo2.json create mode 100644 tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_static_routes_topo1.json create mode 100644 tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_transit_router_topo3.json create mode 100755 tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_ospf_topo2.py create mode 100755 tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_static_routes_topo1.py create mode 100755 tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py create mode 100644 tests/topotests/multicast_pim_sm_topo1/multicast_pim_sm_topo1.json create mode 100755 tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py create mode 100644 tests/topotests/multicast_pim_sm_topo2/multicast_pim_sm_topo2.json create mode 100755 tests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py create mode 100644 tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json create mode 100644 tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json create mode 100644 tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json create mode 100644 tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json create mode 100644 tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json create mode 100644 tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json create mode 100644 tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json create mode 100644 tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo3.json create mode 100644 tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo4.json create mode 100755 tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py create mode 100755 tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo4.py create mode 100644 tests/topotests/multicast_pim_static_rp_topo1/__init__.py create mode 100644 tests/topotests/multicast_pim_static_rp_topo1/multicast_pim_static_rp.json create mode 100755 tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp.py create mode 100755 tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp1.py create mode 100755 tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp2.py create mode 100644 tests/topotests/multicast_pim_uplink_topo1/multicast_pim_uplink_topo1.json create mode 100644 tests/topotests/multicast_pim_uplink_topo1/test_multicast_pim_uplink_topo1.py create mode 100644 tests/topotests/multicast_pim_uplink_topo2/multicast_pim_uplink_topo2.json create mode 100644 tests/topotests/multicast_pim_uplink_topo2/test_multicast_pim_uplink_topo2.py create mode 100644 tests/topotests/multicast_pim_uplink_topo3/multicast_pim_uplink_topo3.json create mode 100644 tests/topotests/multicast_pim_uplink_topo3/test_multicast_pim_uplink_topo3.py create mode 100644 tests/topotests/munet/__init__.py create mode 100644 tests/topotests/munet/__main__.py create mode 100644 tests/topotests/munet/base.py create mode 100644 tests/topotests/munet/cleanup.py create mode 100644 tests/topotests/munet/cli.py create mode 100644 tests/topotests/munet/compat.py create mode 100644 tests/topotests/munet/config.py create mode 100644 tests/topotests/munet/kinds.yaml create mode 100644 tests/topotests/munet/linux.py create mode 100644 tests/topotests/munet/logconf-mutest.yaml create mode 100644 tests/topotests/munet/logconf.yaml create mode 100644 tests/topotests/munet/mucmd.py create mode 100644 tests/topotests/munet/mulog.py create mode 100644 tests/topotests/munet/munet-schema.json create mode 100644 tests/topotests/munet/mutest/__main__.py create mode 100644 tests/topotests/munet/mutest/userapi.py create mode 100644 tests/topotests/munet/mutestshare.py create mode 100755 tests/topotests/munet/mutini.py create mode 100644 tests/topotests/munet/native.py create mode 100644 tests/topotests/munet/parser.py create mode 100644 tests/topotests/munet/testing/__init__.py create mode 100644 tests/topotests/munet/testing/fixtures.py create mode 100644 tests/topotests/munet/testing/hooks.py create mode 100644 tests/topotests/munet/testing/util.py create mode 100644 tests/topotests/nhrp_topo/r1/nhrp4_cache.json create mode 100644 tests/topotests/nhrp_topo/r1/nhrp_route4.json create mode 100644 tests/topotests/nhrp_topo/r1/nhrpd.conf create mode 100644 tests/topotests/nhrp_topo/r1/sharp_route4.json create mode 100644 tests/topotests/nhrp_topo/r1/zebra.conf create mode 100644 tests/topotests/nhrp_topo/r2/nhrp4_cache.json create mode 100644 tests/topotests/nhrp_topo/r2/nhrp_route4.json create mode 100644 tests/topotests/nhrp_topo/r2/nhrpd.conf create mode 100644 tests/topotests/nhrp_topo/r2/zebra.conf create mode 100644 tests/topotests/nhrp_topo/r3/zebra.conf create mode 100644 tests/topotests/nhrp_topo/test_nhrp_topo.dot create mode 100644 tests/topotests/nhrp_topo/test_nhrp_topo.py create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r1/ospf6d.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r1/zebra.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r2/ospf6d.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r2/zebra.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r3/ospf6d.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r3/zebra.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r4/ospf6d.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r4/zebra.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r5/ospf6d.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r5/zebra.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r6/ospf6d.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r6/zebra.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r7/zebra.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r8/zebra.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r9/ospf6d.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r9/zebra.conf create mode 100644 tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py create mode 100644 tests/topotests/ospf6_gr_topo1/__init__.py create mode 100644 tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf create mode 100644 tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_database.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_neighbor.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_route.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt1/zebra.conf create mode 100644 tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf create mode 100644 tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_database.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_neighbor.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_route.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt2/zebra.conf create mode 100644 tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf create mode 100644 tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_database.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_neighbor.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_route.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt3/zebra.conf create mode 100644 tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf create mode 100644 tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_database.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_neighbor.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_route.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt4/zebra.conf create mode 100644 tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf create mode 100644 tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_database.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_neighbor.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_route.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt5/zebra.conf create mode 100644 tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf create mode 100644 tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_database.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_neighbor.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_route.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt6/zebra.conf create mode 100644 tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf create mode 100644 tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_database.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_neighbor.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_route.json create mode 100644 tests/topotests/ospf6_gr_topo1/rt7/zebra.conf create mode 100755 tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py create mode 100644 tests/topotests/ospf6_loopback_cost/__init__.py create mode 100644 tests/topotests/ospf6_loopback_cost/r1/frr.conf create mode 100644 tests/topotests/ospf6_loopback_cost/r2/frr.conf create mode 100644 tests/topotests/ospf6_loopback_cost/test_ospf6_loopback_cost.py create mode 100644 tests/topotests/ospf6_topo1/README.md create mode 100644 tests/topotests/ospf6_topo1/r1/ip_6_address.nhg.ref create mode 100644 tests/topotests/ospf6_topo1/r1/ip_6_address.ref create mode 100644 tests/topotests/ospf6_topo1/r1/ospf6d.conf create mode 100644 tests/topotests/ospf6_topo1/r1/show_ipv6_route.ref create mode 100644 tests/topotests/ospf6_topo1/r1/zebra.conf create mode 100644 tests/topotests/ospf6_topo1/r2/ip_6_address.nhg.ref create mode 100644 tests/topotests/ospf6_topo1/r2/ip_6_address.ref create mode 100644 tests/topotests/ospf6_topo1/r2/ospf6d.conf create mode 100644 tests/topotests/ospf6_topo1/r2/show_ipv6_route.ref create mode 100644 tests/topotests/ospf6_topo1/r2/zebra.conf create mode 100644 tests/topotests/ospf6_topo1/r3/ip_6_address.nhg.ref create mode 100644 tests/topotests/ospf6_topo1/r3/ip_6_address.ref create mode 100644 tests/topotests/ospf6_topo1/r3/ospf6d.conf create mode 100644 tests/topotests/ospf6_topo1/r3/show_ipv6_route.ref create mode 100644 tests/topotests/ospf6_topo1/r3/zebra.conf create mode 100644 tests/topotests/ospf6_topo1/r4/ip_6_address.nhg.ref create mode 100644 tests/topotests/ospf6_topo1/r4/ip_6_address.ref create mode 100644 tests/topotests/ospf6_topo1/r4/ospf6d.conf create mode 100644 tests/topotests/ospf6_topo1/r4/show_ipv6_route.ref create mode 100644 tests/topotests/ospf6_topo1/r4/zebra.conf create mode 100644 tests/topotests/ospf6_topo1/test_ospf6_topo1.py create mode 100644 tests/topotests/ospf6_topo1_vrf/README.md create mode 100644 tests/topotests/ospf6_topo1_vrf/r1/ip_6_address.nhg.ref create mode 100644 tests/topotests/ospf6_topo1_vrf/r1/ip_6_address.ref create mode 100644 tests/topotests/ospf6_topo1_vrf/r1/ospf6d.conf create mode 100644 tests/topotests/ospf6_topo1_vrf/r1/show_ipv6_vrf_route.ref create mode 100644 tests/topotests/ospf6_topo1_vrf/r1/zebra.conf create mode 100644 tests/topotests/ospf6_topo1_vrf/r2/ip_6_address.ref create mode 100644 tests/topotests/ospf6_topo1_vrf/r2/ospf6d.conf create mode 100644 tests/topotests/ospf6_topo1_vrf/r2/show_ipv6_vrf_route.ref create mode 100644 tests/topotests/ospf6_topo1_vrf/r2/zebra.conf create mode 100644 tests/topotests/ospf6_topo1_vrf/r3/ip_6_address.ref create mode 100644 tests/topotests/ospf6_topo1_vrf/r3/ospf6d.conf create mode 100644 tests/topotests/ospf6_topo1_vrf/r3/show_ipv6_vrf_route.ref create mode 100644 tests/topotests/ospf6_topo1_vrf/r3/zebra.conf create mode 100644 tests/topotests/ospf6_topo1_vrf/r4/ip_6_address.ref create mode 100644 tests/topotests/ospf6_topo1_vrf/r4/ospf6d.conf create mode 100644 tests/topotests/ospf6_topo1_vrf/r4/show_ipv6_vrf_route.ref create mode 100644 tests/topotests/ospf6_topo1_vrf/r4/zebra.conf create mode 100755 tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py create mode 100644 tests/topotests/ospf6_topo2/r1/ospf6d.conf create mode 100644 tests/topotests/ospf6_topo2/r1/zebra.conf create mode 100644 tests/topotests/ospf6_topo2/r2/ospf6d.conf create mode 100644 tests/topotests/ospf6_topo2/r2/zebra.conf create mode 100644 tests/topotests/ospf6_topo2/r3/ospf6d.conf create mode 100644 tests/topotests/ospf6_topo2/r3/zebra.conf create mode 100644 tests/topotests/ospf6_topo2/r4/ospf6d.conf create mode 100644 tests/topotests/ospf6_topo2/r4/zebra.conf create mode 100644 tests/topotests/ospf6_topo2/test_ospf6_topo2.dot create mode 100644 tests/topotests/ospf6_topo2/test_ospf6_topo2.png create mode 100644 tests/topotests/ospf6_topo2/test_ospf6_topo2.py create mode 100644 tests/topotests/ospf_basic_functionality/ospf_asbr_summary_topo1.json create mode 100644 tests/topotests/ospf_basic_functionality/ospf_asbr_summary_type7_lsa.json create mode 100644 tests/topotests/ospf_basic_functionality/ospf_authentication.json create mode 100644 tests/topotests/ospf_basic_functionality/ospf_chaos.json create mode 100644 tests/topotests/ospf_basic_functionality/ospf_ecmp.json create mode 100644 tests/topotests/ospf_basic_functionality/ospf_ecmp_lan.json create mode 100644 tests/topotests/ospf_basic_functionality/ospf_flood_reduction.json create mode 100644 tests/topotests/ospf_basic_functionality/ospf_lan.json create mode 100644 tests/topotests/ospf_basic_functionality/ospf_nssa.json create mode 100644 tests/topotests/ospf_basic_functionality/ospf_p2mp.json create mode 100644 tests/topotests/ospf_basic_functionality/ospf_routemaps.json create mode 100644 tests/topotests/ospf_basic_functionality/ospf_rte_calc.json create mode 100644 tests/topotests/ospf_basic_functionality/ospf_single_area.json create mode 100644 tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py create mode 100644 tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py create mode 100644 tests/topotests/ospf_basic_functionality/test_ospf_authentication.py create mode 100644 tests/topotests/ospf_basic_functionality/test_ospf_chaos.py create mode 100644 tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py create mode 100644 tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py create mode 100644 tests/topotests/ospf_basic_functionality/test_ospf_flood_reduction.py create mode 100644 tests/topotests/ospf_basic_functionality/test_ospf_lan.py create mode 100644 tests/topotests/ospf_basic_functionality/test_ospf_nssa.py create mode 100644 tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py create mode 100644 tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py create mode 100644 tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py create mode 100644 tests/topotests/ospf_basic_functionality/test_ospf_single_area.py create mode 100644 tests/topotests/ospf_dual_stack/test_ospf_dual_stack.dot create mode 100644 tests/topotests/ospf_dual_stack/test_ospf_dual_stack.jpg create mode 100644 tests/topotests/ospf_dual_stack/test_ospf_dual_stack.json create mode 100644 tests/topotests/ospf_dual_stack/test_ospf_dual_stack.py create mode 100644 tests/topotests/ospf_gr_helper/ospf_gr_helper.json create mode 100644 tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py create mode 100644 tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py create mode 100644 tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py create mode 100644 tests/topotests/ospf_gr_topo1/__init__.py create mode 100644 tests/topotests/ospf_gr_topo1/rt1/ospfd.conf create mode 100644 tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_database.json create mode 100644 tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_route.json create mode 100644 tests/topotests/ospf_gr_topo1/rt1/show_ip_route.json create mode 100644 tests/topotests/ospf_gr_topo1/rt1/zebra.conf create mode 100644 tests/topotests/ospf_gr_topo1/rt2/ospfd.conf create mode 100644 tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_database.json create mode 100644 tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_route.json create mode 100644 tests/topotests/ospf_gr_topo1/rt2/show_ip_route.json create mode 100644 tests/topotests/ospf_gr_topo1/rt2/zebra.conf create mode 100644 tests/topotests/ospf_gr_topo1/rt3/ospfd.conf create mode 100644 tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_database.json create mode 100644 tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_route.json create mode 100644 tests/topotests/ospf_gr_topo1/rt3/show_ip_route.json create mode 100644 tests/topotests/ospf_gr_topo1/rt3/zebra.conf create mode 100644 tests/topotests/ospf_gr_topo1/rt4/ospfd.conf create mode 100644 tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_database.json create mode 100644 tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_route.json create mode 100644 tests/topotests/ospf_gr_topo1/rt4/show_ip_route.json create mode 100644 tests/topotests/ospf_gr_topo1/rt4/zebra.conf create mode 100644 tests/topotests/ospf_gr_topo1/rt5/ospfd.conf create mode 100644 tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_database.json create mode 100644 tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_route.json create mode 100644 tests/topotests/ospf_gr_topo1/rt5/show_ip_route.json create mode 100644 tests/topotests/ospf_gr_topo1/rt5/zebra.conf create mode 100644 tests/topotests/ospf_gr_topo1/rt6/ospfd.conf create mode 100644 tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_database.json create mode 100644 tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_route.json create mode 100644 tests/topotests/ospf_gr_topo1/rt6/show_ip_route.json create mode 100644 tests/topotests/ospf_gr_topo1/rt6/zebra.conf create mode 100644 tests/topotests/ospf_gr_topo1/rt7/ospfd.conf create mode 100644 tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_database.json create mode 100644 tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json create mode 100644 tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_route.json create mode 100644 tests/topotests/ospf_gr_topo1/rt7/show_ip_route.json create mode 100644 tests/topotests/ospf_gr_topo1/rt7/zebra.conf create mode 100755 tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py create mode 100644 tests/topotests/ospf_instance_redistribute/r1/ospf_default_information.json create mode 100644 tests/topotests/ospf_instance_redistribute/r1/ospf_instance_lsa.json create mode 100644 tests/topotests/ospf_instance_redistribute/r1/ospf_instance_lsa2.json create mode 100644 tests/topotests/ospf_instance_redistribute/r1/ospfd-3.conf create mode 100644 tests/topotests/ospf_instance_redistribute/r1/sharp_installed.json create mode 100644 tests/topotests/ospf_instance_redistribute/r1/zebra.conf create mode 100644 tests/topotests/ospf_instance_redistribute/test_ospf_instance_redistribute.py create mode 100755 tests/topotests/ospf_metric_propagation/__init__.py create mode 100644 tests/topotests/ospf_metric_propagation/h1/frr.conf create mode 100644 tests/topotests/ospf_metric_propagation/h2/frr.conf create mode 100644 tests/topotests/ospf_metric_propagation/r1/frr.conf create mode 100644 tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json create mode 100644 tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json create mode 100644 tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json create mode 100644 tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json create mode 100644 tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json create mode 100644 tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json create mode 100644 tests/topotests/ospf_metric_propagation/r2/frr.conf create mode 100644 tests/topotests/ospf_metric_propagation/r3/frr.conf create mode 100644 tests/topotests/ospf_metric_propagation/r4/frr.conf create mode 100644 tests/topotests/ospf_metric_propagation/ra/frr.conf create mode 100644 tests/topotests/ospf_metric_propagation/rb/frr.conf create mode 100644 tests/topotests/ospf_metric_propagation/rc/frr.conf create mode 100644 tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py create mode 100755 tests/topotests/ospf_multi_vrf_bgp_route_leak/__init__.py create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-default.txt create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-neno.txt create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-default.txt create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-ray.txt create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/ospf-vrf-default.txt create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/ospf-vrf-default.txt create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt create mode 100644 tests/topotests/ospf_multi_vrf_bgp_route_leak/test_ospf_multi_vrf_bgp_route_leak.py create mode 100755 tests/topotests/ospf_netns_vrf/__init__.py create mode 100644 tests/topotests/ospf_netns_vrf/r1/ospfd.conf create mode 100644 tests/topotests/ospf_netns_vrf/r1/ospfroute.txt create mode 100644 tests/topotests/ospf_netns_vrf/r1/ospfroute_down.txt create mode 100644 tests/topotests/ospf_netns_vrf/r1/zebra.conf create mode 100644 tests/topotests/ospf_netns_vrf/r1/zebraroute.txt create mode 100644 tests/topotests/ospf_netns_vrf/r1/zebraroutedown.txt create mode 100644 tests/topotests/ospf_netns_vrf/r2/ospfd.conf create mode 100644 tests/topotests/ospf_netns_vrf/r2/ospfroute.txt create mode 100644 tests/topotests/ospf_netns_vrf/r2/ospfroute_down.txt create mode 100644 tests/topotests/ospf_netns_vrf/r2/zebra.conf create mode 100644 tests/topotests/ospf_netns_vrf/r2/zebraroute.txt create mode 100644 tests/topotests/ospf_netns_vrf/r2/zebraroutedown.txt create mode 100644 tests/topotests/ospf_netns_vrf/r3/ospfd.conf create mode 100644 tests/topotests/ospf_netns_vrf/r3/ospfroute.txt create mode 100644 tests/topotests/ospf_netns_vrf/r3/ospfroute_down.txt create mode 100644 tests/topotests/ospf_netns_vrf/r3/zebra.conf create mode 100644 tests/topotests/ospf_netns_vrf/r3/zebraroute.txt create mode 100644 tests/topotests/ospf_netns_vrf/r3/zebraroutedown.txt create mode 100644 tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.dot create mode 100644 tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.jpg create mode 100644 tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.py create mode 100644 tests/topotests/ospf_nssa_topo1/__init__.py create mode 100644 tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf create mode 100644 tests/topotests/ospf_nssa_topo1/rt1/staticd.conf create mode 100644 tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt1/zebra.conf create mode 100644 tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf create mode 100644 tests/topotests/ospf_nssa_topo1/rt2/staticd.conf create mode 100644 tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt2/zebra.conf create mode 100644 tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf create mode 100644 tests/topotests/ospf_nssa_topo1/rt3/staticd.conf create mode 100644 tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt3/zebra.conf create mode 100644 tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf create mode 100644 tests/topotests/ospf_nssa_topo1/rt4/staticd.conf create mode 100644 tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref create mode 100644 tests/topotests/ospf_nssa_topo1/rt4/zebra.conf create mode 100644 tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py create mode 100644 tests/topotests/ospf_prefix_suppression/r1/frr.conf create mode 100644 tests/topotests/ospf_prefix_suppression/r2/frr.conf create mode 100644 tests/topotests/ospf_prefix_suppression/r3/frr.conf create mode 100644 tests/topotests/ospf_prefix_suppression/test_ospf_prefix_suppression.py create mode 100644 tests/topotests/ospf_sr_te_topo1/dst/zebra.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt1/bgpd.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt1/ospfd.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt1/pathd.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt1/step2/show_operational_data.ref create mode 100644 tests/topotests/ospf_sr_te_topo1/rt1/step2/show_operational_data_with_candidate.ref create mode 100644 tests/topotests/ospf_sr_te_topo1/rt1/step3/show_operational_data_with_single_candidate.ref create mode 100644 tests/topotests/ospf_sr_te_topo1/rt1/step3/show_operational_data_with_two_candidates.ref create mode 100644 tests/topotests/ospf_sr_te_topo1/rt1/zebra.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt2/ospfd.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt2/zebra.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt3/ospfd.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt3/zebra.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt4/ospfd.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt4/zebra.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt5/ospfd.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt5/zebra.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt6/bgpd.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt6/ospfd.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt6/pathd.conf create mode 100644 tests/topotests/ospf_sr_te_topo1/rt6/step2/show_operational_data.ref create mode 100644 tests/topotests/ospf_sr_te_topo1/rt6/step2/show_operational_data_with_candidate.ref create mode 100644 tests/topotests/ospf_sr_te_topo1/rt6/step3/show_operational_data_with_single_candidate.ref create mode 100644 tests/topotests/ospf_sr_te_topo1/rt6/step3/show_operational_data_with_two_candidates.ref create mode 100644 tests/topotests/ospf_sr_te_topo1/rt6/zebra.conf create mode 100755 tests/topotests/ospf_sr_te_topo1/test_ospf_sr_te_topo1.py create mode 100644 tests/topotests/ospf_sr_topo1/__init__.py create mode 100644 tests/topotests/ospf_sr_topo1/rt1/ospfd.conf create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step1/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step1/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step10/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step10/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step2/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step2/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step3/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step3/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step4/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step4/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step5/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step5/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step6/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step6/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step7/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step7/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step8/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step8/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step9/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/step9/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt1/zebra.conf create mode 100644 tests/topotests/ospf_sr_topo1/rt2/ospfd.conf create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step1/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step1/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step10/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step10/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step2/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step2/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step3/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step3/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step4/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step4/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step5/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step5/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step6/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step6/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step7/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step7/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step8/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step8/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step9/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/step9/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt2/zebra.conf create mode 100644 tests/topotests/ospf_sr_topo1/rt3/ospfd.conf create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step1/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step1/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step10/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step10/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step2/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step2/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step3/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step3/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step4/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step4/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step5/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step5/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step6/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step6/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step7/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step7/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step8/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step8/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step9/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/step9/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt3/zebra.conf create mode 100644 tests/topotests/ospf_sr_topo1/rt4/ospfd.conf create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step1/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step1/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step10/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step10/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step2/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step2/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step3/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step3/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step4/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step4/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step5/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step5/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step6/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step6/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step7/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step7/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step8/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step8/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step9/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/step9/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt4/zebra.conf create mode 100644 tests/topotests/ospf_sr_topo1/rt5/ospfd.conf create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step1/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step1/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step10/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step10/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step2/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step2/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step3/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step3/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step4/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step4/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step5/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step5/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step6/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step6/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step7/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step7/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step8/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step8/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step9/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/step9/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt5/zebra.conf create mode 100644 tests/topotests/ospf_sr_topo1/rt6/ospfd.conf create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step1/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step1/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step10/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step10/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step2/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step2/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step3/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step3/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step4/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step4/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step5/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step5/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step6/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step6/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step7/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step7/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step8/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step8/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step9/show_ip_route.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/step9/show_mpls_table.ref create mode 100644 tests/topotests/ospf_sr_topo1/rt6/zebra.conf create mode 100644 tests/topotests/ospf_sr_topo1/test_ospf_sr_topo1.py create mode 100755 tests/topotests/ospf_suppress_fa/__init__.py create mode 100644 tests/topotests/ospf_suppress_fa/r1/ospfd.conf create mode 100644 tests/topotests/ospf_suppress_fa/r1/zebra.conf create mode 100644 tests/topotests/ospf_suppress_fa/r2/ospfd.conf create mode 100644 tests/topotests/ospf_suppress_fa/r2/zebra.conf create mode 100644 tests/topotests/ospf_suppress_fa/r3/ospfd.conf create mode 100644 tests/topotests/ospf_suppress_fa/r3/zebra.conf create mode 100644 tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot create mode 100644 tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot.jpg create mode 100644 tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py create mode 100755 tests/topotests/ospf_te_topo1/__init__.py create mode 100644 tests/topotests/ospf_te_topo1/r1/ospfd.conf create mode 100644 tests/topotests/ospf_te_topo1/r1/zebra.conf create mode 100644 tests/topotests/ospf_te_topo1/r2/ospfd.conf create mode 100644 tests/topotests/ospf_te_topo1/r2/zebra.conf create mode 100644 tests/topotests/ospf_te_topo1/r3/ospfd.conf create mode 100644 tests/topotests/ospf_te_topo1/r3/zebra.conf create mode 100644 tests/topotests/ospf_te_topo1/r4/ospfd.conf create mode 100644 tests/topotests/ospf_te_topo1/r4/zebra.conf create mode 100644 tests/topotests/ospf_te_topo1/reference/ted_step1.json create mode 100644 tests/topotests/ospf_te_topo1/reference/ted_step2.json create mode 100644 tests/topotests/ospf_te_topo1/reference/ted_step3.json create mode 100644 tests/topotests/ospf_te_topo1/reference/ted_step4.json create mode 100644 tests/topotests/ospf_te_topo1/reference/ted_step5.json create mode 100644 tests/topotests/ospf_te_topo1/reference/ted_step6.json create mode 100644 tests/topotests/ospf_te_topo1/reference/ted_step7.json create mode 100644 tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py create mode 100644 tests/topotests/ospf_tilfa_topo1/__init__.py create mode 100644 tests/topotests/ospf_tilfa_topo1/rt1/ospfd.conf create mode 100644 tests/topotests/ospf_tilfa_topo1/rt1/step1/show_ip_route_initial.ref create mode 100644 tests/topotests/ospf_tilfa_topo1/rt1/step2/show_ip_route_initial.ref create mode 100644 tests/topotests/ospf_tilfa_topo1/rt1/step2/show_ip_route_link_protection.ref create mode 100644 tests/topotests/ospf_tilfa_topo1/rt1/step3/show_ip_route_initial.ref create mode 100644 tests/topotests/ospf_tilfa_topo1/rt1/step3/show_ip_route_node_protection.ref create mode 100644 tests/topotests/ospf_tilfa_topo1/rt1/zebra.conf create mode 100644 tests/topotests/ospf_tilfa_topo1/rt2/ospfd.conf create mode 100644 tests/topotests/ospf_tilfa_topo1/rt2/zebra.conf create mode 100644 tests/topotests/ospf_tilfa_topo1/rt3/ospfd.conf create mode 100644 tests/topotests/ospf_tilfa_topo1/rt3/zebra.conf create mode 100644 tests/topotests/ospf_tilfa_topo1/rt4/ospfd.conf create mode 100644 tests/topotests/ospf_tilfa_topo1/rt4/zebra.conf create mode 100644 tests/topotests/ospf_tilfa_topo1/rt5/ospfd.conf create mode 100644 tests/topotests/ospf_tilfa_topo1/rt5/zebra.conf create mode 100644 tests/topotests/ospf_tilfa_topo1/test_ospf_tilfa_topo1.py create mode 100755 tests/topotests/ospf_topo1/__init__.py create mode 100644 tests/topotests/ospf_topo1/r1/ospf6d.conf create mode 100644 tests/topotests/ospf_topo1/r1/ospf6route.txt create mode 100644 tests/topotests/ospf_topo1/r1/ospf6route_down.txt create mode 100644 tests/topotests/ospf_topo1/r1/ospf6route_ecmp.txt create mode 100644 tests/topotests/ospf_topo1/r1/ospfd.conf create mode 100644 tests/topotests/ospf_topo1/r1/ospfroute.txt create mode 100644 tests/topotests/ospf_topo1/r1/ospfroute_down.txt create mode 100644 tests/topotests/ospf_topo1/r1/zebra.conf create mode 100644 tests/topotests/ospf_topo1/r2/ospf6d.conf create mode 100644 tests/topotests/ospf_topo1/r2/ospf6route.txt create mode 100644 tests/topotests/ospf_topo1/r2/ospf6route_down.txt create mode 100644 tests/topotests/ospf_topo1/r2/ospf6route_ecmp.txt create mode 100644 tests/topotests/ospf_topo1/r2/ospfd.conf create mode 100644 tests/topotests/ospf_topo1/r2/ospfroute.txt create mode 100644 tests/topotests/ospf_topo1/r2/ospfroute_down.txt create mode 100644 tests/topotests/ospf_topo1/r2/zebra.conf create mode 100644 tests/topotests/ospf_topo1/r3/ospf6d.conf create mode 100644 tests/topotests/ospf_topo1/r3/ospf6route.txt create mode 100644 tests/topotests/ospf_topo1/r3/ospf6route_down.txt create mode 100644 tests/topotests/ospf_topo1/r3/ospf6route_ecmp.txt create mode 100644 tests/topotests/ospf_topo1/r3/ospfd.conf create mode 100644 tests/topotests/ospf_topo1/r3/ospfroute.txt create mode 100644 tests/topotests/ospf_topo1/r3/ospfroute_down.txt create mode 100644 tests/topotests/ospf_topo1/r3/zebra.conf create mode 100644 tests/topotests/ospf_topo1/r4/ospf6d.conf create mode 100644 tests/topotests/ospf_topo1/r4/ospf6route.txt create mode 100644 tests/topotests/ospf_topo1/r4/ospf6route_down.txt create mode 100644 tests/topotests/ospf_topo1/r4/ospf6route_ecmp.txt create mode 100644 tests/topotests/ospf_topo1/r4/ospfd.conf create mode 100644 tests/topotests/ospf_topo1/r4/ospfroute.txt create mode 100644 tests/topotests/ospf_topo1/r4/ospfroute_down.txt create mode 100644 tests/topotests/ospf_topo1/r4/zebra.conf create mode 100644 tests/topotests/ospf_topo1/test_ospf_topo1.dot create mode 100644 tests/topotests/ospf_topo1/test_ospf_topo1.jpg create mode 100644 tests/topotests/ospf_topo1/test_ospf_topo1.py create mode 100644 tests/topotests/ospf_topo2/__init__.py create mode 100644 tests/topotests/ospf_topo2/r1/frr.conf create mode 100644 tests/topotests/ospf_topo2/r2/frr.conf create mode 100644 tests/topotests/ospf_topo2/r3/frr.conf create mode 100644 tests/topotests/ospf_topo2/r4/frr.conf create mode 100644 tests/topotests/ospf_topo2/test_ospf_topo2.dot create mode 100644 tests/topotests/ospf_topo2/test_ospf_topo2.png create mode 100644 tests/topotests/ospf_topo2/test_ospf_topo2.py create mode 100644 tests/topotests/ospf_unnumbered/r1/ospf-route.json create mode 100644 tests/topotests/ospf_unnumbered/r1/ospfd.conf create mode 100644 tests/topotests/ospf_unnumbered/r1/v4_route.json create mode 100644 tests/topotests/ospf_unnumbered/r1/zebra.conf create mode 100644 tests/topotests/ospf_unnumbered/r2/ospf-route.json create mode 100644 tests/topotests/ospf_unnumbered/r2/ospfd.conf create mode 100644 tests/topotests/ospf_unnumbered/r2/v4_route.json create mode 100644 tests/topotests/ospf_unnumbered/r2/zebra.conf create mode 100644 tests/topotests/ospf_unnumbered/test_ospf_unnumbered.py create mode 100755 tests/topotests/ospfapi/ctester.py create mode 120000 tests/topotests/ospfapi/lib create mode 100644 tests/topotests/ospfapi/r1/ospfd.conf create mode 100644 tests/topotests/ospfapi/r1/zebra.conf create mode 100644 tests/topotests/ospfapi/r2/ospfd.conf create mode 100644 tests/topotests/ospfapi/r2/zebra.conf create mode 100644 tests/topotests/ospfapi/r3/ospfd.conf create mode 100644 tests/topotests/ospfapi/r3/zebra.conf create mode 100644 tests/topotests/ospfapi/r4/ospfd.conf create mode 100644 tests/topotests/ospfapi/r4/zebra.conf create mode 100644 tests/topotests/ospfapi/test_ospf_clientapi.py create mode 100644 tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_topo1.json create mode 100644 tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_type7_lsa.json create mode 100644 tests/topotests/ospfv3_basic_functionality/ospfv3_authentication.json create mode 100644 tests/topotests/ospfv3_basic_functionality/ospfv3_dual_stack.json create mode 100644 tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json create mode 100644 tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp_lan.json create mode 100644 tests/topotests/ospfv3_basic_functionality/ospfv3_lan.json create mode 100644 tests/topotests/ospfv3_basic_functionality/ospfv3_nssa.json create mode 100644 tests/topotests/ospfv3_basic_functionality/ospfv3_nssa2.json create mode 100644 tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json create mode 100644 tests/topotests/ospfv3_basic_functionality/ospfv3_rte_calc.json create mode 100644 tests/topotests/ospfv3_basic_functionality/ospfv3_single_area.json create mode 100644 tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py create mode 100644 tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py create mode 100644 tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py create mode 100644 tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py create mode 100644 tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa.py create mode 100644 tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py create mode 100644 tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py create mode 100644 tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py create mode 100644 tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py create mode 100644 tests/topotests/pbr_topo1/__init__.py create mode 100644 tests/topotests/pbr_topo1/r1/linux-rules.json create mode 100644 tests/topotests/pbr_topo1/r1/pbr-interface.json create mode 100644 tests/topotests/pbr_topo1/r1/pbr-map.json create mode 100644 tests/topotests/pbr_topo1/r1/pbr-nexthop-groups.json create mode 100644 tests/topotests/pbr_topo1/r1/pbrd.conf create mode 100644 tests/topotests/pbr_topo1/r1/zebra.conf create mode 100644 tests/topotests/pbr_topo1/test_pbr_topo1.py create mode 100644 tests/topotests/pim_acl/h1/zebra.conf create mode 100644 tests/topotests/pim_acl/h2/zebra.conf create mode 100644 tests/topotests/pim_acl/r1/acl_1_pim_join.json create mode 100644 tests/topotests/pim_acl/r1/acl_2_pim_join.json create mode 100644 tests/topotests/pim_acl/r1/acl_3_pim_join.json create mode 100644 tests/topotests/pim_acl/r1/acl_4_pim_join.json create mode 100644 tests/topotests/pim_acl/r1/acl_5_pim_join.json create mode 100644 tests/topotests/pim_acl/r1/acl_6_pim_join.json create mode 100644 tests/topotests/pim_acl/r1/ospf_neighbor.json create mode 100644 tests/topotests/pim_acl/r1/ospfd.conf create mode 100644 tests/topotests/pim_acl/r1/pim_neighbor.json create mode 100644 tests/topotests/pim_acl/r1/pimd.conf create mode 100644 tests/topotests/pim_acl/r1/zebra.conf create mode 100644 tests/topotests/pim_acl/r11/acl_1_pim_join.json create mode 100644 tests/topotests/pim_acl/r11/ospfd.conf create mode 100644 tests/topotests/pim_acl/r11/pimd.conf create mode 100644 tests/topotests/pim_acl/r11/zebra.conf create mode 100644 tests/topotests/pim_acl/r12/acl_2_pim_join.json create mode 100644 tests/topotests/pim_acl/r12/ospfd.conf create mode 100644 tests/topotests/pim_acl/r12/pimd.conf create mode 100644 tests/topotests/pim_acl/r12/zebra.conf create mode 100644 tests/topotests/pim_acl/r13/acl_3_pim_join.json create mode 100644 tests/topotests/pim_acl/r13/ospfd.conf create mode 100644 tests/topotests/pim_acl/r13/pimd.conf create mode 100644 tests/topotests/pim_acl/r13/zebra.conf create mode 100644 tests/topotests/pim_acl/r14/acl_4_pim_join.json create mode 100644 tests/topotests/pim_acl/r14/acl_5_pim_join.json create mode 100644 tests/topotests/pim_acl/r14/ospfd.conf create mode 100644 tests/topotests/pim_acl/r14/pimd.conf create mode 100644 tests/topotests/pim_acl/r14/zebra.conf create mode 100644 tests/topotests/pim_acl/r15/acl_6_pim_join.json create mode 100644 tests/topotests/pim_acl/r15/ospfd.conf create mode 100644 tests/topotests/pim_acl/r15/pimd.conf create mode 100644 tests/topotests/pim_acl/r15/zebra.conf create mode 100755 tests/topotests/pim_acl/test_pim_acl.py create mode 100755 tests/topotests/pim_basic/mcast-rx.py create mode 100755 tests/topotests/pim_basic/mcast-tx.py create mode 100644 tests/topotests/pim_basic/r1/bgpd.conf create mode 100644 tests/topotests/pim_basic/r1/pimd.conf create mode 100644 tests/topotests/pim_basic/r1/rp-info.json create mode 100644 tests/topotests/pim_basic/r1/zebra.conf create mode 100644 tests/topotests/pim_basic/r2/pimd.conf create mode 100644 tests/topotests/pim_basic/r2/zebra.conf create mode 100644 tests/topotests/pim_basic/r3/pimd.conf create mode 100644 tests/topotests/pim_basic/r3/zebra.conf create mode 100644 tests/topotests/pim_basic/rp/bgpd.conf create mode 100644 tests/topotests/pim_basic/rp/pimd.conf create mode 100644 tests/topotests/pim_basic/rp/upstream.json create mode 100644 tests/topotests/pim_basic/rp/zebra.conf create mode 100644 tests/topotests/pim_basic/test_pim.py create mode 100644 tests/topotests/pim_basic_topo2/__init__.py create mode 100644 tests/topotests/pim_basic_topo2/r1/bfdd.conf create mode 100644 tests/topotests/pim_basic_topo2/r1/pimd.conf create mode 100644 tests/topotests/pim_basic_topo2/r1/zebra.conf create mode 100644 tests/topotests/pim_basic_topo2/r2/bfdd.conf create mode 100644 tests/topotests/pim_basic_topo2/r2/pimd.conf create mode 100644 tests/topotests/pim_basic_topo2/r2/zebra.conf create mode 100644 tests/topotests/pim_basic_topo2/r3/bfdd.conf create mode 100644 tests/topotests/pim_basic_topo2/r3/pimd.conf create mode 100644 tests/topotests/pim_basic_topo2/r3/zebra.conf create mode 100644 tests/topotests/pim_basic_topo2/r4/bfdd.conf create mode 100644 tests/topotests/pim_basic_topo2/r4/pimd.conf create mode 100644 tests/topotests/pim_basic_topo2/r4/zebra.conf create mode 100644 tests/topotests/pim_basic_topo2/test_pim_basic_topo2.dot create mode 100644 tests/topotests/pim_basic_topo2/test_pim_basic_topo2.png create mode 100644 tests/topotests/pim_basic_topo2/test_pim_basic_topo2.py create mode 100644 tests/topotests/pim_igmp_vrf/h1/zebra.conf create mode 100644 tests/topotests/pim_igmp_vrf/h2/zebra.conf create mode 100644 tests/topotests/pim_igmp_vrf/h3/zebra.conf create mode 100644 tests/topotests/pim_igmp_vrf/h4/zebra.conf create mode 100644 tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json create mode 100644 tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json create mode 100644 tests/topotests/pim_igmp_vrf/r1/ospfd.conf create mode 100644 tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json create mode 100644 tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json create mode 100644 tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json create mode 100644 tests/topotests/pim_igmp_vrf/r1/pim_red_join.json create mode 100644 tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json create mode 100644 tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json create mode 100644 tests/topotests/pim_igmp_vrf/r1/pimd.conf create mode 100644 tests/topotests/pim_igmp_vrf/r1/zebra.conf create mode 100644 tests/topotests/pim_igmp_vrf/r11/ospfd.conf create mode 100644 tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json create mode 100644 tests/topotests/pim_igmp_vrf/r11/pimd.conf create mode 100644 tests/topotests/pim_igmp_vrf/r11/zebra.conf create mode 100644 tests/topotests/pim_igmp_vrf/r12/ospfd.conf create mode 100644 tests/topotests/pim_igmp_vrf/r12/pim_red_join.json create mode 100644 tests/topotests/pim_igmp_vrf/r12/pimd.conf create mode 100644 tests/topotests/pim_igmp_vrf/r12/zebra.conf create mode 100755 tests/topotests/pim_igmp_vrf/test_pim_vrf.py create mode 100644 tests/topotests/pytest.ini create mode 100644 tests/topotests/rip_allow_ecmp/__init__.py create mode 100644 tests/topotests/rip_allow_ecmp/r1/frr.conf create mode 100644 tests/topotests/rip_allow_ecmp/r2/frr.conf create mode 100644 tests/topotests/rip_allow_ecmp/r3/frr.conf create mode 100644 tests/topotests/rip_allow_ecmp/r4/frr.conf create mode 100644 tests/topotests/rip_allow_ecmp/r5/frr.conf create mode 100644 tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py create mode 100755 tests/topotests/rip_bfd_topo1/__init__.py create mode 100644 tests/topotests/rip_bfd_topo1/r1/bfdd.conf create mode 100644 tests/topotests/rip_bfd_topo1/r1/ripd.conf create mode 100644 tests/topotests/rip_bfd_topo1/r1/zebra.conf create mode 100644 tests/topotests/rip_bfd_topo1/r2/bfdd.conf create mode 100644 tests/topotests/rip_bfd_topo1/r2/ripd.conf create mode 100644 tests/topotests/rip_bfd_topo1/r2/staticd.conf create mode 100644 tests/topotests/rip_bfd_topo1/r2/zebra.conf create mode 100644 tests/topotests/rip_bfd_topo1/r3/bfdd.conf create mode 100644 tests/topotests/rip_bfd_topo1/r3/ripd.conf create mode 100644 tests/topotests/rip_bfd_topo1/r3/staticd.conf create mode 100644 tests/topotests/rip_bfd_topo1/r3/zebra.conf create mode 100644 tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot create mode 100644 tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png create mode 100644 tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py create mode 100644 tests/topotests/rip_passive_interface/__init__.py create mode 100644 tests/topotests/rip_passive_interface/r1/frr.conf create mode 100644 tests/topotests/rip_passive_interface/r2/frr.conf create mode 100644 tests/topotests/rip_passive_interface/r3/frr.conf create mode 100644 tests/topotests/rip_passive_interface/test_rip_passive_interface.py create mode 100644 tests/topotests/rip_topo1/r1/rip_status.ref create mode 100644 tests/topotests/rip_topo1/r1/ripd.conf create mode 100644 tests/topotests/rip_topo1/r1/show_ip_rip.ref create mode 100644 tests/topotests/rip_topo1/r1/show_ip_route.ref create mode 100644 tests/topotests/rip_topo1/r1/zebra.conf create mode 100644 tests/topotests/rip_topo1/r2/rip_status.ref create mode 100644 tests/topotests/rip_topo1/r2/ripd.conf create mode 100644 tests/topotests/rip_topo1/r2/show_ip_rip.ref create mode 100644 tests/topotests/rip_topo1/r2/show_ip_route.ref create mode 100644 tests/topotests/rip_topo1/r2/zebra.conf create mode 100644 tests/topotests/rip_topo1/r3/rip_status.ref create mode 100644 tests/topotests/rip_topo1/r3/ripd.conf create mode 100644 tests/topotests/rip_topo1/r3/show_ip_rip.ref create mode 100644 tests/topotests/rip_topo1/r3/show_ip_route.ref create mode 100644 tests/topotests/rip_topo1/r3/zebra.conf create mode 100644 tests/topotests/rip_topo1/test_rip_topo1.dot create mode 100644 tests/topotests/rip_topo1/test_rip_topo1.pdf create mode 100644 tests/topotests/rip_topo1/test_rip_topo1.py create mode 100644 tests/topotests/ripng_allow_ecmp/__init__.py create mode 100644 tests/topotests/ripng_allow_ecmp/r1/frr.conf create mode 100644 tests/topotests/ripng_allow_ecmp/r2/frr.conf create mode 100644 tests/topotests/ripng_allow_ecmp/r3/frr.conf create mode 100644 tests/topotests/ripng_allow_ecmp/r4/frr.conf create mode 100644 tests/topotests/ripng_allow_ecmp/r5/frr.conf create mode 100644 tests/topotests/ripng_allow_ecmp/test_ripng_allow_ecmp.py create mode 100644 tests/topotests/ripng_route_map/__init__.py create mode 100644 tests/topotests/ripng_route_map/r1/frr.conf create mode 100644 tests/topotests/ripng_route_map/r2/frr.conf create mode 100644 tests/topotests/ripng_route_map/r3/frr.conf create mode 100644 tests/topotests/ripng_route_map/test_ripng_route_map.py create mode 100644 tests/topotests/ripng_topo1/r1/ripng_status.ref create mode 100644 tests/topotests/ripng_topo1/r1/ripngd.conf create mode 100644 tests/topotests/ripng_topo1/r1/show_ipv6_ripng.ref create mode 100644 tests/topotests/ripng_topo1/r1/show_ipv6_route.ref create mode 100644 tests/topotests/ripng_topo1/r1/zebra.conf create mode 100644 tests/topotests/ripng_topo1/r2/ripng_status.ref create mode 100644 tests/topotests/ripng_topo1/r2/ripngd.conf create mode 100644 tests/topotests/ripng_topo1/r2/show_ipv6_ripng.ref create mode 100644 tests/topotests/ripng_topo1/r2/show_ipv6_route.ref create mode 100644 tests/topotests/ripng_topo1/r2/zebra.conf create mode 100644 tests/topotests/ripng_topo1/r3/ripng_status.ref create mode 100644 tests/topotests/ripng_topo1/r3/ripngd.conf create mode 100644 tests/topotests/ripng_topo1/r3/show_ipv6_ripng.ref create mode 100644 tests/topotests/ripng_topo1/r3/show_ipv6_route.ref create mode 100644 tests/topotests/ripng_topo1/r3/zebra.conf create mode 100644 tests/topotests/ripng_topo1/test_ripng_topo1.dot create mode 100644 tests/topotests/ripng_topo1/test_ripng_topo1.pdf create mode 100644 tests/topotests/ripng_topo1/test_ripng_topo1.py create mode 100644 tests/topotests/route_scale/r1/installed.routes.json create mode 100644 tests/topotests/route_scale/r1/no.routes.json create mode 100644 tests/topotests/route_scale/r1/sharpd.conf create mode 100644 tests/topotests/route_scale/r1/zebra.conf create mode 100644 tests/topotests/route_scale/scale_test_common.py create mode 100644 tests/topotests/route_scale/test_route_scale1.py create mode 100644 tests/topotests/route_scale/test_route_scale2.py create mode 100644 tests/topotests/simple_snmp_test/r1/bgpd.conf create mode 100644 tests/topotests/simple_snmp_test/r1/isisd.conf create mode 100644 tests/topotests/simple_snmp_test/r1/snmpd.conf create mode 100644 tests/topotests/simple_snmp_test/r1/zebra.conf create mode 100755 tests/topotests/simple_snmp_test/test_simple_snmp.py create mode 100644 tests/topotests/srv6_locator/__init__.py create mode 100644 tests/topotests/srv6_locator/expected_chunks1.json create mode 100644 tests/topotests/srv6_locator/expected_chunks2.json create mode 100644 tests/topotests/srv6_locator/expected_chunks3.json create mode 100644 tests/topotests/srv6_locator/expected_chunks4.json create mode 100644 tests/topotests/srv6_locator/expected_chunks5.json create mode 100644 tests/topotests/srv6_locator/expected_chunks6.json create mode 100644 tests/topotests/srv6_locator/expected_ipv6_routes.json create mode 100644 tests/topotests/srv6_locator/expected_locators1.json create mode 100644 tests/topotests/srv6_locator/expected_locators2.json create mode 100644 tests/topotests/srv6_locator/expected_locators3.json create mode 100644 tests/topotests/srv6_locator/expected_locators4.json create mode 100644 tests/topotests/srv6_locator/expected_locators5.json create mode 100644 tests/topotests/srv6_locator/expected_locators6.json create mode 100644 tests/topotests/srv6_locator/r1/setup.sh create mode 100644 tests/topotests/srv6_locator/r1/sharpd.conf create mode 100644 tests/topotests/srv6_locator/r1/zebra.conf create mode 100755 tests/topotests/srv6_locator/test_srv6_locator.py create mode 100644 tests/topotests/srv6_locator_custom_bits_length/__init__.py create mode 100644 tests/topotests/srv6_locator_custom_bits_length/expected_chunks1.json create mode 100644 tests/topotests/srv6_locator_custom_bits_length/expected_chunks2.json create mode 100644 tests/topotests/srv6_locator_custom_bits_length/expected_chunks3.json create mode 100644 tests/topotests/srv6_locator_custom_bits_length/expected_chunks4.json create mode 100644 tests/topotests/srv6_locator_custom_bits_length/expected_chunks5.json create mode 100644 tests/topotests/srv6_locator_custom_bits_length/expected_chunks6.json create mode 100644 tests/topotests/srv6_locator_custom_bits_length/expected_locators1.json create mode 100644 tests/topotests/srv6_locator_custom_bits_length/expected_locators2.json create mode 100644 tests/topotests/srv6_locator_custom_bits_length/expected_locators3.json create mode 100644 tests/topotests/srv6_locator_custom_bits_length/expected_locators4.json create mode 100644 tests/topotests/srv6_locator_custom_bits_length/expected_locators5.json create mode 100644 tests/topotests/srv6_locator_custom_bits_length/expected_locators6.json create mode 100644 tests/topotests/srv6_locator_custom_bits_length/r1/setup.sh create mode 100644 tests/topotests/srv6_locator_custom_bits_length/r1/sharpd.conf create mode 100644 tests/topotests/srv6_locator_custom_bits_length/r1/zebra.conf create mode 100755 tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator.py create mode 100644 tests/topotests/srv6_locator_usid/__init__.py create mode 100644 tests/topotests/srv6_locator_usid/expected_chunks_1.json create mode 100644 tests/topotests/srv6_locator_usid/expected_chunks_2.json create mode 100644 tests/topotests/srv6_locator_usid/expected_chunks_3.json create mode 100644 tests/topotests/srv6_locator_usid/expected_chunks_4.json create mode 100644 tests/topotests/srv6_locator_usid/expected_chunks_5.json create mode 100644 tests/topotests/srv6_locator_usid/expected_chunks_6.json create mode 100644 tests/topotests/srv6_locator_usid/expected_chunks_7.json create mode 100644 tests/topotests/srv6_locator_usid/expected_chunks_8.json create mode 100644 tests/topotests/srv6_locator_usid/expected_locators_1.json create mode 100644 tests/topotests/srv6_locator_usid/expected_locators_2.json create mode 100644 tests/topotests/srv6_locator_usid/expected_locators_3.json create mode 100644 tests/topotests/srv6_locator_usid/expected_locators_4.json create mode 100644 tests/topotests/srv6_locator_usid/expected_locators_5.json create mode 100644 tests/topotests/srv6_locator_usid/expected_locators_6.json create mode 100644 tests/topotests/srv6_locator_usid/expected_locators_7.json create mode 100644 tests/topotests/srv6_locator_usid/expected_locators_8.json create mode 100644 tests/topotests/srv6_locator_usid/r1/setup.sh create mode 100644 tests/topotests/srv6_locator_usid/r1/sharpd.conf create mode 100644 tests/topotests/srv6_locator_usid/r1/zebra.conf create mode 100755 tests/topotests/srv6_locator_usid/test_srv6_locator_usid.py create mode 100644 tests/topotests/srv6_static_route/__init__.py create mode 100644 tests/topotests/srv6_static_route/expected_srv6_route.json create mode 100644 tests/topotests/srv6_static_route/r1/mgmtd.conf create mode 100644 tests/topotests/srv6_static_route/r1/setup.sh create mode 100644 tests/topotests/srv6_static_route/r1/staticd.conf create mode 100644 tests/topotests/srv6_static_route/r1/zebra.conf create mode 100755 tests/topotests/srv6_static_route/test_srv6_route.py create mode 100644 tests/topotests/static_routing_mpls/r1/zebra.conf create mode 100644 tests/topotests/static_routing_mpls/r2/zebra.conf create mode 100644 tests/topotests/static_routing_mpls/test_static_routing_mpls.py create mode 100644 tests/topotests/static_routing_with_ebgp/static_routes_topo1_ebgp.json create mode 100644 tests/topotests/static_routing_with_ebgp/static_routes_topo2_ebgp.json create mode 100644 tests/topotests/static_routing_with_ebgp/static_routes_topo3_ebgp.json create mode 100644 tests/topotests/static_routing_with_ebgp/static_routes_topo4_ebgp.json create mode 100644 tests/topotests/static_routing_with_ebgp/test_static_routes_topo1_ebgp.py create mode 100644 tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py create mode 100644 tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py create mode 100644 tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py create mode 100644 tests/topotests/static_routing_with_ibgp/static_routes_topo1_ibgp.json create mode 100644 tests/topotests/static_routing_with_ibgp/static_routes_topo2_ibgp.json create mode 100644 tests/topotests/static_routing_with_ibgp/static_routes_topo3_ibgp.json create mode 100644 tests/topotests/static_routing_with_ibgp/static_routes_topo4_ibgp.json create mode 100644 tests/topotests/static_routing_with_ibgp/test_static_routes_topo1_ibgp.py create mode 100644 tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py create mode 100644 tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py create mode 100644 tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py create mode 100644 tests/topotests/static_simple/r1/mgmtd.conf create mode 100644 tests/topotests/static_simple/r1/staticd.conf create mode 100644 tests/topotests/static_simple/r1/zebra.conf create mode 100644 tests/topotests/static_simple/test_static_simple.py create mode 100644 tests/topotests/subdir.am create mode 100755 tests/topotests/tc_basic/test_tc_basic.py create mode 100644 tests/topotests/zebra_multiple_connected/r1/ip_route.json create mode 100644 tests/topotests/zebra_multiple_connected/r1/ip_route2.json create mode 100644 tests/topotests/zebra_multiple_connected/r1/zebra.conf create mode 100644 tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py create mode 100644 tests/topotests/zebra_netlink/__init__.py create mode 100644 tests/topotests/zebra_netlink/r1/zebra.conf create mode 100644 tests/topotests/zebra_netlink/test_zebra_netlink.py create mode 100644 tests/topotests/zebra_nht_resolution/r1/sharpd.conf create mode 100644 tests/topotests/zebra_nht_resolution/r1/zebra.conf create mode 100644 tests/topotests/zebra_nht_resolution/test_verify_nh_resolution.py create mode 100644 tests/topotests/zebra_opaque/__init__.py create mode 100644 tests/topotests/zebra_opaque/r1/bgpd.conf create mode 100644 tests/topotests/zebra_opaque/r1/zebra.conf create mode 100644 tests/topotests/zebra_opaque/r2/bgpd.conf create mode 100644 tests/topotests/zebra_opaque/r2/zebra.conf create mode 100644 tests/topotests/zebra_opaque/r3/ospf6d.conf create mode 100644 tests/topotests/zebra_opaque/r3/ospfd.conf create mode 100644 tests/topotests/zebra_opaque/r3/zebra.conf create mode 100644 tests/topotests/zebra_opaque/r4/ospf6d.conf create mode 100644 tests/topotests/zebra_opaque/r4/ospfd.conf create mode 100644 tests/topotests/zebra_opaque/r4/zebra.conf create mode 100644 tests/topotests/zebra_opaque/test_zebra_opaque.py create mode 100644 tests/topotests/zebra_rib/r1/iproute.ref create mode 100644 tests/topotests/zebra_rib/r1/sharp_rmap.ref create mode 100644 tests/topotests/zebra_rib/r1/static_rmap.ref create mode 100644 tests/topotests/zebra_rib/r1/v4_route_1.json create mode 100644 tests/topotests/zebra_rib/r1/v4_route_1_static_override.json create mode 100644 tests/topotests/zebra_rib/r1/v4_route_1_vrf_before.json create mode 100644 tests/topotests/zebra_rib/r1/v4_route_2.json create mode 100644 tests/topotests/zebra_rib/r1/zebra.conf create mode 100644 tests/topotests/zebra_rib/test_zebra_rib.py create mode 100644 tests/topotests/zebra_seg6_route/r1/routes.json create mode 100644 tests/topotests/zebra_seg6_route/r1/setup.sh create mode 100644 tests/topotests/zebra_seg6_route/r1/sharpd.conf create mode 100644 tests/topotests/zebra_seg6_route/r1/zebra.conf create mode 100755 tests/topotests/zebra_seg6_route/test_zebra_seg6_route.py create mode 100644 tests/topotests/zebra_seg6local_route/r1/routes.json create mode 100644 tests/topotests/zebra_seg6local_route/r1/setup.sh create mode 100644 tests/topotests/zebra_seg6local_route/r1/sharpd.conf create mode 100644 tests/topotests/zebra_seg6local_route/r1/zebra.conf create mode 100755 tests/topotests/zebra_seg6local_route/test_zebra_seg6local_route.py (limited to 'tests/topotests') diff --git a/tests/topotests/.gitignore b/tests/topotests/.gitignore new file mode 100644 index 0000000..b1e3c30 --- /dev/null +++ b/tests/topotests/.gitignore @@ -0,0 +1,4 @@ +.cache +__pycache__ +*.pyc +.pytest_cache diff --git a/tests/topotests/Dockerfile b/tests/topotests/Dockerfile new file mode 100644 index 0000000..1503e67 --- /dev/null +++ b/tests/topotests/Dockerfile @@ -0,0 +1,87 @@ +FROM ubuntu:18.04 + +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-get update \ + && apt-get install -y \ + autoconf \ + binutils \ + bison \ + ca-certificates \ + flex \ + gdb \ + git \ + gpg \ + install-info \ + iputils-ping \ + iproute2 \ + less \ + libtool \ + libjson-c-dev \ + libpcre3-dev \ + libpython-dev \ + libpython3-dev \ + libreadline-dev \ + libc-ares-dev \ + libcap-dev \ + libelf-dev \ + man \ + mininet \ + pkg-config \ + python-pip \ + python3 \ + python3-dev \ + python3-sphinx \ + python3-pytest \ + rsync \ + strace \ + tcpdump \ + texinfo \ + tmux \ + valgrind \ + vim \ + wget \ + x11-xserver-utils \ + xterm \ + && pip install \ + exabgp==3.4.17 \ + "scapy>=2.4.2" \ + ipaddr \ + pytest \ + && rm -rf /var/lib/apt/lists/* + +RUN export DEBIAN_FRONTEND=noninteractive \ + && wget -qO- https://deb.frrouting.org/frr/keys.asc | apt-key add - \ + && echo "deb https://deb.frrouting.org/frr bionic frr-stable" > /etc/apt/sources.list.d/frr.list \ + && apt-get update \ + && apt-get install -y libyang-dev \ + && rm -rf /var/lib/apt/lists/* + +RUN groupadd -r -g 92 frr \ + && groupadd -r -g 85 frrvty \ + && useradd -c "FRRouting suite" \ + -d /var/run/frr \ + -g frr \ + -G frrvty \ + -r \ + -s /sbin/nologin \ + frr \ + && useradd -d /var/run/exabgp/ \ + -s /bin/false \ + exabgp + +# Configure coredumps +RUN echo "" >> /etc/security/limits.conf; \ + echo "* soft core unlimited" >> /etc/security/limits.conf; \ + echo "root soft core unlimited" >> /etc/security/limits.conf; \ + echo "* hard core unlimited" >> /etc/security/limits.conf; \ + echo "root hard core unlimited" >> /etc/security/limits.conf + +# Copy run scripts to facilitate users wanting to run the tests +COPY docker/inner /opt/topotests + +ENV PATH "$PATH:/opt/topotests" + +RUN echo "cat /opt/topotests/motd.txt" >> /root/.profile && \ + echo "export PS1='(topotests) $PS1'" >> /root/.profile + +ENTRYPOINT [ "bash", "/opt/topotests/entrypoint.sh" ] diff --git a/tests/topotests/README.md b/tests/topotests/README.md new file mode 100644 index 0000000..d9d849b --- /dev/null +++ b/tests/topotests/README.md @@ -0,0 +1 @@ +Documentation is located in /doc/developer/topotests.rst diff --git a/tests/topotests/all_protocol_startup/r1/babeld.conf b/tests/topotests/all_protocol_startup/r1/babeld.conf new file mode 100644 index 0000000..3e119bf --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/babeld.conf @@ -0,0 +1,4 @@ +router babel + network 192.168.1.1 + network 192.168.2.1 +! \ No newline at end of file diff --git a/tests/topotests/all_protocol_startup/r1/bgpd.conf b/tests/topotests/all_protocol_startup/r1/bgpd.conf new file mode 100644 index 0000000..32dcb72 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/bgpd.conf @@ -0,0 +1,60 @@ +log file bgpd.log +! +! +router bgp 100 + bgp router-id 192.168.0.1 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.7.10 remote-as 100 + neighbor 192.168.7.10 timers 3 10 + neighbor 192.168.7.20 remote-as 200 + neighbor 192.168.7.20 timers 3 10 + neighbor fc00:0:0:8::1000 remote-as 100 + neighbor fc00:0:0:8::1000 timers 3 10 + neighbor fc00:0:0:8::2000 remote-as 200 + neighbor 192.168.7.10 description Transit_cogent + neighbor 192.168.7.20 description Client_Bibi_Full + neighbor fc00:0:0:8::1000 description Transit_cogent_v6 + neighbor fc00:0:0:8::2000 description Client_Toto_default + neighbor fc00:0:0:8::2000 timers 3 10 + ! + address-family ipv4 unicast + network 192.168.0.0/24 + neighbor 192.168.7.10 route-map bgp-map in + neighbor 192.168.7.10 filter-list bgp-filter-v4 out + neighbor 192.168.7.20 route-map bgp-map in + neighbor 192.168.7.20 filter-list bgp-filter-v4 out + exit-address-family + ! + address-family ipv6 unicast + network fc00::/64 + neighbor fc00:0:0:8::1000 activate + neighbor fc00:0:0:8::1000 route-map bgp-map in + neighbor fc00:0:0:8::1000 filter-list bgp-filter-v6 out + neighbor fc00:0:0:8::2000 activate + neighbor fc00:0:0:8::2000 route-map bgp-map in + neighbor fc00:0:0:8::2000 filter-list bgp-filter-v6 out + exit-address-family +! +! +ip prefix-list bgp-filter-v4 description dummy-test-prefix-list +ip prefix-list bgp-filter-v4 seq 5 permit 192.168.0.0/24 +! +ipv6 prefix-list bgp-filter-v4 seq 5 permit fc00::/64 +ipv6 prefix-list bgp-filter-v6 description dummy-test-prefix-list-v6 +! +route-map bgp-map permit 10 + set community 100:100 additive + set local-preference 100 +! +route-map bgp-map permit 20 + set metric 10 + set local-preference 200 +! +line vty +! + +route-map LIES deny 10 + match interface notpresent +! diff --git a/tests/topotests/all_protocol_startup/r1/ip_nht.ref b/tests/topotests/all_protocol_startup/r1/ip_nht.ref new file mode 100644 index 0000000..a2f3d3b --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/ip_nht.ref @@ -0,0 +1,74 @@ +VRF default: + Resolve via default: on +1.1.1.1 + resolved via static + is directly connected, r1-eth1 (vrf default), weight 1 + Client list: pbr(fd XX) +1.1.1.2 + resolved via static + is directly connected, r1-eth2 (vrf default), weight 1 + Client list: pbr(fd XX) +1.1.1.3 + resolved via static + is directly connected, r1-eth3 (vrf default), weight 1 + Client list: pbr(fd XX) +1.1.1.4 + resolved via static + is directly connected, r1-eth4 (vrf default), weight 1 + Client list: pbr(fd XX) +1.1.1.5 + resolved via static + is directly connected, r1-eth5 (vrf default), weight 1 + Client list: pbr(fd XX) +1.1.1.6 + resolved via static + is directly connected, r1-eth6 (vrf default), weight 1 + Client list: pbr(fd XX) +1.1.1.7 + resolved via static + is directly connected, r1-eth7 (vrf default), weight 1 + Client list: pbr(fd XX) +1.1.1.8 + resolved via static + is directly connected, r1-eth8 (vrf default), weight 1 + Client list: pbr(fd XX) +2.2.2.1 + unresolved + Client list: pbr(fd XX) +4.4.4.1 + unresolved + Client list: pbr(fd XX) +4.4.4.2 + unresolved + Client list: pbr(fd XX) +6.6.6.1 + unresolved + Client list: pbr(fd XX) +6.6.6.2 + unresolved + Client list: pbr(fd XX) +6.6.6.3 + unresolved + Client list: pbr(fd XX) +6.6.6.4 + unresolved + Client list: pbr(fd XX) +192.168.0.2 + resolved via connected + is directly connected, r1-eth0 (vrf default) + Client list: static(fd XX) +192.168.0.4 + resolved via connected + is directly connected, r1-eth0 (vrf default) + Client list: static(fd XX) +192.168.7.10 + resolved via connected + is directly connected, r1-eth7 (vrf default) + Client list: bgp(fd XX) +192.168.7.20(Connected) + resolved via connected + is directly connected, r1-eth7 (vrf default) + Client list: bgp(fd XX) +192.168.161.4 + unresolved + Client list: pbr(fd XX) diff --git a/tests/topotests/all_protocol_startup/r1/ipv4_routes.ref b/tests/topotests/all_protocol_startup/r1/ipv4_routes.ref new file mode 100644 index 0000000..044cffa --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/ipv4_routes.ref @@ -0,0 +1,32 @@ +C>* 192.168.0.0/24 is directly connected, r1-eth0, XX:XX:XX +C>* 192.168.1.0/26 is directly connected, r1-eth1, XX:XX:XX +C>* 192.168.2.0/26 is directly connected, r1-eth2, XX:XX:XX +C>* 192.168.3.0/26 is directly connected, r1-eth3, XX:XX:XX +C>* 192.168.4.0/26 is directly connected, r1-eth4, XX:XX:XX +C>* 192.168.5.0/26 is directly connected, r1-eth5, XX:XX:XX +C>* 192.168.6.0/26 is directly connected, r1-eth6, XX:XX:XX +C>* 192.168.7.0/26 is directly connected, r1-eth7, XX:XX:XX +C>* 192.168.8.0/26 is directly connected, r1-eth8, XX:XX:XX +C>* 192.168.9.0/26 is directly connected, r1-eth9, XX:XX:XX +O 192.168.0.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX +O 192.168.3.0/26 [110/10] is directly connected, r1-eth3, weight 1, XX:XX:XX +S>* 1.1.1.1/32 [1/0] is directly connected, r1-eth1, weight 1, XX:XX:XX +S>* 1.1.1.2/32 [1/0] is directly connected, r1-eth2, weight 1, XX:XX:XX +S>* 1.1.1.3/32 [1/0] is directly connected, r1-eth3, weight 1, XX:XX:XX +S>* 1.1.1.4/32 [1/0] is directly connected, r1-eth4, weight 1, XX:XX:XX +S>* 1.1.1.5/32 [1/0] is directly connected, r1-eth5, weight 1, XX:XX:XX +S>* 1.1.1.6/32 [1/0] is directly connected, r1-eth6, weight 1, XX:XX:XX +S>* 1.1.1.7/32 [1/0] is directly connected, r1-eth7, weight 1, XX:XX:XX +S>* 1.1.1.8/32 [1/0] is directly connected, r1-eth8, weight 1, XX:XX:XX +S>* 4.5.6.10/32 [1/0] via 192.168.0.2, r1-eth0, weight 1, XX:XX:XX +S>* 4.5.6.11/32 [1/0] via 192.168.0.2, r1-eth0, weight 1, XX:XX:XX +S>* 4.5.6.12/32 [1/0] is directly connected, r1-eth0, weight 1, XX:XX:XX +S>* 4.5.6.13/32 [1/0] unreachable (blackhole), weight 1, XX:XX:XX +S>* 4.5.6.14/32 [1/0] unreachable (blackhole), weight 1, XX:XX:XX +S 4.5.6.15/32 [255/0] via 192.168.0.2, r1-eth0, weight 1, XX:XX:XX +S 4.5.6.16/32 [10/0] via 192.168.0.4, r1-eth0, weight 1, XX:XX:XX +S>* 4.5.6.16/32 [5/0] via 192.168.0.2, r1-eth0, weight 1, XX:XX:XX +S>* 4.5.6.17/32 [1/0] via 192.168.0.2, r1-eth0, weight 1, XX:XX:XX +S>* 4.5.6.7/32 [1/0] unreachable (blackhole), weight 1, XX:XX:XX +S>* 4.5.6.8/32 [1/0] unreachable (blackhole), weight 1, XX:XX:XX +S>* 4.5.6.9/32 [1/0] unreachable (ICMP unreachable), weight 1, XX:XX:XX diff --git a/tests/topotests/all_protocol_startup/r1/ipv6_nht.ref b/tests/topotests/all_protocol_startup/r1/ipv6_nht.ref new file mode 100644 index 0000000..100a36a --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/ipv6_nht.ref @@ -0,0 +1,15 @@ +VRF default: + Resolve via default: on +fc00::2 + resolved via connected + is directly connected, r1-eth0 (vrf default) + Client list: static(fd XX) +fc00:0:0:8::1000 + resolved via connected + is directly connected, r1-eth8 (vrf default) + Client list: bgp(fd XX) +fc00:0:0:8::2000(Connected) + resolved via connected + is directly connected, r1-eth8 (vrf default) + Client list: bgp(fd XX) + diff --git a/tests/topotests/all_protocol_startup/r1/ipv6_routes.ref b/tests/topotests/all_protocol_startup/r1/ipv6_routes.ref new file mode 100644 index 0000000..ef12d61 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/ipv6_routes.ref @@ -0,0 +1,29 @@ +C>* fc00:0:0:1::/64 is directly connected, r1-eth1, XX:XX:XX +C>* fc00:0:0:2::/64 is directly connected, r1-eth2, XX:XX:XX +C>* fc00:0:0:3::/64 is directly connected, r1-eth3, XX:XX:XX +C>* fc00:0:0:4::/64 is directly connected, r1-eth4, XX:XX:XX +C>* fc00:0:0:5::/64 is directly connected, r1-eth5, XX:XX:XX +C>* fc00:0:0:6::/64 is directly connected, r1-eth6, XX:XX:XX +C>* fc00:0:0:7::/64 is directly connected, r1-eth7, XX:XX:XX +C>* fc00:0:0:8::/64 is directly connected, r1-eth8, XX:XX:XX +C>* fc00:0:0:9::/64 is directly connected, r1-eth9, XX:XX:XX +C>* fc00::/64 is directly connected, r1-eth0, XX:XX:XX +C>* fe80::/64 is directly connected, lo, XX:XX:XX +C * fe80::/64 is directly connected, r1-eth0, XX:XX:XX +C * fe80::/64 is directly connected, r1-eth1, XX:XX:XX +C * fe80::/64 is directly connected, r1-eth2, XX:XX:XX +C * fe80::/64 is directly connected, r1-eth3, XX:XX:XX +C * fe80::/64 is directly connected, r1-eth4, XX:XX:XX +C * fe80::/64 is directly connected, r1-eth5, XX:XX:XX +C * fe80::/64 is directly connected, r1-eth6, XX:XX:XX +C * fe80::/64 is directly connected, r1-eth7, XX:XX:XX +C * fe80::/64 is directly connected, r1-eth8, XX:XX:XX +C * fe80::/64 is directly connected, r1-eth9, XX:XX:XX +O fc00:0:0:4::/64 [110/10] is directly connected, r1-eth4, weight 1, XX:XX:XX +S>* 4:5::6:10/128 [1/0] via fc00::2, r1-eth0, weight 1, XX:XX:XX +S>* 4:5::6:11/128 [1/0] via fc00::2, r1-eth0, weight 1, XX:XX:XX +S>* 4:5::6:12/128 [1/0] is directly connected, r1-eth0, weight 1, XX:XX:XX +S 4:5::6:15/128 [255/0] via fc00::2, r1-eth0, weight 1, XX:XX:XX +S>* 4:5::6:7/128 [1/0] unreachable (blackhole), weight 1, XX:XX:XX +S>* 4:5::6:8/128 [1/0] unreachable (blackhole), weight 1, XX:XX:XX +S>* 4:5::6:9/128 [1/0] unreachable (ICMP unreachable), weight 1, XX:XX:XX diff --git a/tests/topotests/all_protocol_startup/r1/isisd.conf b/tests/topotests/all_protocol_startup/r1/isisd.conf new file mode 100644 index 0000000..8ceded8 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/isisd.conf @@ -0,0 +1,21 @@ +log file isisd.log +! +! debug isis events +! +! +interface r1-eth5 + ip router isis test + isis circuit-type level-1 +! +interface r1-eth6 + ipv6 router isis test + isis circuit-type level-2-only +! +! +router isis test + net 00.0001.00b0.64bc.43a0.00 + metric-style wide + log-adjacency-changes +! +line vty +! diff --git a/tests/topotests/all_protocol_startup/r1/ldpd.conf b/tests/topotests/all_protocol_startup/r1/ldpd.conf new file mode 100644 index 0000000..2358fb8 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/ldpd.conf @@ -0,0 +1,25 @@ +log file ldpd.log +! +! debug mpls ldp event +! debug mpls ldp zebra +! +! +mpls ldp + router-id 192.168.0.1 + ! + address-family ipv4 + discovery transport-address 192.168.9.1 + ! + interface r1-eth9 + ! + ! + address-family ipv6 + discovery transport-address fc00:0:0:9::1 + ! + interface r1-eth9 + ! + ! +! +! +line vty +! diff --git a/tests/topotests/all_protocol_startup/r1/nhrpd.conf b/tests/topotests/all_protocol_startup/r1/nhrpd.conf new file mode 100644 index 0000000..74e0f12 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/nhrpd.conf @@ -0,0 +1 @@ +! \ No newline at end of file diff --git a/tests/topotests/all_protocol_startup/r1/ospf6d.conf b/tests/topotests/all_protocol_startup/r1/ospf6d.conf new file mode 100644 index 0000000..33c2650 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/ospf6d.conf @@ -0,0 +1,20 @@ +log file ospf6d.log +! +! debug ospf6 lsa unknown +! debug ospf6 zebra +! debug ospf6 interface +! debug ospf6 neighbor +! +interface r1-eth4 + ipv6 ospf6 hello-interval 1 +! +router ospf6 + ospf6 router-id 192.168.0.1 + log-adjacency-changes + interface r1-eth4 area 0.0.0.0 +! +line vty +! +route-map LIES deny 10 + match interface notpresent +! diff --git a/tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4 b/tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4 new file mode 100644 index 0000000..9ce2f2e --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4 @@ -0,0 +1,16 @@ +log file ospf6d.log +! +!debug ospf6 lsa unknown +!debug ospf6 zebra +!debug ospf6 interface +!debug ospf6 neighbor +! +interface r1-eth4 +! +router ospf6 + router-id 192.168.0.1 + log-adjacency-changes + interface r1-eth4 area 0.0.0.0 +! +line vty +! diff --git a/tests/topotests/all_protocol_startup/r1/ospfd.conf b/tests/topotests/all_protocol_startup/r1/ospfd.conf new file mode 100644 index 0000000..61af44a --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/ospfd.conf @@ -0,0 +1,25 @@ +log file ospfd.log +! +! debug ospf event +! debug ospf zebra +! +! +interface r1-eth0 + ip ospf hello-interval 1 + ip ospf dead-interval 5 +! +interface r1-eth3 + ip ospf hello-interval 1 + ip ospf dead-interval 5 +! +router ospf + ospf router-id 192.168.0.1 + log-adjacency-changes + network 192.168.0.0/24 area 0.0.0.0 + network 192.168.3.0/24 area 0.0.0.0 +! +line vty +! +route-map LIES deny 10 + match interface notpresent +! diff --git a/tests/topotests/all_protocol_startup/r1/pbrd.conf b/tests/topotests/all_protocol_startup/r1/pbrd.conf new file mode 100644 index 0000000..360fb13 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/pbrd.conf @@ -0,0 +1,10 @@ +log file pbrd.log + +nexthop-group A + nexthop 192.168.161.4 +! +pbr-map FOO seq 10 + match dst-ip 4.5.6.7/32 + match src-ip 6.7.8.8/32 + set nexthop-group A +! \ No newline at end of file diff --git a/tests/topotests/all_protocol_startup/r1/rip_status.ref b/tests/topotests/all_protocol_startup/r1/rip_status.ref new file mode 100644 index 0000000..4a5255f --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/rip_status.ref @@ -0,0 +1,15 @@ +Routing Protocol is "rip" + Sending updates every 30 seconds with +/-50%, next due in XX seconds + Timeout after 180 seconds, garbage collect after 120 seconds + Outgoing update filter list for all interface is not set + Incoming update filter list for all interface is not set + Default redistribution metric is 1 + Redistributing: + Default version control: send version 2, receive version 2 + Interface Send Recv Key-chain + r1-eth1 2 2 + Routing for Networks: + 192.168.1.0/26 + Routing Information Sources: + Gateway BadPackets BadRoutes Distance Last Update + Distance: (default is 120) diff --git a/tests/topotests/all_protocol_startup/r1/ripd.conf b/tests/topotests/all_protocol_startup/r1/ripd.conf new file mode 100644 index 0000000..0a06794 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/ripd.conf @@ -0,0 +1,15 @@ +log file ripd.log +! +! debug rip events +! debug rip zebra +! +router rip + version 2 + network 192.168.1.0/26 +! +line vty +! + +route-map LIES deny 10 + match interface notpresent +! diff --git a/tests/topotests/all_protocol_startup/r1/ripng_status.ref b/tests/topotests/all_protocol_startup/r1/ripng_status.ref new file mode 100644 index 0000000..5d67c14 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/ripng_status.ref @@ -0,0 +1,14 @@ +Routing Protocol is "RIPng" + Sending updates every 30 seconds with +/-50%, next due in XX seconds + Timeout after 180 seconds, garbage collect after 120 seconds + Outgoing update filter list for all interface is not set + Incoming update filter list for all interface is not set + Default redistribution metric is 1 + Redistributing: + Default version control: send version 1, receive version 1 + Interface Send Recv + r1-eth2 1 1 + Routing for Networks: + fc00:0:0:2::/64 + Routing Information Sources: + Gateway BadPackets BadRoutes Distance Last Update diff --git a/tests/topotests/all_protocol_startup/r1/ripngd.conf b/tests/topotests/all_protocol_startup/r1/ripngd.conf new file mode 100644 index 0000000..d9d900f --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/ripngd.conf @@ -0,0 +1,14 @@ +log file ripngd.log +! +! debug ripng events +! debug ripng zebra +! +router ripng + network fc00:0:0:2::/64 +! +line vty +! + +route-map LIES deny 10 + match interface notpresent +! diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post4.1.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post4.1.ref new file mode 100644 index 0000000..b2e8de5 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post4.1.ref @@ -0,0 +1,9 @@ +BGP table version is 1, local router ID is 192.168.0.1, vrf id 0 +Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, + i internal, r RIB-failure, S Stale, R Removed +Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self +Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found + + Network Next Hop Metric LocPrf Weight Path + *> 192.168.0.0 0.0.0.0 0 32768 i diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post5.0.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post5.0.ref new file mode 100644 index 0000000..7bee704 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post5.0.ref @@ -0,0 +1,9 @@ +BGP table version is 1, local router ID is 192.168.0.1, vrf id 0 +Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, + i internal, r RIB-failure, S Stale, R Removed +Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self +Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found + + Network Next Hop Metric LocPrf Weight Path + *> 192.168.0.0/24 0.0.0.0 0 32768 i diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post6.1.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post6.1.ref new file mode 100644 index 0000000..31071e7 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post6.1.ref @@ -0,0 +1,10 @@ +BGP table version is 1, local router ID is 192.168.0.1, vrf id 0 +Default local pref 100, local AS 100 +Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, + i internal, r RIB-failure, S Stale, R Removed +Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self +Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found + + Network Next Hop Metric LocPrf Weight Path + *> 192.168.0.0/24 0.0.0.0 0 32768 i diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4.ref new file mode 100644 index 0000000..53c4793 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4.ref @@ -0,0 +1,7 @@ +BGP table version is 1, local router ID is 192.168.0.1 +Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, + i internal, r RIB-failure, S Stale, R Removed +Origin codes: i - IGP, e - EGP, ? - incomplete + + Network Next Hop Metric LocPrf Weight Path + *> 192.168.0.0 0.0.0.0 0 32768 i diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6-post4.1.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6-post4.1.ref new file mode 100644 index 0000000..fe3f072 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6-post4.1.ref @@ -0,0 +1,9 @@ +BGP table version is 1, local router ID is 192.168.0.1, vrf id 0 +Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, + i internal, r RIB-failure, S Stale, R Removed +Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self +Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found + + Network Next Hop Metric LocPrf Weight Path + *> fc00::/64 :: 0 32768 i diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6.ref new file mode 100644 index 0000000..363b4f5 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6.ref @@ -0,0 +1,7 @@ +BGP table version is 1, local router ID is 192.168.0.1 +Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, + i internal, r RIB-failure, S Stale, R Removed +Origin codes: i - IGP, e - EGP, ? - incomplete + + Network Next Hop Metric LocPrf Weight Path + *> fc00::/64 :: 0 32768 i diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_post6.1.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_post6.1.ref new file mode 100644 index 0000000..8c3229b --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_post6.1.ref @@ -0,0 +1,10 @@ +BGP table version is 1, local router ID is 192.168.0.1, vrf id 0 +Default local pref 100, local AS 100 +Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, + i internal, r RIB-failure, S Stale, R Removed +Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self +Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found + + Network Next Hop Metric LocPrf Weight Path + *> fc00::/64 :: 0 32768 i diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref new file mode 100644 index 0000000..0246687 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref @@ -0,0 +1,8 @@ +BGP router identifier 192.168.0.1, local AS number 100 vrf-id 0 +BGP table version 1 +RIB entries 1, using XXXX bytes of memory +Peers 2, using XXXX KiB of memory + +Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc +fc00:0:0:8::1000 4 100 0 0 0 0 0 never Active 0 Transit_cogent_v6 +fc00:0:0:8::2000 4 200 0 0 0 0 0 never Active 0 Client_Toto_default diff --git a/tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref b/tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref new file mode 100644 index 0000000..deeae87 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref @@ -0,0 +1,10 @@ +BGP router identifier 192.168.0.1, local AS number 100 vrf-id 0 +BGP table version 1 +RIB entries 1, using XXXX bytes of memory +Peers 4, using XXXX KiB of memory + +Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc +192.168.7.10 4 100 0 0 0 0 0 never Active 0 Transit_cogent +192.168.7.20 4 200 0 0 0 0 0 never Active 0 Client_Bibi_Full +fc00:0:0:8::1000 4 100 0 0 0 0 0 never Active 0 Transit_cogent_v6 +fc00:0:0:8::2000 4 200 0 0 0 0 0 never Active 0 Client_Toto_default diff --git a/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref b/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref new file mode 100644 index 0000000..7e28f04 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref @@ -0,0 +1,26 @@ +r1-eth0 is up + ifindex X, MTU 1500 bytes, BW XX Mbit + Internet Address 192.168.0.1/24, Broadcast 192.168.0.255, Area 0.0.0.0 + MTU mismatch detection: enabled + Router ID 192.168.0.1, Network Type BROADCAST, Cost: 10 + Transmit Delay is 1 sec, State DR, Priority 1 + Designated Router (ID) 192.168.0.1 Interface Address 192.168.0.1/24 + No backup designated router on this network + Multicast group memberships: OSPFAllRouters OSPFDesignatedRouters + Timer intervals configured, Hello 1s, Dead 5s, Wait 5s, Retransmit 5 + Hello due in XX.XXXs + Neighbor Count is 0, Adjacent neighbor count is 0 + Graceful Restart hello delay: 10s +r1-eth3 is up + ifindex X, MTU 1500 bytes, BW XX Mbit + Internet Address 192.168.3.1/26, Broadcast 192.168.3.63, Area 0.0.0.0 + MTU mismatch detection: enabled + Router ID 192.168.0.1, Network Type BROADCAST, Cost: 10 + Transmit Delay is 1 sec, State DR, Priority 1 + Designated Router (ID) 192.168.0.1 Interface Address 192.168.3.1/26 + No backup designated router on this network + Multicast group memberships: OSPFAllRouters OSPFDesignatedRouters + Timer intervals configured, Hello 1s, Dead 5s, Wait 5s, Retransmit 5 + Hello due in XX.XXXs + Neighbor Count is 0, Adjacent neighbor count is 0 + Graceful Restart hello delay: 10s diff --git a/tests/topotests/all_protocol_startup/r1/show_ipv6_ospf6_interface b/tests/topotests/all_protocol_startup/r1/show_ipv6_ospf6_interface new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/all_protocol_startup/r1/show_ipv6_ospf6_interface.ref b/tests/topotests/all_protocol_startup/r1/show_ipv6_ospf6_interface.ref new file mode 100644 index 0000000..6fbf40b --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/show_ipv6_ospf6_interface.ref @@ -0,0 +1,46 @@ +lo is up, type LOOPBACK + Interface ID: 1 + OSPF not enabled on this interface +r1-eth0 is up, type BROADCAST + Interface ID: 2 + OSPF not enabled on this interface +r1-eth1 is up, type BROADCAST + Interface ID: 3 + OSPF not enabled on this interface +r1-eth2 is up, type BROADCAST + Interface ID: 4 + OSPF not enabled on this interface +r1-eth3 is up, type BROADCAST + Interface ID: 5 + OSPF not enabled on this interface +r1-eth4 is up, type BROADCAST + Interface ID: 6 + Internet Address: + inet : 192.168.4.1/26 + inet6: fc00:0:0:4::1/64 + inet6: fe80::XXXX:XXXX:XXXX:XXXX/64 + Instance ID 0, Interface MTU 1500 (autodetect: 1500) + MTU mismatch detection: enabled + Area ID 0.0.0.0, Cost 10 + State DR, Transmit Delay 1 sec, Priority 1 + Timer intervals configured: + Hello 10, Dead 40, Retransmit 5 + DR: 192.168.0.1 BDR: 0.0.0.0 + Number of I/F scoped LSAs is 1 + 0 Pending LSAs for LSUpdate in Time 00:00:00 [thread off] + 0 Pending LSAs for LSAck in Time 00:00:00 [thread off] +r1-eth5 is up, type BROADCAST + Interface ID: 7 + OSPF not enabled on this interface +r1-eth6 is up, type BROADCAST + Interface ID: 8 + OSPF not enabled on this interface +r1-eth7 is up, type BROADCAST + Interface ID: 9 + OSPF not enabled on this interface +r1-eth8 is up, type BROADCAST + Interface ID: 10 + OSPF not enabled on this interface +r1-eth9 is up, type BROADCAST + Interface ID: 11 + OSPF not enabled on this interface diff --git a/tests/topotests/all_protocol_startup/r1/show_isis_interface_detail.ref b/tests/topotests/all_protocol_startup/r1/show_isis_interface_detail.ref new file mode 100644 index 0000000..8659700 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/show_isis_interface_detail.ref @@ -0,0 +1,28 @@ +Area test: + Interface: r1-eth5, State: Up, Active, Circuit Id: 0xXX + Type: lan, Level: L1, SNPA: XXXX.XXXX.XXXX + Level-1 Information: + Metric: 10, Active neighbors: 0 + Hello interval: 3, Holddown count: 10, Padding: yes + CNSP interval: 10, PSNP interval: 2 + LAN Priority: 64, is not DIS + IP Prefix(es): + 192.168.5.1/26 + IPv6 Link-Locals: + fe80::XXXX:XXXX:XXXX:XXXX/64 + IPv6 Prefixes: + fc00:0:0:5::1/64 + + Interface: r1-eth6, State: Up, Active, Circuit Id: 0xXX + Type: lan, Level: L2, SNPA: XXXX.XXXX.XXXX + Level-2 Information: + Metric: 10, Active neighbors: 0 + Hello interval: 3, Holddown count: 10, Padding: yes + CNSP interval: 10, PSNP interval: 2 + LAN Priority: 64, is not DIS + IP Prefix(es): + 192.168.6.1/26 + IPv6 Link-Locals: + fe80::XXXX:XXXX:XXXX:XXXX/64 + IPv6 Prefixes: + fc00:0:0:6::1/64 diff --git a/tests/topotests/all_protocol_startup/r1/show_mpls_ldp_interface.ref b/tests/topotests/all_protocol_startup/r1/show_mpls_ldp_interface.ref new file mode 100644 index 0000000..c6bb01c --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/show_mpls_ldp_interface.ref @@ -0,0 +1,3 @@ +AF Interface State Uptime Hello Timers ac +ipv4 r1-eth9 ACTIVE xx:xx:xx 5/15 0 +ipv6 r1-eth9 ACTIVE xx:xx:xx 5/15 0 diff --git a/tests/topotests/all_protocol_startup/r1/show_route_map.ref b/tests/topotests/all_protocol_startup/r1/show_route_map.ref new file mode 100644 index 0000000..612d0a7 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/show_route_map.ref @@ -0,0 +1,72 @@ +ZEBRA: +route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 + Match clauses: + interface notpresent + Set clauses: + Call clause: + Action: + Exit routemap +RIP: +route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 + Match clauses: + interface notpresent + Set clauses: + Call clause: + Action: + Exit routemap +RIPNG: +route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 + Match clauses: + interface notpresent + Set clauses: + Call clause: + Action: + Exit routemap +OSPF: +route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 + Match clauses: + interface notpresent + Set clauses: + Call clause: + Action: + Exit routemap +OSPF6: +route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 + Match clauses: + interface notpresent + Set clauses: + Call clause: + Action: + Exit routemap +BGP: +route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 + Match clauses: + interface notpresent + Set clauses: + Call clause: + Action: + Exit routemap +route-map: bgp-map Invoked: 0 Optimization: enabled Processed Change: false + permit, sequence 10 Invoked 0 + Match clauses: + Set clauses: + community 100:100 additive + local-preference 100 + Call clause: + Action: + Exit routemap + permit, sequence 20 Invoked 0 + Match clauses: + Set clauses: + metric 10 + local-preference 200 + Call clause: + Action: + Exit routemap +ISIS: diff --git a/tests/topotests/all_protocol_startup/r1/zebra.conf b/tests/topotests/all_protocol_startup/r1/zebra.conf new file mode 100644 index 0000000..c5ef796 --- /dev/null +++ b/tests/topotests/all_protocol_startup/r1/zebra.conf @@ -0,0 +1,120 @@ +log file zebra.log +! +hostname r1 +! +# Create the various blackhole route types +ip route 4.5.6.7/32 blackhole +ipv6 route 4:5::6:7/128 blackhole +ip route 4.5.6.8/32 Null0 +ipv6 route 4:5::6:8/128 Null0 +ip route 4.5.6.9/32 reject +ipv6 route 4:5::6:9/128 reject +# Test various spellings of NULL0 to make sure we accept them +ip route 4.5.6.13/32 null0 +ip route 4.5.6.14/32 NULL0 +# Create normal gateway routes +ip route 4.5.6.10/32 192.168.0.2 +ipv6 route 4:5::6:10/128 fc00:0:0:0::2 +# Create normal gateway + interface routes +ip route 4.5.6.11/32 192.168.0.2 r1-eth0 +ipv6 route 4:5::6:11/128 fc00:0:0:0::2 r1-eth0 +# Create ifname routes +ip route 4.5.6.12/32 r1-eth0 +ipv6 route 4:5::6:12/128 r1-eth0 +# Create a route that has a large admin distance +# an admin distance of 255 should be accepted +# by zebra but not installed. +ip route 4.5.6.15/32 192.168.0.2 255 +ipv6 route 4:5::6:15/128 fc00:0:0:0::2 255 +# Routes to put into a nexthop-group +ip route 1.1.1.1/32 r1-eth1 +ip route 1.1.1.2/32 r1-eth2 +ip route 1.1.1.3/32 r1-eth3 +ip route 1.1.1.4/32 r1-eth4 +ip route 1.1.1.5/32 r1-eth5 +ip route 1.1.1.6/32 r1-eth6 +ip route 1.1.1.7/32 r1-eth7 +ip route 1.1.1.8/32 r1-eth8 + +# Create a route that has overlapping distance +# so we have backups +ip route 4.5.6.16/32 192.168.0.2 5 +ip route 4.5.6.16/32 192.168.0.4 10 + +# Create routes that have different tags +# and how we handle it +ip route 4.5.6.17/32 192.168.0.2 tag 9000 +ip route 4.5.6.17/32 192.168.0.2 tag 10000 + +! +interface r1-eth0 + description to sw0 - no routing protocol + ip address 192.168.0.1/24 + ipv6 address fc00:0:0:0::1/64 +! +interface r1-eth1 + description to sw1 - RIP interface + ip address 192.168.1.1/26 + ipv6 address fc00:0:0:1::1/64 + no link-detect +! +interface r1-eth2 + description to sw2 - RIPng interface + ip address 192.168.2.1/26 + ipv6 address fc00:0:0:2::1/64 + no link-detect +! +interface r1-eth3 + description to sw3 - OSPFv2 interface + ip address 192.168.3.1/26 + ipv6 address fc00:0:0:3::1/64 + no link-detect +! +interface r1-eth4 + description to sw4 - OSPFv3 interface + ip address 192.168.4.1/26 + ipv6 address fc00:0:0:4::1/64 + no link-detect +! +interface r1-eth5 + description to sw5 - ISIS IPv4 interface + ip address 192.168.5.1/26 + ipv6 address fc00:0:0:5::1/64 + no link-detect +! +interface r1-eth6 + description to sw6 - ISIS IPv6 interface + ip address 192.168.6.1/26 + ipv6 address fc00:0:0:6::1/64 + no link-detect +! +interface r1-eth7 + description to sw7 - BGP IPv4 interface + ip address 192.168.7.1/26 + ipv6 address fc00:0:0:7::1/64 + no link-detect +! +interface r1-eth8 + description to sw8 - BGP IPv6 interface + ip address 192.168.8.1/26 + ipv6 address fc00:0:0:8::1/64 + no link-detect +! +interface r1-eth9 + description to sw9 - LDP interface + ip address 192.168.9.1/26 + ipv6 address fc00:0:0:9::1/64 + + no link-detect +! +! +ip forwarding +ipv6 forwarding +! +! +line vty +! + +route-map LIES deny 10 + match interface notpresent +! diff --git a/tests/topotests/all_protocol_startup/test_all_protocol_startup.dot b/tests/topotests/all_protocol_startup/test_all_protocol_startup.dot new file mode 100644 index 0000000..f39f8f8 --- /dev/null +++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.dot @@ -0,0 +1,61 @@ +## GraphViz file for test_all_protocol_startup +## +## Color coding: +######################### +## Main FRR: #f08080 red +## No protocol: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #33ff99 light green +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +## LDP IPv4 #fedbe2 light pink +##### Colors (see http://www.color-hex.com/) + +graph test_all_protocol_startup { + + // title + labelloc="t"; + label="Test Topologoy All Protocols Startup"; + + ###################### + # Routers + ###################### + + # Main FRR Router with all protocols + R1 [shape=doubleoctagon, label="R1 FRR\nMain Router", fillcolor="#f08080", style=filled]; + + ###################### + # Network Lists + ###################### + + SW0_STUB [label="SW0 (no protocol)\n192.168.1.0/24\nfc00:0:0:0::/64", fillcolor="#d0e0d0", style=filled]; + + SW1_RIP [label="SW1 RIP\n192.168.1.0/24\nfc00:0:0:1::/64", fillcolor="#19e3d9", style=filled]; + SW2_RIPNG [label="SW2 RIPng\n192.168.2.0/24\nfc00:0:0:2::/64", fillcolor="#fcb314", style=filled]; + SW3_OSPF [label="SW3 OSPFv2\n192.168.3.0/24\nfc00:0:0:3::/64", fillcolor="#32b835", style=filled]; + SW4_OSPFV3 [label="SW4 OSPFv3\n192.168.4.0/24\nfc00:0:0:4::/64", fillcolor="#19e3d9", style=filled]; + SW5_ISIS_V4 [label="SW5 ISIS IPv4\n192.168.5.0/24\nfc00:0:0:5::/64", fillcolor="#33ff99", style=filled]; + SW6_ISIS_V6 [label="SW6 ISIS IPv6\n192.168.6.0/24\nfc00:0:0:6::/64", fillcolor="#9a81ec", style=filled]; + SW7_BGP_V4 [label="SW7 BGP IPv4\n192.168.7.0/24\nfc00:0:0:7::/64", fillcolor="#eee3d3", style=filled]; + SW8_BGP_V6 [label="SW8 BGP IPv6\n192.168.8.0/24\nfc00:0:0:8::/64", fillcolor="#fdff00", style=filled]; + SW9_LDP [label="SW9 LDP\n192.168.9.0/24\nfc00:0:0:9::/64", fillcolor="#fedbe2", style=filled]; + + ###################### + # Network Connections + ###################### + R1 -- SW0_STUB [label = "eth0\n.1\n::1"]; + R1 -- SW1_RIP [label = "eth1\n.1\n::1"]; + R1 -- SW2_RIPNG [label = "eth2\n.1\n::1"]; + R1 -- SW3_OSPF [label = "eth3\n.1\n::1"]; + R1 -- SW4_OSPFV3 [label = "eth4\n.1\n::1"]; + R1 -- SW5_ISIS_V4 [label = "eth5\n.1\n::1"]; + R1 -- SW6_ISIS_V6 [label = "eth6\n.1\n::1"]; + R1 -- SW7_BGP_V4 [label = "eth7\n.1\n::1"]; + R1 -- SW8_BGP_V6 [label = "eth8\n.1\n::1"]; + R1 -- SW9_LDP [label = "eth9\n.1\n::1"]; + +} diff --git a/tests/topotests/all_protocol_startup/test_all_protocol_startup.pdf b/tests/topotests/all_protocol_startup/test_all_protocol_startup.pdf new file mode 100644 index 0000000..23f69bc Binary files /dev/null and b/tests/topotests/all_protocol_startup/test_all_protocol_startup.pdf differ diff --git a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py new file mode 100644 index 0000000..c319477 --- /dev/null +++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py @@ -0,0 +1,1724 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_all_protocol_startup.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2017 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_all_protocol_startup.py: Test of all protocols at same time + +""" + +import os +import re +import sys +import pytest +import glob +from time import sleep + +pytestmark = [ + pytest.mark.babeld, + pytest.mark.bgpd, + pytest.mark.isisd, + pytest.mark.nhrpd, + pytest.mark.ospfd, + pytest.mark.pbrd, + pytest.mark.ripd, +] + +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from lib import topotest +from lib.topogen import Topogen, get_topogen +from lib.common_config import ( + required_linux_kernel_version, +) + +from lib.topolog import logger +import json + +fatal_error = "" + + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +def build_topo(tgen): + router = tgen.add_router("r1") + for i in range(0, 10): + tgen.add_switch("sw%d" % i).add_link(router) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + global fatal_error + + print("\n\n** %s: Setup Topology" % module.__name__) + print("******************************************\n") + + thisDir = os.path.dirname(os.path.realpath(__file__)) + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + net = tgen.net + + if net["r1"].get_routertype() != "frr": + fatal_error = "Test is only implemented for FRR" + sys.stderr.write("\n\nTest is only implemented for FRR - Skipping\n\n") + pytest.skip(fatal_error) + + # Starting Routers + # + # Main router + for i in range(1, 2): + net["r%s" % i].loadConf("mgmtd", "%s/r%s/zebra.conf" % (thisDir, i)) + net["r%s" % i].loadConf("zebra", "%s/r%s/zebra.conf" % (thisDir, i)) + net["r%s" % i].loadConf("ripd", "%s/r%s/ripd.conf" % (thisDir, i)) + net["r%s" % i].loadConf("ripngd", "%s/r%s/ripngd.conf" % (thisDir, i)) + net["r%s" % i].loadConf("ospfd", "%s/r%s/ospfd.conf" % (thisDir, i)) + if net["r1"].checkRouterVersion("<", "4.0"): + net["r%s" % i].loadConf( + "ospf6d", "%s/r%s/ospf6d.conf-pre-v4" % (thisDir, i) + ) + else: + net["r%s" % i].loadConf("ospf6d", "%s/r%s/ospf6d.conf" % (thisDir, i)) + net["r%s" % i].loadConf("isisd", "%s/r%s/isisd.conf" % (thisDir, i)) + net["r%s" % i].loadConf("bgpd", "%s/r%s/bgpd.conf" % (thisDir, i)) + if net["r%s" % i].daemon_available("ldpd"): + # Only test LDPd if it's installed and Kernel >= 4.5 + net["r%s" % i].loadConf("ldpd", "%s/r%s/ldpd.conf" % (thisDir, i)) + net["r%s" % i].loadConf("sharpd") + net["r%s" % i].loadConf("nhrpd", "%s/r%s/nhrpd.conf" % (thisDir, i)) + net["r%s" % i].loadConf("babeld", "%s/r%s/babeld.conf" % (thisDir, i)) + net["r%s" % i].loadConf("pbrd", "%s/r%s/pbrd.conf" % (thisDir, i)) + tgen.gears["r%s" % i].start() + + # For debugging after starting FRR daemons, uncomment the next line + # tgen.mininet_cli() + + +def teardown_module(module): + print("\n\n** %s: Shutdown Topology" % module.__name__) + print("******************************************\n") + tgen = get_topogen() + tgen.stop_topology() + + +def test_router_running(): + global fatal_error + tgen = get_topogen() + net = tgen.net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + print("\n\n** Check if FRR is running on each Router node") + print("******************************************\n") + sleep(5) + + # Starting Routers + for i in range(1, 2): + fatal_error = net["r%s" % i].checkRouterRunning() + assert fatal_error == "", fatal_error + + # For debugging after starting FRR daemons, uncomment the next line + # tgen.mininet_cli() + + +def test_error_messages_vtysh(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + print("\n\n** Check for error messages on VTYSH") + print("******************************************\n") + + failures = 0 + for i in range(1, 2): + # + # First checking Standard Output + # + + # VTYSH output from router + vtystdout = net["r%s" % i].cmd('vtysh -c "show version" 2> /dev/null').rstrip() + + # Fix newlines (make them all the same) + vtystdout = ("\n".join(vtystdout.splitlines()) + "\n").rstrip() + # Drop everything starting with "FRRouting X.xx" message + vtystdout = re.sub(r"FRRouting [0-9]+.*", "", vtystdout, flags=re.DOTALL) + + if vtystdout == "": + print("r%s StdOut ok" % i) + + assert vtystdout == "", "Vtysh StdOut Output check failed for router r%s" % i + + # + # Second checking Standard Error + # + + # VTYSH StdErr output from router + vtystderr = net["r%s" % i].cmd('vtysh -c "show version" > /dev/null').rstrip() + + # Fix newlines (make them all the same) + vtystderr = ("\n".join(vtystderr.splitlines()) + "\n").rstrip() + # # Drop everything starting with "FRRouting X.xx" message + # vtystderr = re.sub(r"FRRouting [0-9]+.*", "", vtystderr, flags=re.DOTALL) + + if vtystderr == "": + print("r%s StdErr ok" % i) + + assert vtystderr == "", "Vtysh StdErr Output check failed for router r%s" % i + + # Make sure that all daemons are running + for i in range(1, 2): + fatal_error = net["r%s" % i].checkRouterRunning() + assert fatal_error == "", fatal_error + + +def test_error_messages_daemons(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + print( + "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n" + ) + pytest.skip("Skipping test for Stderr output") + + print("\n\n** Check for error messages in daemons") + print("******************************************\n") + + error_logs = "" + + for i in range(1, 2): + log = net["r%s" % i].getStdErr("ripd") + if log: + error_logs += "r%s RIPd StdErr Output:\n" % i + error_logs += log + log = net["r%s" % i].getStdErr("ripngd") + if log: + error_logs += "r%s RIPngd StdErr Output:\n" % i + error_logs += log + log = net["r%s" % i].getStdErr("ospfd") + if log: + error_logs += "r%s OSPFd StdErr Output:\n" % i + error_logs += log + log = net["r%s" % i].getStdErr("ospf6d") + if log: + error_logs += "r%s OSPF6d StdErr Output:\n" % i + error_logs += log + log = net["r%s" % i].getStdErr("isisd") + # ISIS shows debugging enabled status on StdErr + # Remove these messages + log = re.sub(r"^IS-IS .* debugging is on.*", "", log).rstrip() + if log: + error_logs += "r%s ISISd StdErr Output:\n" % i + error_logs += log + log = net["r%s" % i].getStdErr("bgpd") + if log: + error_logs += "r%s BGPd StdErr Output:\n" % i + error_logs += log + if net["r%s" % i].daemon_available("ldpd"): + log = net["r%s" % i].getStdErr("ldpd") + if log: + error_logs += "r%s LDPd StdErr Output:\n" % i + error_logs += log + + log = net["r1"].getStdErr("nhrpd") + # NHRPD shows YANG model not embedded messages + # Ignore these + log = re.sub(r".*YANG model.*not embedded.*", "", log).rstrip() + if log: + error_logs += "r%s NHRPd StdErr Output:\n" % i + error_logs += log + + log = net["r1"].getStdErr("babeld") + if log: + error_logs += "r%s BABELd StdErr Output:\n" % i + error_logs += log + + log = net["r1"].getStdErr("pbrd") + if log: + error_logs += "r%s PBRd StdErr Output:\n" % i + error_logs += log + + log = net["r%s" % i].getStdErr("zebra") + if log: + error_logs += "r%s Zebra StdErr Output:\n" % i + error_logs += log + + if error_logs: + sys.stderr.write( + "Failed check for StdErr Output on daemons:\n%s\n" % error_logs + ) + + # Ignoring the issue if told to ignore (ie not yet fixed) + if error_logs != "": + if os.environ.get("bamboo_TOPOTESTS_ISSUE_349") == "IGNORE": + sys.stderr.write( + "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/349\n" + ) + pytest.skip( + "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/349" + ) + + assert error_logs == "", "Daemons report errors to StdErr" + + +def test_converge_protocols(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + # We need loopback to have a link local so it always is the + # "selected" router for fe80::/64 when we static compare below. + print("Adding link-local to loopback for stable results") + cmd = ( + "mac=`cat /sys/class/net/lo/address`; echo lo: $mac;" + " [ -z \"$mac\" ] && continue; IFS=':'; set $mac; unset IFS;" + " ip address add dev lo scope link" + " fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64" + ) + net["r1"].cmd_raises(cmd) + + print("\n\n** Waiting for protocols convergence") + print("******************************************\n") + + # Not really implemented yet - just sleep 60 secs for now + sleep(5) + + # Make sure that all daemons are running + failures = 0 + for i in range(1, 2): + fatal_error = net["r%s" % i].checkRouterRunning() + assert fatal_error == "", fatal_error + + print("Show that v4 routes are right\n") + v4_routesFile = "%s/r%s/ipv4_routes.ref" % (thisDir, i) + expected = ( + net["r%s" % i].cmd("sort {} 2> /dev/null".format(v4_routesFile)).rstrip() + ) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + + actual = ( + net["r%s" % i] + .cmd( + "vtysh -c \"show ip route\" | sed -e '/^Codes: /,/^\\s*$/d' | sort 2> /dev/null" + ) + .rstrip() + ) + # Drop time in last update + actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + diff = topotest.get_textdiff( + actual, + expected, + title1="Actual IP Routing Table", + title2="Expected IP RoutingTable", + ) + if diff: + sys.stderr.write("r%s failed IP Routing table check:\n%s\n" % (i, diff)) + failures += 1 + else: + print("r%s ok" % i) + + assert failures == 0, "IP Routing table failed for r%s\n%s" % (i, diff) + + failures = 0 + + print("Show that v6 routes are right\n") + v6_routesFile = "%s/r%s/ipv6_routes.ref" % (thisDir, i) + expected = ( + net["r%s" % i].cmd("sort {} 2> /dev/null".format(v6_routesFile)).rstrip() + ) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + + actual = ( + net["r%s" % i] + .cmd( + "vtysh -c \"show ipv6 route\" | sed -e '/^Codes: /,/^\\s*$/d' | sort 2> /dev/null" + ) + .rstrip() + ) + # Drop time in last update + actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + diff = topotest.get_textdiff( + actual, + expected, + title1="Actual IPv6 Routing Table", + title2="Expected IPv6 RoutingTable", + ) + if diff: + sys.stderr.write("r%s failed IPv6 Routing table check:\n%s\n" % (i, diff)) + failures += 1 + else: + print("r%s ok" % i) + + assert failures == 0, "IPv6 Routing table failed for r%s\n%s" % (i, diff) + + +def route_get_nhg_id(route_str): + net = get_topogen().net + output = net["r1"].cmd('vtysh -c "show ip route %s nexthop-group"' % route_str) + match = re.search(r"Nexthop Group ID: (\d+)", output) + assert match is not None, ( + "Nexthop Group ID not found for sharpd route %s" % route_str + ) + + nhg_id = int(match.group(1)) + return nhg_id + + +def verify_nexthop_group(nhg_id, recursive=False, ecmp=0): + net = get_topogen().net + count = 0 + valid = None + ecmpcount = None + depends = None + resolved_id = None + installed = None + found = False + + while not found and count < 10: + count += 1 + # Verify NHG is valid/installed + output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id) + valid = re.search(r"Valid", output) + if valid is None: + found = False + sleep(1) + continue + + if ecmp or recursive: + ecmpcount = re.search(r"Depends:.*\n", output) + if ecmpcount is None: + found = False + sleep(1) + continue + + # list of IDs in group + depends = re.findall(r"\((\d+)\)", ecmpcount.group(0)) + + if ecmp: + if len(depends) != ecmp: + found = False + sleep(1) + continue + else: + # If recursive, we need to look at its resolved group + if len(depends) != 1: + found = False + sleep(1) + continue + + resolved_id = int(depends[0]) + verify_nexthop_group(resolved_id, False) + else: + installed = re.search(r"Installed", output) + if installed is None: + found = False + sleep(1) + continue + found = True + + assert valid is not None, "Nexthop Group ID=%d not marked Valid" % nhg_id + if ecmp or recursive: + assert ecmpcount is not None, "Nexthop Group ID=%d has no depends" % nhg_id + if ecmp: + assert len(depends) == ecmp, ( + "Nexthop Group ID=%d doesn't match ecmp size" % nhg_id + ) + else: + assert len(depends) == 1, ( + "Nexthop Group ID=%d should only have one recursive depend" % nhg_id + ) + else: + assert installed is not None, ( + "Nexthop Group ID=%d not marked Installed" % nhg_id + ) + + +def verify_route_nexthop_group(route_str, recursive=False, ecmp=0): + # Verify route and that zebra created NHGs for and they are valid/installed + nhg_id = route_get_nhg_id(route_str) + verify_nexthop_group(nhg_id, recursive, ecmp) + + +def test_nexthop_groups(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + print("\n\n** Verifying Nexthop Groups") + print("******************************************\n") + + ### Nexthop Group Tests + + ## Basic test + + # Create a lib nexthop-group + net["r1"].cmd( + 'vtysh -c "c t" -c "nexthop-group basic" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"' + ) + + # Create with sharpd using nexthop-group + net["r1"].cmd('vtysh -c "sharp install routes 2.2.2.1 nexthop-group basic 1"') + verify_route_nexthop_group("2.2.2.1/32") + + ## Connected + + net["r1"].cmd( + 'vtysh -c "c t" -c "nexthop-group connected" -c "nexthop r1-eth1" -c "nexthop r1-eth2"' + ) + + net["r1"].cmd('vtysh -c "sharp install routes 2.2.2.2 nexthop-group connected 1"') + verify_route_nexthop_group("2.2.2.2/32") + + ## Recursive + + net["r1"].cmd( + 'vtysh -c "c t" -c "nexthop-group basic-recursive" -c "nexthop 2.2.2.1"' + ) + + net["r1"].cmd( + 'vtysh -c "sharp install routes 3.3.3.1 nexthop-group basic-recursive 1"' + ) + + verify_route_nexthop_group("3.3.3.1/32", True) + + ## Duplicate + + net["r1"].cmd( + 'vtysh -c "c t" -c "nexthop-group duplicate" -c "nexthop 2.2.2.1" -c "nexthop 1.1.1.1"' + ) + + net["r1"].cmd('vtysh -c "sharp install routes 3.3.3.2 nexthop-group duplicate 1"') + + verify_route_nexthop_group("3.3.3.2/32") + + ## Two 4-Way ECMP + + net["r1"].cmd( + 'vtysh -c "c t" -c "nexthop-group fourA" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2" \ + -c "nexthop 1.1.1.3" -c "nexthop 1.1.1.4"' + ) + + net["r1"].cmd('vtysh -c "sharp install routes 4.4.4.1 nexthop-group fourA 1"') + + verify_route_nexthop_group("4.4.4.1/32") + + net["r1"].cmd( + 'vtysh -c "c t" -c "nexthop-group fourB" -c "nexthop 1.1.1.5" -c "nexthop 1.1.1.6" \ + -c "nexthop 1.1.1.7" -c "nexthop 1.1.1.8"' + ) + + net["r1"].cmd('vtysh -c "sharp install routes 4.4.4.2 nexthop-group fourB 1"') + + verify_route_nexthop_group("4.4.4.2/32") + + ## Recursive to 8-Way ECMP + + net["r1"].cmd( + 'vtysh -c "c t" -c "nexthop-group eight-recursive" -c "nexthop 4.4.4.1" -c "nexthop 4.4.4.2"' + ) + + net["r1"].cmd( + 'vtysh -c "sharp install routes 5.5.5.1 nexthop-group eight-recursive 1"' + ) + + verify_route_nexthop_group("5.5.5.1/32") + + ## 4-way ECMP Routes Pointing to Each Other + + # This is to check for a bug with NH resolution where + # routes would infintely resolve to each other blowing + # up the resolved-> nexthop pointer. + + net["r1"].cmd( + 'vtysh -c "c t" -c "nexthop-group infinite-recursive" -c "nexthop 6.6.6.1" -c "nexthop 6.6.6.2" \ + -c "nexthop 6.6.6.3" -c "nexthop 6.6.6.4"' + ) + + # static route nexthops can recurse to + + net["r1"].cmd('vtysh -c "c t" -c "ip route 6.6.6.0/24 1.1.1.1"') + + # Make routes that point to themselves in ecmp + + net["r1"].cmd( + 'vtysh -c "sharp install routes 6.6.6.4 nexthop-group infinite-recursive 1"' + ) + sleep(5) + + net["r1"].cmd( + 'vtysh -c "sharp install routes 6.6.6.3 nexthop-group infinite-recursive 1"' + ) + sleep(5) + + net["r1"].cmd( + 'vtysh -c "sharp install routes 6.6.6.2 nexthop-group infinite-recursive 1"' + ) + sleep(5) + + net["r1"].cmd( + 'vtysh -c "sharp install routes 6.6.6.1 nexthop-group infinite-recursive 1"' + ) + + # Get routes and test if has too many (duplicate) nexthops + count = 0 + dups = [] + nhg_id = route_get_nhg_id("6.6.6.1/32") + while (len(dups) != 3) and count < 10: + output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id) + + dups = re.findall(r"(via 1\.1\.1\.1)", output) + if len(dups) != 3: + count += 1 + sleep(1) + + # Should find 3, itself is inactive + assert len(dups) == 3, ( + "Route 6.6.6.1/32 with Nexthop Group ID=%d has wrong number of resolved nexthops" + % nhg_id + ) + + ## Remove all NHG routes + + net["r1"].cmd('vtysh -c "sharp remove routes 2.2.2.1 1"') + net["r1"].cmd('vtysh -c "sharp remove routes 2.2.2.2 1"') + net["r1"].cmd('vtysh -c "sharp remove routes 3.3.3.1 1"') + net["r1"].cmd('vtysh -c "sharp remove routes 3.3.3.2 1"') + net["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.1 1"') + net["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.2 1"') + net["r1"].cmd('vtysh -c "sharp remove routes 5.5.5.1 1"') + net["r1"].cmd('vtysh -c "sharp remove routes 6.6.6.1 4"') + net["r1"].cmd('vtysh -c "c t" -c "no ip route 6.6.6.0/24 1.1.1.1"') + + +def test_rip_status(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + print("\n\n** Verifying RIP status") + print("******************************************\n") + failures = 0 + for i in range(1, 2): + refTableFile = "%s/r%s/rip_status.ref" % (thisDir, i) + if os.path.isfile(refTableFile): + # Read expected result from file + expected = open(refTableFile).read().rstrip() + # Fix newlines (make them all the same) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + + # Actual output from router + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show ip rip status" 2> /dev/null') + .rstrip() + ) + # Drop time in next due + actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual) + # Drop time in last update + actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual) + # Fix newlines (make them all the same) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + + # Generate Diff + diff = topotest.get_textdiff( + actual, + expected, + title1="actual IP RIP status", + title2="expected IP RIP status", + ) + + # Empty string if it matches, otherwise diff contains unified diff + if diff: + sys.stderr.write("r%s failed IP RIP status check:\n%s\n" % (i, diff)) + failures += 1 + else: + print("r%s ok" % i) + + assert failures == 0, "IP RIP status failed for router r%s:\n%s" % (i, diff) + + # Make sure that all daemons are running + for i in range(1, 2): + fatal_error = net["r%s" % i].checkRouterRunning() + assert fatal_error == "", fatal_error + + +def test_ripng_status(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + print("\n\n** Verifying RIPng status") + print("******************************************\n") + failures = 0 + for i in range(1, 2): + refTableFile = "%s/r%s/ripng_status.ref" % (thisDir, i) + if os.path.isfile(refTableFile): + # Read expected result from file + expected = open(refTableFile).read().rstrip() + # Fix newlines (make them all the same) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + + # Actual output from router + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show ipv6 ripng status" 2> /dev/null') + .rstrip() + ) + # Mask out Link-Local mac address portion. They are random... + actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual) + # Drop time in next due + actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual) + # Drop time in last update + actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual) + # Fix newlines (make them all the same) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + + # Generate Diff + diff = topotest.get_textdiff( + actual, + expected, + title1="actual IPv6 RIPng status", + title2="expected IPv6 RIPng status", + ) + + # Empty string if it matches, otherwise diff contains unified diff + if diff: + sys.stderr.write( + "r%s failed IPv6 RIPng status check:\n%s\n" % (i, diff) + ) + failures += 1 + else: + print("r%s ok" % i) + + assert failures == 0, "IPv6 RIPng status failed for router r%s:\n%s" % ( + i, + diff, + ) + + # Make sure that all daemons are running + for i in range(1, 2): + fatal_error = net["r%s" % i].checkRouterRunning() + assert fatal_error == "", fatal_error + + +def test_ospfv2_interfaces(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + print("\n\n** Verifying OSPFv2 interfaces") + print("******************************************\n") + failures = 0 + for i in range(1, 2): + refTableFile = "%s/r%s/show_ip_ospf_interface.ref" % (thisDir, i) + if os.path.isfile(refTableFile): + # Read expected result from file + expected = open(refTableFile).read().rstrip() + # Fix newlines (make them all the same) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + + # Actual output from router + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show ip ospf interface" 2> /dev/null') + .rstrip() + ) + # Mask out Bandwidth portion. They may change.. + actual = re.sub(r"BW [0-9]+ Mbit", "BW XX Mbit", actual) + actual = re.sub(r"ifindex [0-9]+", "ifindex X", actual) + + # Drop time in next due + actual = re.sub(r"Hello due in [0-9\.]+s", "Hello due in XX.XXXs", actual) + actual = re.sub( + r"Hello due in [0-9\.]+ usecs", "Hello due in XX.XXXs", actual + ) + # Fix 'MTU mismatch detection: enabled' vs 'MTU mismatch detection:enabled' - accept both + actual = re.sub( + r"MTU mismatch detection:([a-z]+.*)", + r"MTU mismatch detection: \1", + actual, + ) + # Fix newlines (make them all the same) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + + # Generate Diff + diff = topotest.get_textdiff( + actual, + expected, + title1="actual SHOW IP OSPF INTERFACE", + title2="expected SHOW IP OSPF INTERFACE", + ) + + # Empty string if it matches, otherwise diff contains unified diff + if diff: + sys.stderr.write( + "r%s failed SHOW IP OSPF INTERFACE check:\n%s\n" % (i, diff) + ) + failures += 1 + else: + print("r%s ok" % i) + + # Ignoring the issue if told to ignore (ie not yet fixed) + if failures != 0: + if os.environ.get("bamboo_TOPOTESTS_ISSUE_348") == "IGNORE": + sys.stderr.write( + "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348\n" + ) + pytest.skip( + "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348" + ) + + assert ( + failures == 0 + ), "SHOW IP OSPF INTERFACE failed for router r%s:\n%s" % (i, diff) + + # Make sure that all daemons are running + for i in range(1, 2): + fatal_error = net["r%s" % i].checkRouterRunning() + assert fatal_error == "", fatal_error + + +def test_isis_interfaces(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + print("\n\n** Verifying ISIS interfaces") + print("******************************************\n") + failures = 0 + for i in range(1, 2): + refTableFile = "%s/r%s/show_isis_interface_detail.ref" % (thisDir, i) + if os.path.isfile(refTableFile): + # Read expected result from file + expected = open(refTableFile).read().rstrip() + # Fix newlines (make them all the same) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + + # Actual output from router + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show isis interface detail" 2> /dev/null') + .rstrip() + ) + # Mask out Link-Local mac address portion. They are random... + actual = re.sub(r"fe80::[0-9a-f:]+", "fe80::XXXX:XXXX:XXXX:XXXX", actual) + # Mask out SNPA mac address portion. They are random... + actual = re.sub(r"SNPA: [0-9a-f\.]+", "SNPA: XXXX.XXXX.XXXX", actual) + # Mask out Circuit ID number + actual = re.sub(r"Circuit Id: 0x[0-9a-f]+", "Circuit Id: 0xXX", actual) + # Fix newlines (make them all the same) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + + # Generate Diff + diff = topotest.get_textdiff( + actual, + expected, + title1="actual SHOW ISIS INTERFACE DETAIL", + title2="expected SHOW ISIS OSPF6 INTERFACE DETAIL", + ) + + # Empty string if it matches, otherwise diff contains unified diff + if diff: + sys.stderr.write( + "r%s failed SHOW ISIS INTERFACE DETAIL check:\n%s\n" % (i, diff) + ) + failures += 1 + else: + print("r%s ok" % i) + + assert ( + failures == 0 + ), "SHOW ISIS INTERFACE DETAIL failed for router r%s:\n%s" % (i, diff) + + # Make sure that all daemons are running + for i in range(1, 2): + fatal_error = net["r%s" % i].checkRouterRunning() + assert fatal_error == "", fatal_error + + +def test_bgp_summary(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + print("\n\n** Verifying BGP Summary") + print("******************************************\n") + failures = 0 + for i in range(1, 2): + refTableFile = "%s/r%s/show_ip_bgp_summary.ref" % (thisDir, i) + if os.path.isfile(refTableFile): + # Read expected result from file + expected_original = open(refTableFile).read().rstrip() + + for arguments in [ + "", + "remote-as internal", + "remote-as external", + "remote-as 100", + "remote-as 123", + "neighbor 192.168.7.10", + "neighbor 192.168.7.10", + "neighbor fc00:0:0:8::1000", + "neighbor 10.0.0.1", + "terse", + "remote-as internal terse", + "remote-as external terse", + "remote-as 100 terse", + "remote-as 123 terse", + "neighbor 192.168.7.10 terse", + "neighbor 192.168.7.10 terse", + "neighbor fc00:0:0:8::1000 terse", + "neighbor 10.0.0.1 terse", + ]: + # Actual output from router + actual = ( + net["r%s" % i] + .cmd( + 'vtysh -c "show ip bgp summary ' + arguments + '" 2> /dev/null' + ) + .rstrip() + ) + + # Mask out "using XXiXX bytes" portion. They are random... + actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual) + # Mask out "using XiXXX KiB" portion. They are random... + actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual) + + # Remove extra summaries which exist with newer versions + + # Remove summary lines (changed recently) + actual = re.sub(r"Total number.*", "", actual) + actual = re.sub(r"Displayed.*", "", actual) + # Remove IPv4 Unicast Summary (Title only) + actual = re.sub(r"IPv4 Unicast Summary \(VRF default\):", "", actual) + # Remove IPv4 Multicast Summary (all of it) + actual = re.sub(r"IPv4 Multicast Summary \(VRF default\):", "", actual) + actual = re.sub(r"No IPv4 Multicast neighbor is configured", "", actual) + # Remove IPv4 VPN Summary (all of it) + actual = re.sub(r"IPv4 VPN Summary \(VRF default\):", "", actual) + actual = re.sub(r"No IPv4 VPN neighbor is configured", "", actual) + # Remove IPv4 Encap Summary (all of it) + actual = re.sub(r"IPv4 Encap Summary \(VRF default\):", "", actual) + actual = re.sub(r"No IPv4 Encap neighbor is configured", "", actual) + # Remove Unknown Summary (all of it) + actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual) + actual = re.sub(r"No Unknown neighbor is configured", "", actual) + # Make Connect/Active/Idle the same (change them all to Active) + actual = re.sub(r" Connect ", " Active ", actual) + actual = re.sub(r" Idle ", " Active ", actual) + + actual = re.sub( + r"IPv4 labeled-unicast Summary \(VRF default\):", "", actual + ) + actual = re.sub( + r"No IPv4 labeled-unicast neighbor is configured", "", actual + ) + + expected = expected_original + # apply argumentss on expected output + if "internal" in arguments or "remote-as 100" in arguments: + expected = re.sub(r".+\s+200\s+.+", "", expected) + elif "external" in arguments: + expected = re.sub(r".+\s+100\s+.+Active.+", "", expected) + elif "remote-as 123" in arguments: + expected = re.sub( + r"(192.168.7.(1|2)0|fc00:0:0:8::(1|2)000).+Active.+", + "", + expected, + ) + expected = re.sub(r"\nNeighbor.+Desc", "", expected) + expected = expected + "% No matching neighbor\n" + elif "192.168.7.10" in arguments: + expected = re.sub( + r"(192.168.7.20|fc00:0:0:8::(1|2)000).+Active.+", "", expected + ) + elif "fc00:0:0:8::1000" in arguments: + expected = re.sub( + r"(192.168.7.(1|2)0|fc00:0:0:8::2000).+Active.+", "", expected + ) + elif "10.0.0.1" in arguments: + expected = "No such neighbor in this view/vrf" + + if "terse" in arguments: + expected = re.sub(r"BGP table version .+", "", expected) + expected = re.sub(r"RIB entries .+", "", expected) + expected = re.sub(r"Peers [0-9]+, using .+", "", expected) + + # Strip empty lines + actual = actual.lstrip().rstrip() + expected = expected.lstrip().rstrip() + actual = re.sub(r"\n+", "\n", actual) + expected = re.sub(r"\n+", "\n", expected) + + # reapply initial formatting + if "terse" in arguments: + actual = re.sub(r" vrf-id 0\n", " vrf-id 0\n\n", actual) + expected = re.sub(r" vrf-id 0\n", " vrf-id 0\n\n", expected) + else: + actual = re.sub(r"KiB of memory\n", "KiB of memory\n\n", actual) + expected = re.sub(r"KiB of memory\n", "KiB of memory\n\n", expected) + + # realign expected neighbor columns if needed + try: + idx_actual = ( + re.search(r"(Neighbor\s+V\s+)", actual).group(1).find("V") + ) + idx_expected = ( + re.search(r"(Neighbor\s+V\s+)", expected).group(1).find("V") + ) + idx_diff = idx_expected - idx_actual + if idx_diff > 0: + # Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd + expected = re.sub(" " * idx_diff + "V ", "V ", expected) + # 192.168.7.10 4 100 0 0 0 0 0 never Active + expected = re.sub(" " * idx_diff + "4 ", "4 ", expected) + except AttributeError: + pass + + # Fix newlines (make them all the same) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + + # Generate Diff + diff = topotest.get_textdiff( + actual, + expected, + title1="actual SHOW IP BGP SUMMARY " + arguments.upper(), + title2="expected SHOW IP BGP SUMMARY " + arguments.upper(), + ) + + # Empty string if it matches, otherwise diff contains unified diff + if diff: + sys.stderr.write( + "r%s failed SHOW IP BGP SUMMARY check:\n%s\n" % (i, diff) + ) + failures += 1 + else: + print("r%s ok" % i) + + assert ( + failures == 0 + ), "SHOW IP BGP SUMMARY failed for router r%s:\n%s" % ( + i, + diff, + ) + + # Make sure that all daemons are running + for i in range(1, 2): + fatal_error = net["r%s" % i].checkRouterRunning() + assert fatal_error == "", fatal_error + + +def test_bgp_ipv6_summary(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + print("\n\n** Verifying BGP IPv6 Summary") + print("******************************************\n") + failures = 0 + for i in range(1, 2): + refTableFile = "%s/r%s/show_bgp_ipv6_summary.ref" % (thisDir, i) + if os.path.isfile(refTableFile): + # Read expected result from file + expected = open(refTableFile).read().rstrip() + # Fix newlines (make them all the same) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + + # Actual output from router + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show bgp ipv6 summary" 2> /dev/null') + .rstrip() + ) + # Mask out "using XXiXX bytes" portion. They are random... + actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual) + # Mask out "using XiXXX KiB" portion. They are random... + actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual) + # + # Remove extra summaries which exist with newer versions + # + # Remove summary lines (changed recently) + actual = re.sub(r"Total number.*", "", actual) + actual = re.sub(r"Displayed.*", "", actual) + # Remove IPv4 Unicast Summary (Title only) + actual = re.sub(r"IPv6 Unicast Summary \(VRF default\):", "", actual) + # Remove IPv4 Multicast Summary (all of it) + actual = re.sub(r"IPv6 Multicast Summary \(VRF default\):", "", actual) + actual = re.sub(r"No IPv6 Multicast neighbor is configured", "", actual) + # Remove IPv4 VPN Summary (all of it) + actual = re.sub(r"IPv6 VPN Summary \(VRF default\):", "", actual) + actual = re.sub(r"No IPv6 VPN neighbor is configured", "", actual) + # Remove IPv4 Encap Summary (all of it) + actual = re.sub(r"IPv6 Encap Summary \(VRF default\):", "", actual) + actual = re.sub(r"No IPv6 Encap neighbor is configured", "", actual) + # Remove Unknown Summary (all of it) + actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual) + actual = re.sub(r"No Unknown neighbor is configured", "", actual) + # Make Connect/Active/Idle the same (change them all to Active) + actual = re.sub(r" Connect ", " Active ", actual) + actual = re.sub(r" Idle ", " Active ", actual) + + # Remove Labeled Unicast Summary (all of it) + actual = re.sub( + r"IPv6 labeled-unicast Summary \(VRF default\):", "", actual + ) + actual = re.sub( + r"No IPv6 labeled-unicast neighbor is configured", "", actual + ) + + # Strip empty lines + actual = actual.lstrip() + actual = actual.rstrip() + # + # Fix newlines (make them all the same) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + + # Generate Diff + diff = topotest.get_textdiff( + actual, + expected, + title1="actual SHOW BGP IPv6 SUMMARY", + title2="expected SHOW BGP IPv6 SUMMARY", + ) + + # Empty string if it matches, otherwise diff contains unified diff + if diff: + sys.stderr.write( + "r%s failed SHOW BGP IPv6 SUMMARY check:\n%s\n" % (i, diff) + ) + failures += 1 + else: + print("r%s ok" % i) + + assert failures == 0, "SHOW BGP IPv6 SUMMARY failed for router r%s:\n%s" % ( + i, + diff, + ) + + # Make sure that all daemons are running + for i in range(1, 2): + fatal_error = net["r%s" % i].checkRouterRunning() + assert fatal_error == "", fatal_error + + +def test_nht(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + print("\n\n**** Test that nexthop tracking is at least nominally working ****\n") + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + for i in range(1, 2): + nhtFile = "%s/r%s/ip_nht.ref" % (thisDir, i) + expected = open(nhtFile).read().rstrip() + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + + actual = net["r%s" % i].cmd('vtysh -c "show ip nht" 2> /dev/null').rstrip() + actual = re.sub(r"fd [0-9]+", "fd XX", actual) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + + diff = topotest.get_textdiff( + actual, + expected, + title1="Actual `show ip nht`", + title2="Expected `show ip nht`", + ) + + if diff: + assert 0, "r%s failed ip nht check:\n%s\n" % (i, diff) + else: + print("show ip nht is ok\n") + + nhtFile = "%s/r%s/ipv6_nht.ref" % (thisDir, i) + expected = open(nhtFile).read().rstrip() + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + + actual = net["r%s" % i].cmd('vtysh -c "show ipv6 nht" 2> /dev/null').rstrip() + actual = re.sub(r"fd [0-9]+", "fd XX", actual) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + + diff = topotest.get_textdiff( + actual, + expected, + title1="Actual `show ip nht`", + title2="Expected `show ip nht`", + ) + + if diff: + assert 0, "r%s failed ipv6 nht check:\n%s\n" % (i, diff) + else: + print("show ipv6 nht is ok\n") + + +def test_bgp_ipv4(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + print("\n\n** Verifying BGP IPv4") + print("******************************************\n") + diffresult = {} + for i in range(1, 2): + success = 0 + for refTableFile in glob.glob("%s/r%s/show_bgp_ipv4*.ref" % (thisDir, i)): + if os.path.isfile(refTableFile): + # Read expected result from file + expected = open(refTableFile).read().rstrip() + # Fix newlines (make them all the same) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + + # Actual output from router + actual = ( + net["r%s" % i].cmd('vtysh -c "show bgp ipv4" 2> /dev/null').rstrip() + ) + # Remove summary line (changed recently) + actual = re.sub(r"Total number.*", "", actual) + actual = re.sub(r"Displayed.*", "", actual) + actual = actual.rstrip() + # Fix newlines (make them all the same) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + + # Generate Diff + diff = topotest.get_textdiff( + actual, + expected, + title1="actual SHOW BGP IPv4", + title2="expected SHOW BGP IPv4", + ) + + # Empty string if it matches, otherwise diff contains unified diff + if diff: + diffresult[refTableFile] = diff + else: + success = 1 + print("template %s matched: r%s ok" % (refTableFile, i)) + break + + if not success: + resultstr = "No template matched.\n" + for f in diffresult.keys(): + resultstr += "template %s: r%s failed SHOW BGP IPv4 check:\n%s\n" % ( + f, + i, + diffresult[f], + ) + raise AssertionError( + "SHOW BGP IPv4 failed for router r%s:\n%s" % (i, resultstr) + ) + + # Make sure that all daemons are running + for i in range(1, 2): + fatal_error = net["r%s" % i].checkRouterRunning() + assert fatal_error == "", fatal_error + + +def test_bgp_ipv6(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + print("\n\n** Verifying BGP IPv6") + print("******************************************\n") + diffresult = {} + for i in range(1, 2): + success = 0 + for refTableFile in glob.glob("%s/r%s/show_bgp_ipv6*.ref" % (thisDir, i)): + if os.path.isfile(refTableFile): + # Read expected result from file + expected = open(refTableFile).read().rstrip() + # Fix newlines (make them all the same) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + + # Actual output from router + actual = ( + net["r%s" % i].cmd('vtysh -c "show bgp ipv6" 2> /dev/null').rstrip() + ) + # Remove summary line (changed recently) + actual = re.sub(r"Total number.*", "", actual) + actual = re.sub(r"Displayed.*", "", actual) + actual = actual.rstrip() + # Fix newlines (make them all the same) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + + # Generate Diff + diff = topotest.get_textdiff( + actual, + expected, + title1="actual SHOW BGP IPv6", + title2="expected SHOW BGP IPv6", + ) + + # Empty string if it matches, otherwise diff contains unified diff + if diff: + diffresult[refTableFile] = diff + else: + success = 1 + print("template %s matched: r%s ok" % (refTableFile, i)) + + if not success: + resultstr = "No template matched.\n" + for f in diffresult.keys(): + resultstr += "template %s: r%s failed SHOW BGP IPv6 check:\n%s\n" % ( + f, + i, + diffresult[f], + ) + raise AssertionError( + "SHOW BGP IPv6 failed for router r%s:\n%s" % (i, resultstr) + ) + + # Make sure that all daemons are running + for i in range(1, 2): + fatal_error = net["r%s" % i].checkRouterRunning() + assert fatal_error == "", fatal_error + + +def test_route_map(): + global fatal_error + net = get_topogen().net + + if fatal_error != "": + pytest.skip(fatal_error) + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + print("\n\n** Verifying some basic routemap forward references\n") + print("*******************************************************\n") + failures = 0 + for i in range(1, 2): + refroutemap = "%s/r%s/show_route_map.ref" % (thisDir, i) + if os.path.isfile(refroutemap): + expected = open(refroutemap).read().rstrip() + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + + actual = ( + net["r%s" % i].cmd('vtysh -c "show route-map" 2> /dev/null').rstrip() + ) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + + diff = topotest.get_textdiff( + actual, + expected, + title1="actual show route-map", + title2="expected show route-map", + ) + + if diff: + sys.stderr.write( + "r%s failed show route-map command Check:\n%s\n" % (i, diff) + ) + failures += 1 + else: + print("r%s ok" % i) + + assert ( + failures == 0 + ), "Show route-map command failed for router r%s:\n%s" % (i, diff) + + +def test_nexthop_groups_with_route_maps(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + print("\n\n** Verifying Nexthop Groups With Route-Maps") + print("******************************************\n") + + ### Nexthop Group With Route-Map Tests + + # Create a lib nexthop-group + net["r1"].cmd( + 'vtysh -c "c t" -c "nexthop-group test" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"' + ) + + ## Route-Map Proto Source + + route_str = "2.2.2.1" + src_str = "192.168.0.1" + + net["r1"].cmd( + 'vtysh -c "c t" -c "route-map NH-SRC permit 111" -c "set src %s"' % src_str + ) + net["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NH-SRC"') + + net["r1"].cmd('vtysh -c "sharp install routes %s nexthop-group test 1"' % route_str) + + verify_route_nexthop_group("%s/32" % route_str) + + # Only a valid test on linux using nexthop objects + if sys.platform.startswith("linux"): + output = net["r1"].cmd("ip route show %s/32" % route_str) + match = re.search(r"src %s" % src_str, output) + assert match is not None, "Route %s/32 not installed with src %s" % ( + route_str, + src_str, + ) + + # Remove NHG routes and route-map + net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % route_str) + net["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NH-SRC"') + net["r1"].cmd( + 'vtysh -c "c t" -c "no route-map NH-SRC permit 111" # -c "set src %s"' % src_str + ) + net["r1"].cmd('vtysh -c "c t" -c "no route-map NH-SRC"') + + ## Route-Map Deny/Permit with same nexthop group + + permit_route_str = "3.3.3.1" + deny_route_str = "3.3.3.2" + + net["r1"].cmd( + 'vtysh -c "c t" -c "ip prefix-list NOPE seq 5 permit %s/32"' % permit_route_str + ) + net["r1"].cmd( + 'vtysh -c "c t" -c "route-map NOPE permit 111" -c "match ip address prefix-list NOPE"' + ) + net["r1"].cmd('vtysh -c "c t" -c "route-map NOPE deny 222"') + net["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NOPE"') + + # This route should be permitted + net["r1"].cmd( + 'vtysh -c "sharp install routes %s nexthop-group test 1"' % permit_route_str + ) + + verify_route_nexthop_group("%s/32" % permit_route_str) + + # This route should be denied + net["r1"].cmd( + 'vtysh -c "sharp install routes %s nexthop-group test 1"' % deny_route_str + ) + + nhg_id = route_get_nhg_id(deny_route_str) + output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id) + + match = re.search(r"Valid", output) + assert match is None, "Nexthop Group ID=%d should not be marked Valid" % nhg_id + + match = re.search(r"Installed", output) + assert match is None, "Nexthop Group ID=%d should not be marked Installed" % nhg_id + + # Remove NHG routes and route-map + net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % permit_route_str) + net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % deny_route_str) + net["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NOPE"') + net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE permit 111"') + net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE deny 222"') + net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE"') + net["r1"].cmd( + 'vtysh -c "c t" -c "no ip prefix-list NOPE seq 5 permit %s/32"' + % permit_route_str + ) + + +def test_nexthop_group_replace(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + print("\n\n** Verifying Nexthop Groups") + print("******************************************\n") + + ### Nexthop Group Tests + + ## 2-Way ECMP Directly Connected + + net["r1"].cmd( + 'vtysh -c "c t" -c "nexthop-group replace" -c "nexthop 1.1.1.1 r1-eth1 onlink" -c "nexthop 1.1.1.2 r1-eth2 onlink"' + ) + + # At the moment there is absolutely no real easy way to query sharpd + # for the nexthop group actually installed. If it is not installed + # sharpd will just transmit the nexthops down instead of the nexthop + # group id. Leading to a situation where the replace is not actually + # being tested. So let's just wait some time here because this + # is hard and this test fails all the time + sleep(5) + + # Create with sharpd using nexthop-group + net["r1"].cmd('vtysh -c "sharp install routes 3.3.3.1 nexthop-group replace 1"') + + verify_route_nexthop_group("3.3.3.1/32") + + # Change the nexthop group + net["r1"].cmd( + 'vtysh -c "c t" -c "nexthop-group replace" -c "no nexthop 1.1.1.1 r1-eth1 onlink" -c "nexthop 1.1.1.3 r1-eth1 onlink" -c "nexthop 1.1.1.4 r1-eth4 onlink"' + ) + + # Verify it updated. We can just check install and ecmp count here. + verify_route_nexthop_group("3.3.3.1/32", False, 3) + + +def test_mpls_interfaces(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + # Skip if no LDP installed or old kernel + if net["r1"].daemon_available("ldpd") == False: + pytest.skip("No MPLS or kernel < 4.5") + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + print("\n\n** Verifying MPLS Interfaces") + print("******************************************\n") + failures = 0 + for i in range(1, 2): + refTableFile = "%s/r%s/show_mpls_ldp_interface.ref" % (thisDir, i) + if os.path.isfile(refTableFile): + # Read expected result from file + expected = open(refTableFile).read().rstrip() + # Fix newlines (make them all the same) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + + # Actual output from router + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show mpls ldp interface" 2> /dev/null') + .rstrip() + ) + # Mask out Timer in Uptime + actual = re.sub(r" [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ", " xx:xx:xx ", actual) + # Fix newlines (make them all the same) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + + # Generate Diff + diff = topotest.get_textdiff( + actual, + expected, + title1="actual MPLS LDP interface status", + title2="expected MPLS LDP interface status", + ) + + # Empty string if it matches, otherwise diff contains unified diff + if diff: + sys.stderr.write( + "r%s failed MPLS LDP Interface status Check:\n%s\n" % (i, diff) + ) + failures += 1 + else: + print("r%s ok" % i) + + if failures > 0: + fatal_error = "MPLS LDP Interface status failed" + + assert ( + failures == 0 + ), "MPLS LDP Interface status failed for router r%s:\n%s" % (i, diff) + + # Make sure that all daemons are running + for i in range(1, 2): + fatal_error = net["r%s" % i].checkRouterRunning() + assert fatal_error == "", fatal_error + + +def test_resilient_nexthop_group(): + net = get_topogen().net + + result = required_linux_kernel_version("5.19") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >= 5.19") + + net["r1"].cmd( + 'vtysh -c "conf" -c "nexthop-group resilience" -c "resilient buckets 64 idle-timer 128 unbalanced-timer 256" -c "nexthop 1.1.1.1 r1-eth1 onlink" -c "nexthop 1.1.1.2 r1-eth2 onlink"' + ) + + output = net["r1"].cmd('vtysh -c "show nexthop-group rib sharp"') + buckets = re.findall(r"Buckets", output) + + output = net["r1"].cmd('vtysh -c "show nexthop-group rib sharp json"') + + joutput = json.loads(output) + + # Use the json output and collect the nhg id from it + + for nhgid in joutput: + n = joutput[nhgid] + if "buckets" in n: + break + + verify_nexthop_group(int(nhgid)) + assert len(buckets) == 1, "Resilient NHG not created in zebra" + + +def test_shutdown_check_stderr(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + print("\n\n** Verifying unexpected STDERR output from daemons") + print("******************************************\n") + + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + print( + "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n" + ) + pytest.skip("Skipping test for Stderr output") + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + print("thisDir=" + thisDir) + + net["r1"].stopRouter() + + log = net["r1"].getStdErr("ripd") + if log: + print("\nRIPd StdErr Log:\n" + log) + log = net["r1"].getStdErr("ripngd") + if log: + print("\nRIPngd StdErr Log:\n" + log) + log = net["r1"].getStdErr("ospfd") + if log: + print("\nOSPFd StdErr Log:\n" + log) + log = net["r1"].getStdErr("ospf6d") + if log: + print("\nOSPF6d StdErr Log:\n" + log) + log = net["r1"].getStdErr("isisd") + if log: + print("\nISISd StdErr Log:\n" + log) + log = net["r1"].getStdErr("bgpd") + if log: + print("\nBGPd StdErr Log:\n" + log) + + log = net["r1"].getStdErr("nhrpd") + if log: + print("\nNHRPd StdErr Log:\n" + log) + + log = net["r1"].getStdErr("pbrd") + if log: + print("\nPBRd StdErr Log:\n" + log) + + log = net["r1"].getStdErr("babeld") + if log: + print("\nBABELd StdErr Log:\n" + log) + + if net["r1"].daemon_available("ldpd"): + log = net["r1"].getStdErr("ldpd") + if log: + print("\nLDPd StdErr Log:\n" + log) + log = net["r1"].getStdErr("zebra") + if log: + print("\nZebra StdErr Log:\n" + log) + + +def test_shutdown_check_memleak(): + global fatal_error + net = get_topogen().net + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None: + print( + "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n" + ) + pytest.skip("Skipping test for memory leaks") + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + for i in range(1, 2): + net["r%s" % i].stopRouter() + net["r%s" % i].report_memory_leaks( + os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__) + ) + + +if __name__ == "__main__": + # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli + # retval = pytest.main(["-s", "--tb=no"]) + retval = pytest.main(["-s"]) + sys.exit(retval) diff --git a/tests/topotests/analyze.py b/tests/topotests/analyze.py new file mode 100755 index 0000000..690786a --- /dev/null +++ b/tests/topotests/analyze.py @@ -0,0 +1,433 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# July 9 2021, Christian Hopps +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# +import argparse +import atexit +import logging +import os +import re +import subprocess +import sys +import tempfile +from collections import OrderedDict + +import xmltodict + + +def get_range_list(rangestr): + result = [] + for e in rangestr.split(","): + e = e.strip() + if not e: + continue + if e.find("-") == -1: + result.append(int(e)) + else: + start, end = e.split("-") + result.extend(list(range(int(start), int(end) + 1))) + return result + + +def dict_range_(dct, rangestr, dokeys): + keys = list(dct.keys()) + if not rangestr or rangestr == "all": + for key in keys: + if dokeys: + yield key + else: + yield dct[key] + return + + dlen = len(keys) + for index in get_range_list(rangestr): + if index >= dlen: + break + key = keys[index] + if dokeys: + yield key + else: + yield dct[key] + + +def dict_range_keys(dct, rangestr): + return dict_range_(dct, rangestr, True) + + +def dict_range_values(dct, rangestr): + return dict_range_(dct, rangestr, False) + + +def get_summary(results): + ntest = int(results["@tests"]) + nfail = int(results["@failures"]) + nerror = int(results["@errors"]) + nskip = int(results["@skipped"]) + npass = ntest - nfail - nskip - nerror + return ntest, npass, nfail, nerror, nskip + + +def print_summary(results, args): + ntest, npass, nfail, nerror, nskip = (0, 0, 0, 0, 0) + for group in results: + _ntest, _npass, _nfail, _nerror, _nskip = get_summary(results[group]) + if args.verbose: + print( + f"Group: {group} Total: {_ntest} PASSED: {_npass}" + " FAIL: {_nfail} ERROR: {_nerror} SKIP: {_nskip}" + ) + ntest += _ntest + npass += _npass + nfail += _nfail + nerror += _nerror + nskip += _nskip + print(f"Total: {ntest} PASSED: {npass} FAIL: {nfail} ERROR: {nerror} SKIP: {nskip}") + + +def get_global_testcase(results): + for group in results: + for testcase in results[group]["testcase"]: + if "@file" not in testcase: + return testcase + return None + + +def get_filtered(tfilters, results, args): + if isinstance(tfilters, str) or tfilters is None: + tfilters = [tfilters] + found_files = OrderedDict() + for group in results: + if isinstance(results[group]["testcase"], list): + tlist = results[group]["testcase"] + else: + tlist = [results[group]["testcase"]] + for testcase in tlist: + for tfilter in tfilters: + if tfilter is None: + if ( + "failure" not in testcase + and "error" not in testcase + and "skipped" not in testcase + ): + break + elif tfilter in testcase: + break + else: + continue + # cname = testcase["@classname"] + fname = testcase.get("@file", "") + cname = testcase.get("@classname", "") + if not fname and not cname: + name = testcase.get("@name", "") + if not name: + continue + # If we had a failure at the module level we could be here. + fname = name.replace(".", "/") + ".py" + tcname = fname + else: + if not fname: + fname = cname.replace(".", "/") + ".py" + if "@name" not in testcase: + tcname = fname + else: + tcname = fname + "::" + testcase["@name"] + found_files[tcname] = testcase + return found_files + + +def search_testcase(testcase, regexp): + for key, val in testcase.items(): + if regexp.search(str(val)): + return True + return False + + +def dump_testcase(testcase): + s = "" + for key, val in testcase.items(): + if isinstance(val, str) or isinstance(val, float) or isinstance(val, int): + s += "{}: {}\n".format(key, val) + elif isinstance(val, list): + for k2, v2 in enumerate(val): + s += "{}: {}\n".format(k2, v2) + else: + for k2, v2 in val.items(): + s += "{}: {}\n".format(k2, v2) + return s + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "-a", + "--save-xml", + action="store_true", + help=( + "Move [container:]/tmp/topotests/topotests.xml " + "to --results value if --results does not exist yet" + ), + ) + parser.add_argument( + "-A", + "--save", + action="store_true", + help=( + "Move [container:]/tmp/topotests{,.xml} " + "to --results value if --results does not exist yet" + ), + ) + parser.add_argument( + "-C", + "--container", + help="specify docker/podman container of the run", + ) + parser.add_argument( + "--use-podman", + action="store_true", + help="Use `podman` instead of `docker` for saving container data", + ) + parser.add_argument( + "-S", + "--select", + help=( + "select results combination of letters: " + "'e'rrored 'f'ailed 'p'assed 's'kipped. " + "Default is 'fe', unless --search or --time which default to 'efps'" + ), + ) + parser.add_argument( + "-R", + "--search", + help=( + "filter results to those which match a regex. " + "All test text is search unless restricted by --errmsg or --errtext" + ), + ) + parser.add_argument( + "-r", + "--results", + help="xml results file or directory containing xml results file", + ) + parser.add_argument("--rundir", help=argparse.SUPPRESS) + parser.add_argument( + "-E", + "--enumerate", + action="store_true", + help="enumerate each item (results scoped)", + ) + parser.add_argument( + "-T", "--test", help="select testcase at given ordinal from the enumerated list" + ) + parser.add_argument( + "--errmsg", action="store_true", help="print testcase error message" + ) + parser.add_argument( + "--errtext", action="store_true", help="print testcase error text" + ) + parser.add_argument( + "--full", action="store_true", help="print all logging for selected testcases" + ) + parser.add_argument("--time", action="store_true", help="print testcase run times") + + parser.add_argument("-s", "--summary", action="store_true", help="print summary") + parser.add_argument("-v", "--verbose", action="store_true", help="be verbose") + args = parser.parse_args() + + if args.save and args.save_xml: + logging.critical("Only one of --save or --save-xml allowed") + sys.exit(1) + + scount = bool(args.save) + bool(args.save_xml) + + # + # Saving/Archiving results + # + + docker_bin = "podman" if args.use_podman else "docker" + contid = "" + if args.container: + # check for container existence + contid = args.container + try: + # p = + subprocess.run( + f"{docker_bin} inspect {contid}", + check=True, + shell=True, + errors="ignore", + capture_output=True, + ) + except subprocess.CalledProcessError: + print(f"{docker_bin} container '{contid}' does not exist") + sys.exit(1) + # If you need container info someday... + # cont_info = json.loads(p.stdout) + + cppath = "/tmp/topotests" + if args.save_xml or scount == 0: + cppath += "/topotests.xml" + if contid: + cppath = contid + ":" + cppath + + tresfile = None + + if scount and args.results and not os.path.exists(args.results): + if not contid: + if not os.path.exists(cppath): + print(f"'{cppath}' doesn't exist to save") + sys.exit(1) + if args.save_xml: + subprocess.run(["cp", cppath, args.results]) + else: + subprocess.run(["mv", cppath, args.results]) + else: + try: + subprocess.run( + f"{docker_bin} cp {cppath} {args.results}", + check=True, + shell=True, + errors="ignore", + capture_output=True, + ) + except subprocess.CalledProcessError as error: + print(f"Can't {docker_bin} cp '{cppath}': %s", str(error)) + sys.exit(1) + + if "SUDO_USER" in os.environ: + subprocess.run(["chown", "-R", os.environ["SUDO_USER"], args.results]) + elif not args.results: + # User doesn't want to save results just use them inplace + if not contid: + if not os.path.exists(cppath): + print(f"'{cppath}' doesn't exist") + sys.exit(1) + args.results = cppath + else: + tresfile, tresname = tempfile.mkstemp( + suffix=".xml", prefix="topotests-", text=True + ) + atexit.register(lambda: os.unlink(tresname)) + os.close(tresfile) + try: + subprocess.run( + f"{docker_bin} cp {cppath} {tresname}", + check=True, + shell=True, + errors="ignore", + capture_output=True, + ) + except subprocess.CalledProcessError as error: + print(f"Can't {docker_bin} cp '{cppath}': %s", str(error)) + sys.exit(1) + args.results = tresname + + # + # Result option validation + # + + count = 0 + if args.errmsg: + count += 1 + if args.errtext: + count += 1 + if args.full: + count += 1 + if count > 1: + logging.critical("Only one of --full, --errmsg or --errtext allowed") + sys.exit(1) + + if args.time and count: + logging.critical("Can't use --full, --errmsg or --errtext with --time") + sys.exit(1) + + if args.enumerate and (count or args.time or args.test): + logging.critical( + "Can't use --enumerate with --errmsg, --errtext, --full, --test or --time" + ) + sys.exit(1) + + results = {} + ttfiles = [] + + if os.path.exists(os.path.join(args.results, "topotests.xml")): + args.results = os.path.join(args.results, "topotests.xml") + if not os.path.exists(args.results): + logging.critical("%s doesn't exist", args.results) + sys.exit(1) + + ttfiles = [args.results] + + for f in ttfiles: + m = re.match(r"tt-group-(\d+)/topotests.xml", f) + group = int(m.group(1)) if m else 0 + with open(f) as xml_file: + results[group] = xmltodict.parse(xml_file.read())["testsuites"]["testsuite"] + + search_re = re.compile(args.search) if args.search else None + + if args.select is None: + if search_re or args.time: + args.select = "efsp" + else: + args.select = "fe" + + filters = [] + if "e" in args.select: + filters.append("error") + if "f" in args.select: + filters.append("failure") + if "s" in args.select: + filters.append("skipped") + if "p" in args.select: + filters.append(None) + + found_files = get_filtered(filters, results, args) + + if search_re: + found_files = { + k: v for k, v in found_files.items() if search_testcase(v, search_re) + } + + if args.enumerate: + # print the selected test names with ordinal + print("\n".join(["{} {}".format(i, x) for i, x in enumerate(found_files)])) + elif args.test is None and count == 0 and not args.time: + # print the selected test names + print("\n".join([str(x) for x in found_files])) + else: + rangestr = args.test if args.test else "all" + for key in dict_range_keys(found_files, rangestr): + testcase = found_files[key] + if args.time: + text = testcase["@time"] + s = "{}: {}".format(text, key) + elif args.errtext: + if "error" in testcase: + errmsg = testcase["error"]["#text"] + elif "failure" in testcase: + errmsg = testcase["failure"]["#text"] + else: + errmsg = "none found" + s = "{}: {}".format(key, errmsg) + elif args.errmsg: + if "error" in testcase: + errmsg = testcase["error"]["@message"] + elif "failure" in testcase: + errmsg = testcase["failure"]["@message"] + else: + errmsg = "none found" + s = "{}: {}".format(key, errmsg) + else: + s = dump_testcase(testcase) + print(s) + + if args.summary: + print_summary(results, args) + + +if __name__ == "__main__": + main() diff --git a/tests/topotests/babel_topo1/r1/babeld.conf b/tests/topotests/babel_topo1/r1/babeld.conf new file mode 100644 index 0000000..4058362 --- /dev/null +++ b/tests/topotests/babel_topo1/r1/babeld.conf @@ -0,0 +1,20 @@ + +interface r1-eth0 + babel hello-interval 1000 + babel wired + babel update-interval 50 +! +interface r1-eth1 + babel hello-interval 1000 + babel wired + babel update-interval 50 +! +router babel + network r1-eth0 + network r1-eth1 + redistribute ipv4 connected + redistribute ipv6 connected +! +line vty +! + diff --git a/tests/topotests/babel_topo1/r1/show_ip_route.json_ref b/tests/topotests/babel_topo1/r1/show_ip_route.json_ref new file mode 100644 index 0000000..0d52b67 --- /dev/null +++ b/tests/topotests/babel_topo1/r1/show_ip_route.json_ref @@ -0,0 +1,80 @@ +{ + "192.168.1.0/24":[ + { + "prefix":"192.168.1.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "192.168.2.0/24":[ + { + "prefix":"192.168.2.0/24", + "prefixLen":24, + "protocol":"babel", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"193.1.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth1" + } + ] + } + ], + "192.168.3.0/24":[ + { + "prefix":"192.168.3.0/24", + "protocol":"babel", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"193.1.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "193.1.1.0/26":[ + { + "prefix":"193.1.1.0/26", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "193.1.2.0/24":[ + { + "prefix":"193.1.2.0/24", + "protocol":"babel", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"193.1.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/babel_topo1/r1/zebra.conf b/tests/topotests/babel_topo1/r1/zebra.conf new file mode 100644 index 0000000..5eda3e2 --- /dev/null +++ b/tests/topotests/babel_topo1/r1/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! debug zebra rib detail +! +hostname r1 +! +interface r1-eth0 + ip address 192.168.1.1/24 + ipv6 address 192:168:1::1/64 +! +interface r1-eth1 + description to sw2 - babel interface + ip address 193.1.1.1/26 + ipv6 address 193:1:1::1/64 + no link-detect +! +ip forwarding +ipv6 forwarding +! +! +line vty +! + diff --git a/tests/topotests/babel_topo1/r2/babeld.conf b/tests/topotests/babel_topo1/r2/babeld.conf new file mode 100644 index 0000000..bae4e59 --- /dev/null +++ b/tests/topotests/babel_topo1/r2/babeld.conf @@ -0,0 +1,17 @@ +! +interface r2-eth0 + babel hello-interval 1000 + babel wired + babel update-interval 50 +! +interface r2-eth1 + babel hello-interval 1000 + babel wired + babel update-interval 50 +! +router babel + network r2-eth0 + network r2-eth1 + redistribute ipv4 connected + redistribute ipv6 connected +! diff --git a/tests/topotests/babel_topo1/r2/show_ip_route.json_ref b/tests/topotests/babel_topo1/r2/show_ip_route.json_ref new file mode 100644 index 0000000..ff3f6ab --- /dev/null +++ b/tests/topotests/babel_topo1/r2/show_ip_route.json_ref @@ -0,0 +1,80 @@ +{ + "192.168.1.0/24":[ + { + "prefix":"192.168.1.0/24", + "protocol":"babel", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"193.1.1.1", + "afi":"ipv4", + "interfaceName":"r2-eth0", + "active":true + } + ] + } + ], + "192.168.2.0/24":[ + { + "prefix":"192.168.2.0/24", + "protocol":"babel", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"193.1.2.2", + "afi":"ipv4", + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "192.168.3.0/24":[ + { + "prefix":"192.168.3.0/24", + "protocol":"babel", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"193.1.2.2", + "afi":"ipv4", + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "193.1.1.0/26":[ + { + "prefix":"193.1.1.0/26", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r2-eth0", + "active":true + } + ] + } + ], + "193.1.2.0/24":[ + { + "prefix":"193.1.2.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/babel_topo1/r2/zebra.conf b/tests/topotests/babel_topo1/r2/zebra.conf new file mode 100644 index 0000000..301a8b2 --- /dev/null +++ b/tests/topotests/babel_topo1/r2/zebra.conf @@ -0,0 +1,23 @@ +log file zebra.log +! +hostname r2 +! +interface r2-eth0 + description to sw2 - babel interface + ip address 193.1.1.2/26 + ipv6 address 193:1:1::2/64 + no link-detect +! +interface r2-eth1 + description to sw3 - babel interface + ip address 193.1.2.1/24 + ipv6 address 193:1:2::1/64 + no link-detect +! +ip forwarding +ipv6 forwarding +! +! +line vty +! + diff --git a/tests/topotests/babel_topo1/r3/babeld.conf b/tests/topotests/babel_topo1/r3/babeld.conf new file mode 100644 index 0000000..e10e5aa --- /dev/null +++ b/tests/topotests/babel_topo1/r3/babeld.conf @@ -0,0 +1,16 @@ +! +interface r3-eth0 + babel hello-interval 1000 + babel wired + babel update-interval 50 +! +interface r3-eth1 + babel hello-interval 1000 + babel wired + babel update-interval 50 +! +router babel + network r3-eth0 + network r3-eth1 + redistribute ipv4 connected + redistribute ipv4 static diff --git a/tests/topotests/babel_topo1/r3/show_ip_route.json_ref b/tests/topotests/babel_topo1/r3/show_ip_route.json_ref new file mode 100644 index 0000000..7fe66fa --- /dev/null +++ b/tests/topotests/babel_topo1/r3/show_ip_route.json_ref @@ -0,0 +1,81 @@ +{ + "192.168.1.0/24":[ + { + "prefix":"192.168.1.0/24", + "protocol":"babel", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"193.1.2.1", + "afi":"ipv4", + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ], + "192.168.2.0/24":[ + { + "prefix":"192.168.2.0/24", + "protocol":"static", + "selected":true, + "distance":1, + "nexthops":[ + { + "fib":true, + "ip":"192.168.3.10", + "afi":"ipv4", + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ], + "192.168.3.0/24":[ + { + "prefix":"192.168.3.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ], + "193.1.1.0/26":[ + { + "prefix":"193.1.1.0/26", + "protocol":"babel", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"193.1.2.1", + "afi":"ipv4", + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ], + "193.1.2.0/24":[ + { + "prefix":"193.1.2.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/babel_topo1/r3/zebra.conf b/tests/topotests/babel_topo1/r3/zebra.conf new file mode 100644 index 0000000..34e8c62 --- /dev/null +++ b/tests/topotests/babel_topo1/r3/zebra.conf @@ -0,0 +1,25 @@ +log file zebra.log +! +hostname r3 +! +interface r3-eth0 + description to sw4 - Stub interface + ip address 192.168.3.1/24 + ipv6 address 192:168:3::1/64 + no link-detect +! +interface r3-eth1 + description to sw3 - RIPv2 interface + ip address 193.1.2.2/24 + ipv6 address 193:1:2::2/64 + no link-detect +! +ip route 192.168.2.0/24 192.168.3.10 +ipv6 route 192:168:2::0/64 192:168:3::10 +! +ip forwarding +ipv6 forwarding +! +! +line vty +! diff --git a/tests/topotests/babel_topo1/test_babel_topo1.py b/tests/topotests/babel_topo1/test_babel_topo1.py new file mode 100644 index 0000000..decf0c2 --- /dev/null +++ b/tests/topotests/babel_topo1/test_babel_topo1.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_babel_topo1.py +# +# Copyright (c) 2017 by +# Cumulus Networks, Inc. +# Donald Sharp +# + +""" +test_babel_topo1.py: Testing BABEL + +""" + +import os +import re +import sys +import pytest +import json +from functools import partial + +pytestmark = [pytest.mark.babeld] + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + # On main router + # First switch is for a dummy interface (for local network) + switch = tgen.add_switch("sw1") + switch.add_link(tgen.gears["r1"]) + + # Switches for BABEL + # switch 2 switch is for connection to BABEL router + switch = tgen.add_switch("sw2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # switch 4 is stub on remote BABEL router + switch = tgen.add_switch("sw4") + switch.add_link(tgen.gears["r3"]) + + # switch 3 is between BABEL routers + switch = tgen.add_switch("sw3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + "Setup topology" + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BABEL, os.path.join(CWD, "{}/babeld.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_converge_protocols(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + topotest.sleep(10, "Waiting for BABEL convergence") + + +def runit(router, assertmsg, cmd, expfile): + logger.info(expfile) + + # Read expected result from file + expected = json.loads(open(expfile).read()) + + test_func = partial(topotest.router_json_cmp, router, cmd, expected) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, assertmsg + + +def test_zebra_ipv4_routingTable(): + "Test 'show ip route'" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + failures = 0 + router_list = tgen.routers().values() + for router in router_list: + assertmsg = "Zebra IPv4 Routing Table verification failed for router {}".format( + router.name + ) + refTableFile = "{}/{}/show_ip_route.json_ref".format(CWD, router.name) + runit(router, assertmsg, "show ip route json", refTableFile) + + +def test_shutdown_check_stderr(): + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + pytest.skip("Skipping test for Stderr output and memory leaks") + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Verifying unexpected STDERR output from daemons") + + router_list = tgen.routers().values() + for router in router_list: + router.stop() + + log = tgen.net[router.name].getStdErr("babeld") + if log: + logger.error("BABELd StdErr Log:" + log) + log = tgen.net[router.name].getStdErr("zebra") + if log: + logger.error("Zebra StdErr Log:" + log) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd_bgp_cbit_topo3/__init__.py b/tests/topotests/bfd_bgp_cbit_topo3/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/bfdd.conf b/tests/topotests/bfd_bgp_cbit_topo3/r1/bfdd.conf new file mode 100644 index 0000000..ee7144d --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/bfdd.conf @@ -0,0 +1,5 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/bgp_ipv6_routes_down.json b/tests/topotests/bfd_bgp_cbit_topo3/r1/bgp_ipv6_routes_down.json new file mode 100644 index 0000000..5cba71e --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/bgp_ipv6_routes_down.json @@ -0,0 +1,98 @@ +{ + "vrfName": "default", + "routerId": "10.254.254.1", + "localAS": 101, + "routes": + { + "2001:db8:6::/64": [ + { + "stale": true, + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:6::", + "prefixLen": 64, + "network": "2001:db8:6::\/64", + "metric": 0, + "weight": 0, + "peerId": "2001:db8:4::1", + "origin": "IGP", + "nexthops": [ + { "ip": "2001:db8:4::1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:7::/64": [ + { + "stale": true, + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:7::", + "prefixLen": 64, "network": + "2001:db8:7::\/64", + "metric": 0, + "weight": 0, + "peerId": "2001:db8:4::1", + "origin": "IGP", + "nexthops": [ + { + "ip": "2001:db8:4::1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:8::/64": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:8::", + "prefixLen": 64, + "network": "2001:db8:8::\/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "origin": "IGP", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:9::/64": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:9::", + "prefixLen": 64, + "network": "2001:db8:9::\/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "origin": "IGP", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} + diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/bgpd.conf b/tests/topotests/bfd_bgp_cbit_topo3/r1/bgpd.conf new file mode 100644 index 0000000..f8ad1f3 --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/bgpd.conf @@ -0,0 +1,23 @@ +! debug bgp neighbor-events +router bgp 101 + bgp router-id 10.254.254.1 + no bgp ebgp-requires-policy + no bgp network import-check + timers bgp 3 10 + bgp graceful-restart + neighbor 2001:db8:4::1 remote-as 102 + neighbor 2001:db8:4::1 timers 3 10 + neighbor 2001:db8:4::1 remote-as external + neighbor 2001:db8:4::1 bfd + neighbor 2001:db8:4::1 bfd check-control-plane-failure + neighbor 2001:db8:4::1 update-source 2001:db8:1::1 + neighbor 2001:db8:4::1 ebgp-multihop 5 + address-family ipv4 unicast + no neighbor 2001:db8:4::1 activate + exit-address-family + address-family ipv6 unicast + network 2001:db8:8::/64 + network 2001:db8:9::/64 + neighbor 2001:db8:4::1 activate + exit-address-family +! diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/ipv6_routes.json b/tests/topotests/bfd_bgp_cbit_topo3/r1/ipv6_routes.json new file mode 100644 index 0000000..8eea183 --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/ipv6_routes.json @@ -0,0 +1,80 @@ +{ + "2001:db8:1::/64": [{ + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [{ + "directlyConnected": true, + "interfaceName": "r1-eth0", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "2001:db8:4::/64": [{ + "distance": 1, + "protocol": "static", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:4::/64", + "nexthops": [{ + "interfaceName": "r1-eth0", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ], + "2001:db8:6::/64": [{ + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:6::/64", + "nexthops": [{ + "ip":"2001:db8:4::1", + "active": true, + "afi": "ipv6", + "recursive":true + }, + { + "fib":true, + "ip":"2001:db8:1::2", + "afi": "ipv6", + "interfaceName": "r1-eth0" + } + ] + } + ], + "2001:db8:7::/64": [{ + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:7::/64", + "nexthops": [{ + "ip":"2001:db8:4::1", + "active": true, + "afi": "ipv6", + "recursive": true + }, + { + "fib":true, + "ip":"2001:db8:1::2", + "afi": "ipv6", + "interfaceName":"r1-eth0" + } + ] + } + ] +} diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/peers.json b/tests/topotests/bfd_bgp_cbit_topo3/r1/peers.json new file mode 100644 index 0000000..b436d55 --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/peers.json @@ -0,0 +1,17 @@ +[ + { + "multihop":true, + "peer":"2001:db8:4::1", + "local":"2001:db8:1::1", + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":300, + "transmit-interval":300, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "remote-receive-interval":300, + "remote-transmit-interval":300, + "remote-echo-receive-interval":50 + } +] diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/peers_down.json b/tests/topotests/bfd_bgp_cbit_topo3/r1/peers_down.json new file mode 100644 index 0000000..4984b52 --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/peers_down.json @@ -0,0 +1,15 @@ +[ + { + "multihop":true, + "peer":"2001:db8:4::1", + "local":"2001:db8:1::1", + "status":"init", + "receive-interval":300, + "transmit-interval":300, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "remote-receive-interval":1000, + "remote-transmit-interval":1000, + "remote-echo-receive-interval":50 + } +] diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/zebra.conf b/tests/topotests/bfd_bgp_cbit_topo3/r1/zebra.conf new file mode 100644 index 0000000..3a30cd4 --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/zebra.conf @@ -0,0 +1,8 @@ +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ipv6 address 2001:db8:1::1/64 +! +ipv6 route 2001:db8:4::/64 2001:db8:1::2 + diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r2/zebra.conf b/tests/topotests/bfd_bgp_cbit_topo3/r2/zebra.conf new file mode 100644 index 0000000..0f70be1 --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/r2/zebra.conf @@ -0,0 +1,9 @@ +ip forwarding +ipv6 forwarding +! +interface r2-eth0 + ipv6 address 2001:db8:1::2/64 +! +interface r2-eth1 + ipv6 address 2001:db8:4::2/64 +! diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r3/bfdd.conf b/tests/topotests/bfd_bgp_cbit_topo3/r3/bfdd.conf new file mode 100644 index 0000000..ee7144d --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/r3/bfdd.conf @@ -0,0 +1,5 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r3/bgp_ipv6_routes_down.json b/tests/topotests/bfd_bgp_cbit_topo3/r3/bgp_ipv6_routes_down.json new file mode 100644 index 0000000..c0cb3c4 --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/r3/bgp_ipv6_routes_down.json @@ -0,0 +1,52 @@ +{ + "vrfName": "default", + "routerId": "10.254.254.3", + "localAS": 102, + "routes": + { + "2001:db8:6::/64": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:6::", + "prefixLen": 64, + "network": "2001:db8:6::\/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "origin": "IGP", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:7::/64": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:7::", + "prefixLen": 64, + "network": "2001:db8:7::\/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "origin": "IGP", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r3/bgpd.conf b/tests/topotests/bfd_bgp_cbit_topo3/r3/bgpd.conf new file mode 100644 index 0000000..42953a0 --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/r3/bgpd.conf @@ -0,0 +1,29 @@ +! debug bgp neighbor-events +router bgp 102 + bgp router-id 10.254.254.3 + no bgp ebgp-requires-policy + no bgp network import-check + timers bgp 3 10 + bgp graceful-restart + ! simulate NSF machine + bgp graceful-restart preserve-fw-state + bgp graceful-restart stalepath-time 900 + bgp graceful-restart restart-time 900 + neighbor 2001:db8:1::1 remote-as 101 + neighbor 2001:db8:1::1 timers 3 10 + neighbor 2001:db8:1::1 remote-as external + neighbor 2001:db8:1::1 update-source 2001:db8:4::1 + neighbor 2001:db8:1::1 bfd + neighbor 2001:db8:1::1 bfd check-control-plane-failure + neighbor 2001:db8:1::1 ebgp-multihop 5 + ! + address-family ipv4 unicast + no neighbor 2001:db8:1::1 activate + exit-address-family + ! + address-family ipv6 unicast + neighbor 2001:db8:1::1 activate + network 2001:db8:6::/64 + network 2001:db8:7::/64 + exit-address-family +! diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r3/ipv6_routes.json b/tests/topotests/bfd_bgp_cbit_topo3/r3/ipv6_routes.json new file mode 100644 index 0000000..09808cc --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/r3/ipv6_routes.json @@ -0,0 +1,80 @@ +{ + "2001:db8:1::/64": [{ + "distance": 1, + "protocol": "static", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [{ + "interfaceName": "r3-eth0", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ], + "2001:db8:4::/64": [{ + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:4::/64", + "nexthops": [{ + "directlyConnected": true, + "interfaceName": "r3-eth0", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "2001:db8:8::/64": [{ + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:8::/64", + "nexthops": [{ + "ip":"2001:db8:1::1", + "active": true, + "afi": "ipv6", + "recursive":true + }, + { + "fib":true, + "ip":"2001:db8:4::2", + "afi": "ipv6", + "interfaceName":"r3-eth0" + } + ] + } + ], + "2001:db8:9::/64": [{ + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:9::/64", + "nexthops": [{ + "ip":"2001:db8:1::1", + "active": true, + "afi": "ipv6", + "recursive":true + }, + { + "fib":true, + "ip":"2001:db8:4::2", + "afi": "ipv6", + "interfaceName":"r3-eth0" + } + ] + } + ] +} diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r3/peers.json b/tests/topotests/bfd_bgp_cbit_topo3/r3/peers.json new file mode 100644 index 0000000..fc9e145 --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/r3/peers.json @@ -0,0 +1,17 @@ +[ + { + "multihop":true, + "peer":"2001:db8:1::1", + "local":"2001:db8:4::1", + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":300, + "transmit-interval":300, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "remote-receive-interval":300, + "remote-transmit-interval":300, + "remote-echo-receive-interval":50 + } +] diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r3/peers_down.json b/tests/topotests/bfd_bgp_cbit_topo3/r3/peers_down.json new file mode 100644 index 0000000..620c6dd --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/r3/peers_down.json @@ -0,0 +1,15 @@ +[ + { + "multihop":true, + "peer":"2001:db8:1::1", + "local":"2001:db8:4::1", + "status":"down", + "receive-interval":300, + "transmit-interval":300, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "remote-receive-interval":300, + "remote-transmit-interval":300, + "remote-echo-receive-interval":50 + } +] diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r3/zebra.conf b/tests/topotests/bfd_bgp_cbit_topo3/r3/zebra.conf new file mode 100644 index 0000000..7759251 --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/r3/zebra.conf @@ -0,0 +1,7 @@ +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ipv6 address 2001:db8:4::1/64 +! +ipv6 route 2001:db8:1::/64 2001:db8:4::2 diff --git a/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.dot b/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.dot new file mode 100644 index 0000000..270de82 --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.dot @@ -0,0 +1,58 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n10.0.3.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + + r2 -- sw2 [label="eth1"]; + r3 -- sw2 [label="eth0"]; +} diff --git a/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.py b/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.py new file mode 100644 index 0000000..906687d --- /dev/null +++ b/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bfd_bgp_cbit_topo3.py +# +# Copyright (c) 2019 6WIND +# + +""" +test_bfd_bgp_cbit_topo3.py: Test the FRR BFD daemon with multihop and BGP +unnumbered. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd, pytest.mark.bfdd] + + +def setup_module(mod): + "Sets up the pytest environment" + topodef = { + "s1": ("r1", "r2"), + "s2": ("r2", "r3"), + } + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, "{}/zebra.conf".format(rname)), + ) + router.load_config( + TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + # Verify that we are using the proper version and that the BFD + # daemon exists. + for router in router_list.values(): + # Check for Version + if router.has_version("<", "5.1"): + tgen.set_error("Unsupported FRR version") + break + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged before checking for the BFD + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv6 routing tables. + logger.info("Checking IPv6 routes for convergence") + for router in tgen.routers().values(): + if router.name == "r2": + continue + json_file = "{}/{}/ipv6_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + continue + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show ipv6 route json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_bfd_connection(): + "Assert that the BFD peers can find themselves." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bfd peers to go up") + for router in tgen.routers().values(): + if router.name == "r2": + continue + json_file = "{}/{}/peers.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=32, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_bfd_loss_intermediate(): + """ + Assert that BFD notices the bfd link down failure. + but BGP entries should still be present + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + expected = { "as":101, "peers":{ "2001:db8:4::1": { "state":"Established" } } } + test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv6 uni summ json", expected) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg ='"r1" has not established bgp peering yet' + assert result is None, assertmsg + + #assert False + logger.info("removing IPv6 address from r2 to simulate loss of connectivity") + # Disable r2-eth0 ipv6 address + cmd = 'vtysh -c "configure terminal" -c "interface r2-eth1" -c "no ipv6 address 2001:db8:4::2/64"' + tgen.net["r2"].cmd(cmd) + + # Wait the minimum time we can before checking that BGP/BFD + # converged. + logger.info("waiting for BFD converge down") + + # Check that BGP converged quickly. + for router in tgen.routers().values(): + if router.name == "r2": + continue + json_file = "{}/{}/peers_down.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=32, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + logger.info("waiting for BGP entries to become stale") + for router in tgen.routers().values(): + if router.name == "r2": + continue + json_file = "{}/{}/bgp_ipv6_routes_down.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bgp ipv6 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + logger.info("Checking IPv6 routes on r1 should still be present") + for router in tgen.routers().values(): + if router.name == "r2": + continue + if router.name == "r3": + continue + json_file = "{}/r1/ipv6_routes.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show ipv6 route json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_bfd_comes_back_again(): + """ + Assert that BFD notices the bfd link up + and that ipv6 entries appear back + """ + tgen = get_topogen() + logger.info("re-adding IPv6 address from r2 to simulate connectivity is back") + # adds back r2-eth0 ipv6 address + cmd = 'vtysh -c "configure terminal" -c "interface r2-eth1" -c "ipv6 address 2001:db8:4::2/64"' + tgen.net["r2"].cmd(cmd) + + # Wait the minimum time we can before checking that BGP/BFD + # converged. + logger.info("waiting for BFD to converge up") + + # Check that BGP converged quickly. + for router in tgen.routers().values(): + if router.name == "r2": + continue + json_file = "{}/{}/peers.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=16, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd_isis_topo1/__init__.py b/tests/topotests/bfd_isis_topo1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bfd_isis_topo1/rt1/bfdd.conf b/tests/topotests/bfd_isis_topo1/rt1/bfdd.conf new file mode 100644 index 0000000..dbcf23f --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt1/bfdd.conf @@ -0,0 +1,19 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 10.0.1.2 interface eth-rt2 + detect-multiplier 3 + receive-interval 300 + transmit-interval 300 + no shutdown + ! + peer 10.0.2.2 interface eth-rt3 + detect-multiplier 3 + receive-interval 300 + transmit-interval 300 + no shutdown + ! +! diff --git a/tests/topotests/bfd_isis_topo1/rt1/isisd.conf b/tests/topotests/bfd_isis_topo1/rt1/isisd.conf new file mode 100644 index 0000000..5ae236d --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt1/isisd.conf @@ -0,0 +1,36 @@ +log file isisd.log +log timestamp precision 3 +! +hostname rt1 +! +password 1 +! +! debug isis events +! debug isis route-events +! debug isis spf-events +! debug isis adj-packets +! debug isis lsp-sched +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt2 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 10 + isis bfd +! +interface eth-rt3 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 10 + isis network point-to-point + isis bfd +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.0001.00 + is-type level-1 +! diff --git a/tests/topotests/bfd_isis_topo1/rt1/step1/show_ip_route.ref b/tests/topotests/bfd_isis_topo1/rt1/step1/show_ip_route.ref new file mode 100644 index 0000000..af6e45c --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt1/step1/show_ip_route.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_isis_topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/bfd_isis_topo1/rt1/step1/show_ipv6_route.ref new file mode 100644 index 0000000..68d3fe2 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt1/step1/show_ipv6_route.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_isis_topo1/rt1/step2/show_bfd_peers.ref b/tests/topotests/bfd_isis_topo1/rt1/step2/show_bfd_peers.ref new file mode 100644 index 0000000..cb4083d --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt1/step2/show_bfd_peers.ref @@ -0,0 +1,16 @@ +[ + { + "peer": "10.0.2.2", + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "peer": "10.0.1.2", + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_healthy.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_healthy.ref new file mode 100644 index 0000000..cb4083d --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_healthy.ref @@ -0,0 +1,16 @@ +[ + { + "peer": "10.0.2.2", + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "peer": "10.0.1.2", + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt2_down.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt2_down.ref new file mode 100644 index 0000000..f00b9f3 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt2_down.ref @@ -0,0 +1,9 @@ +[ + { + "peer": "10.0.2.2", + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt3_down.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt3_down.ref new file mode 100644 index 0000000..f5bd276 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt3_down.ref @@ -0,0 +1,9 @@ +[ + { + "peer": "10.0.1.2", + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_healthy.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_healthy.ref new file mode 100644 index 0000000..af6e45c --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_healthy.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt2_down.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt2_down.ref new file mode 100644 index 0000000..b8366bc --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt2_down.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt3_down.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt3_down.ref new file mode 100644 index 0000000..42bd6ab --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt3_down.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_healthy.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_healthy.ref new file mode 100644 index 0000000..68d3fe2 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_healthy.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt2_down.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt2_down.ref new file mode 100644 index 0000000..200053c --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt2_down.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt3_down.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt3_down.ref new file mode 100644 index 0000000..4297f16 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt3_down.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_isis_topo1/rt1/zebra.conf b/tests/topotests/bfd_isis_topo1/rt1/zebra.conf new file mode 100644 index 0000000..7e6f788 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt1/zebra.conf @@ -0,0 +1,25 @@ +log file zebra.log +log timestamp precision 3 +! +hostname rt1 +! +! debug zebra kernel +! debug zebra packet +! debug zebra events +! debug zebra rib +! +interface lo + ip address 1.1.1.1/32 + ipv6 address ::ffff:0101:0101/128 +! +interface eth-rt2 + ip address 10.0.1.1/24 +! +interface eth-rt3 + ip address 10.0.2.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd_isis_topo1/rt2/bfdd.conf b/tests/topotests/bfd_isis_topo1/rt2/bfdd.conf new file mode 100644 index 0000000..d5054aa --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt2/bfdd.conf @@ -0,0 +1,13 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 10.0.1.1 interface eth-rt1 + detect-multiplier 3 + receive-interval 300 + transmit-interval 300 + no shutdown + ! +! diff --git a/tests/topotests/bfd_isis_topo1/rt2/isisd.conf b/tests/topotests/bfd_isis_topo1/rt2/isisd.conf new file mode 100644 index 0000000..ff99f18 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt2/isisd.conf @@ -0,0 +1,31 @@ +log file isisd.log +! +hostname rt2 +! +password 1 +! +! debug isis events +! debug isis route-events +! debug isis spf-events +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 10 + isis bfd +! +interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 10 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.0002.00 + is-type level-1 +! diff --git a/tests/topotests/bfd_isis_topo1/rt2/step2/show_bfd_peers.ref b/tests/topotests/bfd_isis_topo1/rt2/step2/show_bfd_peers.ref new file mode 100644 index 0000000..8a90649 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt2/step2/show_bfd_peers.ref @@ -0,0 +1,9 @@ +[ + { + "peer": "10.0.1.1", + "interface": "eth-rt1", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd_isis_topo1/rt2/zebra.conf b/tests/topotests/bfd_isis_topo1/rt2/zebra.conf new file mode 100644 index 0000000..5788e31 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt2/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt2 +! +! debug zebra kernel +! debug zebra packet +! +interface lo + ip address 2.2.2.2/32 + ipv6 address ::ffff:0202:0202/128 +! +interface eth-rt1 + ip address 10.0.1.2/24 +! +interface eth-rt5 + ip address 10.0.3.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd_isis_topo1/rt3/bfdd.conf b/tests/topotests/bfd_isis_topo1/rt3/bfdd.conf new file mode 100644 index 0000000..fd9a5e1 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt3/bfdd.conf @@ -0,0 +1,13 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 10.0.2.1 interface eth-rt1 + detect-multiplier 3 + receive-interval 300 + transmit-interval 300 + no shutdown + ! +! diff --git a/tests/topotests/bfd_isis_topo1/rt3/isisd.conf b/tests/topotests/bfd_isis_topo1/rt3/isisd.conf new file mode 100644 index 0000000..2ad1b9c --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt3/isisd.conf @@ -0,0 +1,33 @@ +log file isisd.log +! +hostname rt3 +! +password 1 +! +! debug isis events +! debug isis route-events +! debug isis spf-events +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 10 + isis network point-to-point + isis bfd +! +interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 10 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.0003.00 + is-type level-1 +! + diff --git a/tests/topotests/bfd_isis_topo1/rt3/step2/show_bfd_peers.ref b/tests/topotests/bfd_isis_topo1/rt3/step2/show_bfd_peers.ref new file mode 100644 index 0000000..13eb2a2 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt3/step2/show_bfd_peers.ref @@ -0,0 +1,9 @@ +[ + { + "peer": "10.0.2.1", + "interface": "eth-rt1", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd_isis_topo1/rt3/zebra.conf b/tests/topotests/bfd_isis_topo1/rt3/zebra.conf new file mode 100644 index 0000000..78eac2e --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt3/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt3 +! +! debug zebra kernel +! debug zebra packet +! +interface lo + ip address 3.3.3.3/32 + ipv6 address ::ffff:0303:0303/128 +! +interface eth-rt1 + ip address 10.0.2.2/24 +! +interface eth-rt4 + ip address 10.0.4.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd_isis_topo1/rt4/bfdd.conf b/tests/topotests/bfd_isis_topo1/rt4/bfdd.conf new file mode 100644 index 0000000..ee7144d --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt4/bfdd.conf @@ -0,0 +1,5 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! diff --git a/tests/topotests/bfd_isis_topo1/rt4/isisd.conf b/tests/topotests/bfd_isis_topo1/rt4/isisd.conf new file mode 100644 index 0000000..e170c19 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt4/isisd.conf @@ -0,0 +1,30 @@ +log file isisd.log +! +hostname rt4 +! +password 1 +! +! debug isis events +! debug isis route-events +! debug isis spf-events +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt3 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 10 +! +interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 10 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.0004.00 + is-type level-1 +! diff --git a/tests/topotests/bfd_isis_topo1/rt4/zebra.conf b/tests/topotests/bfd_isis_topo1/rt4/zebra.conf new file mode 100644 index 0000000..a6cb573 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt4/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt4 +! +! debug zebra kernel +! debug zebra packet +! +interface lo + ip address 4.4.4.4/32 + ipv6 address ::ffff:0404:0404/128 +! +interface eth-rt3 + ip address 10.0.4.2/24 +! +interface eth-rt5 + ip address 10.0.5.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd_isis_topo1/rt5/bfdd.conf b/tests/topotests/bfd_isis_topo1/rt5/bfdd.conf new file mode 100644 index 0000000..ee7144d --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt5/bfdd.conf @@ -0,0 +1,5 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! diff --git a/tests/topotests/bfd_isis_topo1/rt5/isisd.conf b/tests/topotests/bfd_isis_topo1/rt5/isisd.conf new file mode 100644 index 0000000..3caaca7 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt5/isisd.conf @@ -0,0 +1,30 @@ +log file isisd.log +! +hostname rt5 +! +password 1 +! +! debug isis events +! debug isis route-events +! debug isis spf-events +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt2 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 10 +! +interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 10 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.0005.00 + is-type level-1 +! diff --git a/tests/topotests/bfd_isis_topo1/rt5/zebra.conf b/tests/topotests/bfd_isis_topo1/rt5/zebra.conf new file mode 100644 index 0000000..33473c9 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/rt5/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt5 +! +! debug zebra kernel +! debug zebra packet +! +interface lo + ip address 5.5.5.5/32 + ipv6 address ::ffff:0505:0505/128 +! +interface eth-rt2 + ip address 10.0.3.2/24 +! +interface eth-rt4 + ip address 10.0.5.2/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd_isis_topo1/test_bfd_isis_topo1.py b/tests/topotests/bfd_isis_topo1/test_bfd_isis_topo1.py new file mode 100644 index 0000000..09e6914 --- /dev/null +++ b/tests/topotests/bfd_isis_topo1/test_bfd_isis_topo1.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bfd_isis_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_bfd_isis_topo1.py: + + +---------+ + | | + eth-rt2 (.1) | RT1 | eth-rt3 (.1) + +----------+ 1.1.1.1 +----------+ + | | | | + | +---------+ | + | | + | 10.0.2.0/24 | + | | + | eth-rt1 | (.2) + | 10.0.1.0/24 +----+----+ + | | | + | | RT3 | + | | 3.3.3.3 | + | | | + (.2) | eth-rt1 +----+----+ + +----+----+ eth-rt4 | (.1) + | | | + | RT2 | | + | 2.2.2.2 | 10.0.4.0/24 | + | | | + +----+----+ | + (.1) | eth-rt5 eth-rt3 | (.2) + | +----+----+ + | | | + | | RT4 | + | | 4.4.4.4 | + | | | + | +----+----+ + | 10.0.3.0/24 eth-rt5 | (.1) + | | + | | + | 10.0.5.0/24 | + | | + | +---------+ | + | | | | + +----------+ RT5 +----------+ + eth-rt2 (.2) | 5.5.5.5 | eth-rt4 (.2) + | | + +---------+ + +""" + +import os +import sys +import pytest +import json +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bfdd, pytest.mark.isisd] + + +def setup_module(mod): + "Sets up the pytest environment" + topodef = { + "s1": ("rt1:eth-rt2", "rt2:eth-rt1"), + "s2": ("rt1:eth-rt3", "rt3:eth-rt1"), + "s3": ("rt2:eth-rt5", "rt5:eth-rt2"), + "s4": ("rt3:eth-rt4", "rt4:eth-rt3"), + "s5": ("rt4:eth-rt5", "rt5:eth-rt4"), + } + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def print_cmd_result(rname, command): + print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False)) + + +def router_compare_json_output(rname, command, reference, count=120, wait=0.5): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = "{}/{}/{}".format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=count, wait=wait) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +## TEST STEPS + + +def test_rib_isis_step1(): + logger.info("Test (step 1): verify RIB (IPv4 and IPv6) for IS-IS") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router_compare_json_output( + "rt1", "show ip route isis json", "step1/show_ip_route.ref" + ) + router_compare_json_output( + "rt1", "show ipv6 route isis json", "step1/show_ipv6_route.ref" + ) + + +def test_bfd_isis_sessions_step2(): + logger.info("Test (step 2): verify BFD peers for IS-IS") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # BFD is just used on three routers + for rt in ["rt1", "rt2", "rt3"]: + router_compare_json_output( + rt, "show bfd peers json", "step2/show_bfd_peers.ref" + ) + + +def test_bfd_isis_interface_failure_rt2_step3(): + logger.info("Test (step 2): check failover handling when RT2 goes down") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Let's kill the interface on rt2 and see what happens with the RIB and BFD on rt1 + tgen.gears["rt2"].link_enable("eth-rt1", enabled=False) + + # By default BFD provides a recovery time of 900ms plus jitter, so let's wait + # initial 2 seconds to let the CI not suffer. + # TODO: add check for array size + router_compare_json_output( + "rt1", "show ip route isis json", "step3/show_ip_route_rt2_down.ref", 20, 1 + ) + router_compare_json_output( + "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_rt2_down.ref", 20, 1 + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_rt2_down.ref", 20, 1 + ) + + # Check recovery, this can take some time + tgen.gears["rt2"].link_enable("eth-rt1", enabled=True) + + router_compare_json_output( + "rt1", "show ip route isis json", "step3/show_ip_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref" + ) + + +def test_bfd_isis_interface_failure_rt3_step3(): + logger.info("Test (step 2): check failover handling when RT2 goes down") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Let's kill the interface on rt3 and see what happens with the RIB and BFD on rt1 + tgen.gears["rt3"].link_enable("eth-rt1", enabled=False) + + # By default BFD provides a recovery time of 900ms plus jitter, so let's wait + # initial 2 seconds to let the CI not suffer. + # TODO: add check for array size + router_compare_json_output( + "rt1", "show ip route isis json", "step3/show_ip_route_rt3_down.ref", 20, 1 + ) + router_compare_json_output( + "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_rt3_down.ref", 20, 1 + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_rt3_down.ref", 20, 1 + ) + + # Check recovery, this can take some time + tgen.gears["rt3"].link_enable("eth-rt1", enabled=True) + + router_compare_json_output( + "rt1", "show ip route isis json", "step3/show_ip_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref" + ) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd_ospf_topo1/__init__.py b/tests/topotests/bfd_ospf_topo1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bfd_ospf_topo1/rt1/bfdd.conf b/tests/topotests/bfd_ospf_topo1/rt1/bfdd.conf new file mode 100644 index 0000000..f34f4ca --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/bfdd.conf @@ -0,0 +1,9 @@ +log file bfdd.log +log timestamp precision 3 +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd +! diff --git a/tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf new file mode 100644 index 0000000..98da8c2 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf @@ -0,0 +1,25 @@ +log file ospf6d.log +log timestamp precision 3 +! +hostname rt1 +! +password 1 +! +interface eth-rt2 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 8 + ipv6 ospf6 network broadcast + ipv6 ospf6 bfd +! +interface eth-rt3 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 8 + ipv6 ospf6 network broadcast + ipv6 ospf6 bfd +! +router ospf6 + ospf6 router-id 1.1.1.1 + interface eth-rt2 area 0.0.0.0 + interface eth-rt3 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/bfd_ospf_topo1/rt1/ospfd.conf b/tests/topotests/bfd_ospf_topo1/rt1/ospfd.conf new file mode 100644 index 0000000..72238cc --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/ospfd.conf @@ -0,0 +1,31 @@ +log file ospfd.log +log timestamp precision 3 +! +hostname rt1 +! +password 1 +! +! debug ospf event +! debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 8 +! +interface eth-rt2 + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 8 + ip ospf bfd +! +interface eth-rt3 + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 8 + ip ospf bfd +! +router ospf + ospf router-id 1.1.1.1 + router-info area 0.0.0.0 +! diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ip_route.ref b/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ip_route.ref new file mode 100644 index 0000000..f354eff --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ip_route.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ipv6_route.ref new file mode 100644 index 0000000..6465efb --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ipv6_route.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step2/show_bfd_peers.ref b/tests/topotests/bfd_ospf_topo1/rt1/step2/show_bfd_peers.ref new file mode 100644 index 0000000..63f0d50 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/step2/show_bfd_peers.ref @@ -0,0 +1,26 @@ +[ + { + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_healthy.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_healthy.ref new file mode 100644 index 0000000..42051f9 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_healthy.ref @@ -0,0 +1,28 @@ +[ + { + "peer": "10.0.2.2", + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "peer": "10.0.1.2", + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt2_down.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt2_down.ref new file mode 100644 index 0000000..d844ee6 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt2_down.ref @@ -0,0 +1,15 @@ +[ + { + "peer": "10.0.2.2", + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt3_down.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt3_down.ref new file mode 100644 index 0000000..3279908 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt3_down.ref @@ -0,0 +1,15 @@ +[ + { + "peer": "10.0.1.2", + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_healthy.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_healthy.ref new file mode 100644 index 0000000..f354eff --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_healthy.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt2_down.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt2_down.ref new file mode 100644 index 0000000..43eecd0 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt2_down.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt3_down.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt3_down.ref new file mode 100644 index 0000000..409af63 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt3_down.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_healthy.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_healthy.ref new file mode 100644 index 0000000..6465efb --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_healthy.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt2_down.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt2_down.ref new file mode 100644 index 0000000..cfb1ef1 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt2_down.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt3_down.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt3_down.ref new file mode 100644 index 0000000..58b44da --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt3_down.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_ospf_topo1/rt1/zebra.conf b/tests/topotests/bfd_ospf_topo1/rt1/zebra.conf new file mode 100644 index 0000000..7e6f788 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt1/zebra.conf @@ -0,0 +1,25 @@ +log file zebra.log +log timestamp precision 3 +! +hostname rt1 +! +! debug zebra kernel +! debug zebra packet +! debug zebra events +! debug zebra rib +! +interface lo + ip address 1.1.1.1/32 + ipv6 address ::ffff:0101:0101/128 +! +interface eth-rt2 + ip address 10.0.1.1/24 +! +interface eth-rt3 + ip address 10.0.2.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd_ospf_topo1/rt2/bfdd.conf b/tests/topotests/bfd_ospf_topo1/rt2/bfdd.conf new file mode 100644 index 0000000..5baea3c --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt2/bfdd.conf @@ -0,0 +1,7 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd +! diff --git a/tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf new file mode 100644 index 0000000..34b0902 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf @@ -0,0 +1,23 @@ +log file ospf6d.log +! +hostname rt2 +! +password 1 +! +interface eth-rt1 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 8 + ipv6 ospf6 network broadcast + ipv6 ospf6 bfd +! +interface eth-rt5 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 8 + ipv6 ospf6 network broadcast +! +router ospf6 + ospf6 router-id 2.2.2.2 + interface eth-rt1 area 0.0.0.0 + interface eth-rt5 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/bfd_ospf_topo1/rt2/ospfd.conf b/tests/topotests/bfd_ospf_topo1/rt2/ospfd.conf new file mode 100644 index 0000000..c5f4262 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt2/ospfd.conf @@ -0,0 +1,29 @@ +log file ospfd.log +! +hostname rt2 +! +password 1 +! +! debug ospf event +! debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 8 +! +interface eth-rt1 + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 8 + ip ospf bfd +! +interface eth-rt5 + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 8 +! +router ospf + ospf router-id 2.2.2.2 + router-info area 0.0.0.0 +! diff --git a/tests/topotests/bfd_ospf_topo1/rt2/step2/show_bfd_peers.ref b/tests/topotests/bfd_ospf_topo1/rt2/step2/show_bfd_peers.ref new file mode 100644 index 0000000..d6df1eb --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt2/step2/show_bfd_peers.ref @@ -0,0 +1,14 @@ +[ + { + "interface": "eth-rt1", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt1", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd_ospf_topo1/rt2/zebra.conf b/tests/topotests/bfd_ospf_topo1/rt2/zebra.conf new file mode 100644 index 0000000..5788e31 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt2/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt2 +! +! debug zebra kernel +! debug zebra packet +! +interface lo + ip address 2.2.2.2/32 + ipv6 address ::ffff:0202:0202/128 +! +interface eth-rt1 + ip address 10.0.1.2/24 +! +interface eth-rt5 + ip address 10.0.3.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd_ospf_topo1/rt3/bfdd.conf b/tests/topotests/bfd_ospf_topo1/rt3/bfdd.conf new file mode 100644 index 0000000..5baea3c --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt3/bfdd.conf @@ -0,0 +1,7 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd +! diff --git a/tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf new file mode 100644 index 0000000..8ab4eee --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf @@ -0,0 +1,23 @@ +log file ospf6d.log +! +hostname rt3 +! +password 1 +! +interface eth-rt1 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 8 + ipv6 ospf6 network broadcast + ipv6 ospf6 bfd +! +interface eth-rt4 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 8 + ipv6 ospf6 network broadcast +! +router ospf6 + ospf6 router-id 3.3.3.3 + interface eth-rt1 area 0.0.0.0 + interface eth-rt4 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/bfd_ospf_topo1/rt3/ospfd.conf b/tests/topotests/bfd_ospf_topo1/rt3/ospfd.conf new file mode 100644 index 0000000..e487bdd --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt3/ospfd.conf @@ -0,0 +1,29 @@ +log file ospfd.log +! +hostname rt3 +! +password 1 +! +! debug ospf event +! debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 8 +! +interface eth-rt1 + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 8 + ip ospf bfd +! +interface eth-rt4 + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 8 +! +router ospf + ospf router-id 3.3.3.3 + router-info area 0.0.0.0 +! diff --git a/tests/topotests/bfd_ospf_topo1/rt3/step2/show_bfd_peers.ref b/tests/topotests/bfd_ospf_topo1/rt3/step2/show_bfd_peers.ref new file mode 100644 index 0000000..d6df1eb --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt3/step2/show_bfd_peers.ref @@ -0,0 +1,14 @@ +[ + { + "interface": "eth-rt1", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt1", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd_ospf_topo1/rt3/zebra.conf b/tests/topotests/bfd_ospf_topo1/rt3/zebra.conf new file mode 100644 index 0000000..78eac2e --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt3/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt3 +! +! debug zebra kernel +! debug zebra packet +! +interface lo + ip address 3.3.3.3/32 + ipv6 address ::ffff:0303:0303/128 +! +interface eth-rt1 + ip address 10.0.2.2/24 +! +interface eth-rt4 + ip address 10.0.4.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd_ospf_topo1/rt4/bfdd.conf b/tests/topotests/bfd_ospf_topo1/rt4/bfdd.conf new file mode 100644 index 0000000..ee7144d --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt4/bfdd.conf @@ -0,0 +1,5 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! diff --git a/tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf new file mode 100644 index 0000000..138b688 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf @@ -0,0 +1,22 @@ +log file ospf6d.log +! +hostname rt4 +! +password 1 +! +interface eth-rt3 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 8 + ipv6 ospf6 network broadcast +! +interface eth-rt5 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 8 + ipv6 ospf6 network broadcast +! +router ospf6 + ospf6 router-id 4.4.4.4 + interface eth-rt3 area 0.0.0.0 + interface eth-rt5 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/bfd_ospf_topo1/rt4/ospfd.conf b/tests/topotests/bfd_ospf_topo1/rt4/ospfd.conf new file mode 100644 index 0000000..560904e --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt4/ospfd.conf @@ -0,0 +1,28 @@ +log file ospfd.log +! +hostname rt4 +! +password 1 +! +! debug ospf event +! debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 8 +! +interface eth-rt3 + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 8 +! +interface eth-rt5 + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 8 +! +router ospf + ospf router-id 4.4.4.4 + router-info area 0.0.0.0 +! diff --git a/tests/topotests/bfd_ospf_topo1/rt4/zebra.conf b/tests/topotests/bfd_ospf_topo1/rt4/zebra.conf new file mode 100644 index 0000000..a6cb573 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt4/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt4 +! +! debug zebra kernel +! debug zebra packet +! +interface lo + ip address 4.4.4.4/32 + ipv6 address ::ffff:0404:0404/128 +! +interface eth-rt3 + ip address 10.0.4.2/24 +! +interface eth-rt5 + ip address 10.0.5.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd_ospf_topo1/rt5/bfdd.conf b/tests/topotests/bfd_ospf_topo1/rt5/bfdd.conf new file mode 100644 index 0000000..ee7144d --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt5/bfdd.conf @@ -0,0 +1,5 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! diff --git a/tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf new file mode 100644 index 0000000..6eb4fe5 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf @@ -0,0 +1,22 @@ +log file ospf6d.log +! +hostname rt5 +! +password 1 +! +interface eth-rt2 + ipv6 ospf6 network broadcast + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 8 +! +interface eth-rt4 + ipv6 ospf6 network broadcast + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 8 +! +router ospf6 + ospf6 router-id 5.5.5.5 + interface eth-rt2 area 0.0.0.0 + interface eth-rt4 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/bfd_ospf_topo1/rt5/ospfd.conf b/tests/topotests/bfd_ospf_topo1/rt5/ospfd.conf new file mode 100644 index 0000000..77f5445 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt5/ospfd.conf @@ -0,0 +1,28 @@ +log file ospfd.log +! +hostname rt5 +! +password 1 +! +! debug ospf event +! debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 8 +! +interface eth-rt2 + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 8 +! +interface eth-rt4 + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 8 +! +router ospf + ospf router-id 5.5.5.5 + router-info area 0.0.0.0 +! diff --git a/tests/topotests/bfd_ospf_topo1/rt5/zebra.conf b/tests/topotests/bfd_ospf_topo1/rt5/zebra.conf new file mode 100644 index 0000000..33473c9 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/rt5/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt5 +! +! debug zebra kernel +! debug zebra packet +! +interface lo + ip address 5.5.5.5/32 + ipv6 address ::ffff:0505:0505/128 +! +interface eth-rt2 + ip address 10.0.3.2/24 +! +interface eth-rt4 + ip address 10.0.5.2/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd_ospf_topo1/test_bfd_ospf_topo1.py b/tests/topotests/bfd_ospf_topo1/test_bfd_ospf_topo1.py new file mode 100755 index 0000000..b9e8b73 --- /dev/null +++ b/tests/topotests/bfd_ospf_topo1/test_bfd_ospf_topo1.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bfd_ospf_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_bfd_ospf_topo1.py: + + +---------+ + | | + eth-rt2 (.1) | RT1 | eth-rt3 (.1) + +----------+ 1.1.1.1 +----------+ + | | | | + | +---------+ | + | | + | 10.0.2.0/24 | + | | + | eth-rt1 | (.2) + | 10.0.1.0/24 +----+----+ + | | | + | | RT3 | + | | 3.3.3.3 | + | | | + (.2) | eth-rt1 +----+----+ + +----+----+ eth-rt4 | (.1) + | | | + | RT2 | | + | 2.2.2.2 | 10.0.4.0/24 | + | | | + +----+----+ | + (.1) | eth-rt5 eth-rt3 | (.2) + | +----+----+ + | | | + | | RT4 | + | | 4.4.4.4 | + | | | + | +----+----+ + | 10.0.3.0/24 eth-rt5 | (.1) + | | + | | + | 10.0.5.0/24 | + | | + | +---------+ | + | | | | + +----------+ RT5 +----------+ + eth-rt2 (.2) | 5.5.5.5 | eth-rt4 (.2) + | | + +---------+ + +""" + +import os +import sys +import pytest +import json +from time import sleep +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bfdd, pytest.mark.ospfd] + + +def setup_module(mod): + "Sets up the pytest environment" + topodef = { + "s1": ("rt1:eth-rt2", "rt2:eth-rt1"), + "s2": ("rt1:eth-rt3", "rt3:eth-rt1"), + "s3": ("rt2:eth-rt5", "rt5:eth-rt2"), + "s4": ("rt3:eth-rt4", "rt4:eth-rt3"), + "s5": ("rt4:eth-rt5", "rt5:eth-rt4"), + } + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def print_cmd_result(rname, command): + print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False)) + + +def router_compare_json_output(rname, command, reference, count=40, wait=2): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = "{}/{}/{}".format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + # Run test function until we get an result. Wait at most 80 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=count, wait=wait) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +## TEST STEPS + + +def test_rib_ospf_step1(): + logger.info("Test (step 1): verify RIB for OSPF") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router_compare_json_output( + "rt1", "show ip route ospf json", "step1/show_ip_route.ref" + ) + router_compare_json_output( + "rt1", "show ipv6 route ospf json", "step1/show_ipv6_route.ref" + ) + + +def test_bfd_ospf_sessions_step2(): + logger.info("Test (step 2): verify BFD peers for OSPF") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # BFD is just used on three routers + for rt in ["rt1", "rt2", "rt3"]: + router_compare_json_output( + rt, "show bfd peers json", "step2/show_bfd_peers.ref" + ) + + +def test_bfd_ospf_interface_failure_rt2_step3(): + logger.info("Test (step 3): Check failover handling with RT2 down") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Let's kill the interface on rt2 and see what happens with the RIB and BFD on rt1 + tgen.gears["rt2"].link_enable("eth-rt1", enabled=False) + + # By default BFD provides a recovery time of 900ms plus jitter, so let's wait + # initial 2 seconds to let the CI not suffer. + topotest.sleep(2, "Wait for BFD down notification") + + router_compare_json_output( + "rt1", "show ip route ospf json", "step3/show_ip_route_rt2_down.ref", 10, 2 + ) + router_compare_json_output( + "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_rt2_down.ref", 10, 2 + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_rt2_down.ref", 10, 2 + ) + + # Check recovery, this can take some time + tgen.gears["rt2"].link_enable("eth-rt1", enabled=True) + + router_compare_json_output( + "rt1", "show ip route ospf json", "step3/show_ip_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref" + ) + + +def test_bfd_ospf_interface_failure_rt3_step3(): + logger.info("Test (step 3): Check failover handling with RT3 down") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Let's kill the interface on rt3 and see what happens with the RIB and BFD on rt1 + tgen.gears["rt3"].link_enable("eth-rt1", enabled=False) + + # By default BFD provides a recovery time of 900ms plus jitter, so let's wait + # initial 2 seconds to let the CI not suffer. + topotest.sleep(2, "Wait for BFD down notification") + router_compare_json_output( + "rt1", "show ip route ospf json", "step3/show_ip_route_rt3_down.ref", 10, 2 + ) + router_compare_json_output( + "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_rt3_down.ref", 10, 2 + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_rt3_down.ref", 10, 2 + ) + + # Check recovery, this can take some time + tgen.gears["rt3"].link_enable("eth-rt1", enabled=True) + + router_compare_json_output( + "rt1", "show ip route ospf json", "step3/show_ip_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref" + ) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd_profiles_topo1/__init__.py b/tests/topotests/bfd_profiles_topo1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bfd_profiles_topo1/r1/bfd-peers-initial.json b/tests/topotests/bfd_profiles_topo1/r1/bfd-peers-initial.json new file mode 100644 index 0000000..86a7e51 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r1/bfd-peers-initial.json @@ -0,0 +1,38 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r1-eth1", + "multihop": false, + "peer": "172.16.100.2", + "receive-interval": 800, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 300, + "remote-transmit-interval": 300, + "status": "up", + "transmit-interval": 800, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r1-eth0", + "multihop": false, + "peer": "172.16.0.1", + "receive-interval": 800, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 800, + "remote-transmit-interval": 800, + "status": "up", + "transmit-interval": 800, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd_profiles_topo1/r1/bfdd.conf b/tests/topotests/bfd_profiles_topo1/r1/bfdd.conf new file mode 100644 index 0000000..c2ac9c1 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r1/bfdd.conf @@ -0,0 +1,15 @@ +! debug bfd peer +! debug bfd network +! debug bfd zebra +! +bfd + profile slowtx + receive-interval 800 + transmit-interval 800 + echo receive-interval 400 + ! + peer 172.16.0.1 interface r1-eth0 + profile slowtx + no shutdown + ! +! diff --git a/tests/topotests/bfd_profiles_topo1/r1/ospfd.conf b/tests/topotests/bfd_profiles_topo1/r1/ospfd.conf new file mode 100644 index 0000000..373a0c5 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r1/ospfd.conf @@ -0,0 +1,11 @@ +interface r1-eth1 + ip ospf area 0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf bfd + ip ospf bfd profile slowtx +! +router ospf + ospf router-id 10.254.254.1 + redistribute connected +! diff --git a/tests/topotests/bfd_profiles_topo1/r1/zebra.conf b/tests/topotests/bfd_profiles_topo1/r1/zebra.conf new file mode 100644 index 0000000..4b7982b --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r1/zebra.conf @@ -0,0 +1,9 @@ +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ip address 172.16.0.2/24 +! +interface r1-eth1 + ip address 172.16.100.1/24 +! diff --git a/tests/topotests/bfd_profiles_topo1/r2/bfd-peers-initial.json b/tests/topotests/bfd_profiles_topo1/r2/bfd-peers-initial.json new file mode 100644 index 0000000..503f776 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r2/bfd-peers-initial.json @@ -0,0 +1,40 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r2-eth0", + "multihop": false, + "peer": "172.16.0.2", + "receive-interval": 800, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 800, + "remote-transmit-interval": 800, + "remote-echo-receive-interval": 400, + "status": "up", + "transmit-interval": 800, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r2-eth1", + "multihop": false, + "peer": "172.16.1.1", + "receive-interval": 250, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 300, + "remote-transmit-interval": 300, + "status": "up", + "transmit-interval": 250, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd_profiles_topo1/r2/bfdd.conf b/tests/topotests/bfd_profiles_topo1/r2/bfdd.conf new file mode 100644 index 0000000..b68eecb --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r2/bfdd.conf @@ -0,0 +1,19 @@ +! debug bfd peer +! debug bfd network +! debug bfd zebra +! +bfd + profile slowtx + receive-interval 800 + transmit-interval 800 + ! + profile fasttx + receive-interval 250 + transmit-interval 250 + echo receive-interval disabled + ! + peer 172.16.0.2 interface r2-eth0 + profile slowtx + no shutdown + ! +! diff --git a/tests/topotests/bfd_profiles_topo1/r2/bgpd.conf b/tests/topotests/bfd_profiles_topo1/r2/bgpd.conf new file mode 100644 index 0000000..1aab1d1 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r2/bgpd.conf @@ -0,0 +1,21 @@ +! debug bgp neighbor-events +! +router bgp 100 + bgp router-id 10.254.254.2 + no bgp ebgp-requires-policy + neighbor 172.16.1.1 remote-as 100 + neighbor 172.16.1.1 timers 3 10 + neighbor 172.16.1.1 bfd profile fasttx + neighbor 2001:db8:2::2 remote-as 200 + neighbor 2001:db8:2::2 timers 3 10 + neighbor 2001:db8:2::2 ebgp-multihop 2 + neighbor 2001:db8:2::2 bfd profile slowtx + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 172.16.1.1 activate + neighbor 2001:db8:2::2 activate + exit-address-family +! diff --git a/tests/topotests/bfd_profiles_topo1/r2/zebra.conf b/tests/topotests/bfd_profiles_topo1/r2/zebra.conf new file mode 100644 index 0000000..6acef13 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r2/zebra.conf @@ -0,0 +1,10 @@ +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ip address 172.16.0.1/24 +! +interface r2-eth1 + ip address 172.16.1.2/24 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/bfd_profiles_topo1/r3/bfd-peers-initial.json b/tests/topotests/bfd_profiles_topo1/r3/bfd-peers-initial.json new file mode 100644 index 0000000..d987a0a --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r3/bfd-peers-initial.json @@ -0,0 +1,40 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r3-eth0", + "multihop": false, + "peer": "172.16.1.2", + "receive-interval": 300, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 250, + "remote-transmit-interval": 250, + "remote-echo-receive-interval": 0, + "status": "up", + "transmit-interval": 300, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r3-eth1", + "local": "*", + "multihop": false, + "peer": "*", + "receive-interval": 250, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 300, + "remote-transmit-interval": 300, + "status": "up", + "transmit-interval": 250, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd_profiles_topo1/r3/bfdd.conf b/tests/topotests/bfd_profiles_topo1/r3/bfdd.conf new file mode 100644 index 0000000..f3a86ed --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r3/bfdd.conf @@ -0,0 +1,10 @@ +! debug bfd peer +! debug bfd network +! debug bfd zebra +! +bfd + profile fasttx + receive-interval 250 + transmit-interval 250 + ! +! diff --git a/tests/topotests/bfd_profiles_topo1/r3/bgpd.conf b/tests/topotests/bfd_profiles_topo1/r3/bgpd.conf new file mode 100644 index 0000000..65647b3 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r3/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 100 + bgp router-id 10.254.254.3 + neighbor 172.16.1.2 remote-as 100 + neighbor 172.16.1.2 timers 3 10 + neighbor 172.16.1.2 bfd profile DOES_NOT_EXIST + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 172.16.1.2 activate + exit-address-family + ! +! diff --git a/tests/topotests/bfd_profiles_topo1/r3/isisd.conf b/tests/topotests/bfd_profiles_topo1/r3/isisd.conf new file mode 100644 index 0000000..3bba2b0 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r3/isisd.conf @@ -0,0 +1,17 @@ +hostname r3 +! +! debug isis adj-packets +! debug isis events +! debug isis update-packets +! +interface r3-eth1 + ipv6 router isis lan + isis circuit-type level-1 + isis bfd + isis bfd profile fasttx +! +router isis lan + lsp-gen-interval 2 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00 + redistribute ipv6 connected level-1 +! diff --git a/tests/topotests/bfd_profiles_topo1/r3/zebra.conf b/tests/topotests/bfd_profiles_topo1/r3/zebra.conf new file mode 100644 index 0000000..2297bfa --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r3/zebra.conf @@ -0,0 +1,12 @@ +ipv6 forwarding +! +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ip address 172.16.1.1/24 + ipv6 address 2001:db8:1::1/64 +! +interface r3-eth1 + ipv6 address 2001:db8:2::1/64 +! diff --git a/tests/topotests/bfd_profiles_topo1/r4/bfd-peers-initial.json b/tests/topotests/bfd_profiles_topo1/r4/bfd-peers-initial.json new file mode 100644 index 0000000..9ab7479 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r4/bfd-peers-initial.json @@ -0,0 +1,41 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r4-eth0", + "local": "*", + "multihop": false, + "peer": "*", + "receive-interval": 300, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 250, + "remote-transmit-interval": 250, + "status": "up", + "transmit-interval": 300, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r4-eth1", + "local": "*", + "multihop": false, + "peer": "*", + "receive-interval": 250, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 300, + "remote-transmit-interval": 300, + "status": "up", + "transmit-interval": 250, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd_profiles_topo1/r4/bfdd.conf b/tests/topotests/bfd_profiles_topo1/r4/bfdd.conf new file mode 100644 index 0000000..a5d1e25 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r4/bfdd.conf @@ -0,0 +1,10 @@ +! debug bfd peer +! debug bfd network +! debug bfd zebra +! +bfd + profile fast-tx + receive-interval 250 + transmit-interval 250 + ! +! diff --git a/tests/topotests/bfd_profiles_topo1/r4/bgpd.conf b/tests/topotests/bfd_profiles_topo1/r4/bgpd.conf new file mode 100644 index 0000000..12d6827 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r4/bgpd.conf @@ -0,0 +1,18 @@ +! debug bgp neighbor-events +! +router bgp 200 + bgp router-id 10.254.254.4 + no bgp ebgp-requires-policy + neighbor 2001:db8:1::2 remote-as 100 + neighbor 2001:db8:1::2 timers 3 10 + neighbor 2001:db8:1::2 ebgp-multihop 2 + neighbor 2001:db8:1::2 bfd profile DOES_NOT_EXIST + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::2 activate + exit-address-family + ! +! diff --git a/tests/topotests/bfd_profiles_topo1/r4/isisd.conf b/tests/topotests/bfd_profiles_topo1/r4/isisd.conf new file mode 100644 index 0000000..18009ce --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r4/isisd.conf @@ -0,0 +1,17 @@ +hostname r4 +! +! debug isis adj-packets +! debug isis events +! debug isis update-packets +! +interface r4-eth0 + ipv6 router isis lan + isis circuit-type level-1 + isis bfd + isis bfd profile DOES_NOT_EXIST +! +router isis lan + lsp-gen-interval 2 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00 + redistribute ipv6 connected level-1 +! diff --git a/tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf b/tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf new file mode 100644 index 0000000..4ef28c3 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf @@ -0,0 +1,10 @@ +interface r4-eth1 + ipv6 ospf6 bfd profile fast-tx + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +router ospf6 + ospf6 router-id 10.254.254.4 + redistribute connected + interface r4-eth1 area 0.0.0.0 +! diff --git a/tests/topotests/bfd_profiles_topo1/r4/zebra.conf b/tests/topotests/bfd_profiles_topo1/r4/zebra.conf new file mode 100644 index 0000000..753041f --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r4/zebra.conf @@ -0,0 +1,12 @@ +ipv6 forwarding +! +interface lo + ip address 10.254.254.4/32 +! +interface r4-eth0 + ipv6 address 2001:db8:2::2/64 +! +interface r4-eth1 + ip address 172.16.3.1/24 + ipv6 address 2001:db8:3::1/64 +! diff --git a/tests/topotests/bfd_profiles_topo1/r5/bfd-peers-initial.json b/tests/topotests/bfd_profiles_topo1/r5/bfd-peers-initial.json new file mode 100644 index 0000000..0fe56d5 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r5/bfd-peers-initial.json @@ -0,0 +1,21 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r5-eth0", + "local": "*", + "multihop": false, + "peer": "*", + "receive-interval": 300, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 250, + "remote-transmit-interval": 250, + "status": "up", + "transmit-interval": 300, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd_profiles_topo1/r5/bfdd.conf b/tests/topotests/bfd_profiles_topo1/r5/bfdd.conf new file mode 100644 index 0000000..670fd44 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r5/bfdd.conf @@ -0,0 +1,11 @@ +! debug bfd peer +! debug bfd network +! debug bfd zebra +! +bfd + ! profile is commented out on purpose. + !profile fasttx + ! receive-interval 250 + ! transmit-interval 250 + !! +! diff --git a/tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf b/tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf new file mode 100644 index 0000000..20b53cf --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf @@ -0,0 +1,10 @@ +interface r5-eth0 + ipv6 ospf6 bfd + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +router ospf6 + ospf6 router-id 10.254.254.5 + redistribute connected + interface r5-eth0 area 0.0.0.0 +! diff --git a/tests/topotests/bfd_profiles_topo1/r5/zebra.conf b/tests/topotests/bfd_profiles_topo1/r5/zebra.conf new file mode 100644 index 0000000..de8ae16 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r5/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.5/32 +! +interface r5-eth0 + ipv6 address 2001:db8:3::2/64 +! diff --git a/tests/topotests/bfd_profiles_topo1/r6/bfd-peers-initial.json b/tests/topotests/bfd_profiles_topo1/r6/bfd-peers-initial.json new file mode 100644 index 0000000..ec973eb --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r6/bfd-peers-initial.json @@ -0,0 +1,20 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r6-eth0", + "multihop": false, + "peer": "172.16.100.1", + "receive-interval": 300, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 800, + "remote-transmit-interval": 800, + "status": "up", + "transmit-interval": 300, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd_profiles_topo1/r6/bfdd.conf b/tests/topotests/bfd_profiles_topo1/r6/bfdd.conf new file mode 100644 index 0000000..670fd44 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r6/bfdd.conf @@ -0,0 +1,11 @@ +! debug bfd peer +! debug bfd network +! debug bfd zebra +! +bfd + ! profile is commented out on purpose. + !profile fasttx + ! receive-interval 250 + ! transmit-interval 250 + !! +! diff --git a/tests/topotests/bfd_profiles_topo1/r6/ospfd.conf b/tests/topotests/bfd_profiles_topo1/r6/ospfd.conf new file mode 100644 index 0000000..d8fce34 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r6/ospfd.conf @@ -0,0 +1,10 @@ +interface r6-eth0 + ip ospf area 0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf bfd +! +router ospf + ospf router-id 10.254.254.6 + redistribute connected +! diff --git a/tests/topotests/bfd_profiles_topo1/r6/zebra.conf b/tests/topotests/bfd_profiles_topo1/r6/zebra.conf new file mode 100644 index 0000000..c0804b9 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/r6/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.6/32 +! +interface r6-eth0 + ip address 172.16.100.2/24 +! diff --git a/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.dot b/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.dot new file mode 100644 index 0000000..a393609 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.dot @@ -0,0 +1,97 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-profiles-topo1"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + r5 [ + shape=doubleoctagon + label="r5", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n172.16.0.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n172.16.1.0/24\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n2001:db8:2::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw4 [ + shape=oval, + label="sw4\n2001:db8:3::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw5 [ + shape=oval, + label="sw5\n172.16.100.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + + r2 -- sw2 [label="eth1"]; + r3 -- sw2 [label="eth0"]; + + r3 -- sw3 [label="eth1"]; + r4 -- sw3 [label="eth0"]; + + r4 -- sw4 [label="eth1"]; + r5 -- sw4 [label="eth0"]; + + r1 -- sw5 [label="eth1"]; + r6 -- sw5 [label="eth0"]; +} diff --git a/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.png b/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.png new file mode 100644 index 0000000..775fae1 Binary files /dev/null and b/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.png differ diff --git a/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.py b/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.py new file mode 100644 index 0000000..78841b3 --- /dev/null +++ b/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bfd_profiles_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_bfd_profiles_topo1.py: Test the FRR BFD profile protocol integration. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd, pytest.mark.isisd, pytest.mark.ospfd] + + +def setup_module(mod): + "Sets up the pytest environment" + + topodef = { + "s1": ("r1", "r2"), + "s2": ("r2", "r3"), + "s3": ("r3", "r4"), + "s4": ("r4", "r5"), + "s5": ("r1", "r6"), + } + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + daemon_file = "{}/{}/bfdd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BFD, daemon_file) + + daemon_file = "{}/{}/bgpd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BGP, daemon_file) + + daemon_file = "{}/{}/isisd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_ISIS, daemon_file) + + daemon_file = "{}/{}/ospfd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_OSPF, daemon_file) + + daemon_file = "{}/{}/ospf6d.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_OSPF6, daemon_file) + + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_ZEBRA, daemon_file) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_wait_protocols_convergence(): + "Wait for all protocols to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + def expect_loopback_route(router, iptype, route, proto): + "Wait until route is present on RIB for protocol." + logger.info("waiting route {} in {}".format(route, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show {} route json".format(iptype), + {route: [{"protocol": proto}]}, + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" OSPF convergence failure'.format(router) + assert result is None, assertmsg + + # Wait for R1 <-> R6 convergence. + expect_loopback_route("r1", "ip", "10.254.254.6/32", "ospf") + + # Wait for R6 <-> R1 convergence. + expect_loopback_route("r6", "ip", "10.254.254.1/32", "ospf") + + # Wait for R2 <-> R3 convergence. + expect_loopback_route("r2", "ip", "10.254.254.3/32", "bgp") + + # Wait for R3 <-> R2 convergence. + expect_loopback_route("r3", "ip", "10.254.254.2/32", "bgp") + + # Wait for R3 <-> R4 convergence. + expect_loopback_route("r3", "ipv6", "2001:db8:3::/64", "isis") + + # Wait for R4 <-> R3 convergence. + expect_loopback_route("r4", "ipv6", "2001:db8:1::/64", "isis") + + # Wait for R4 <-> R5 convergence. + expect_loopback_route("r4", "ipv6", "2001:db8:3::/64", "ospf6") + + # Wait for R5 <-> R4 convergence. + expect_loopback_route("r5", "ipv6", "2001:db8:2::/64", "ospf6") + + +def test_bfd_profile_values(): + "Assert that the BFD peers can find themselves." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bfd peers to go up and checking profile values") + + for router in tgen.routers().values(): + json_file = "{}/{}/bfd-peers-initial.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=12, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd_topo1/__init__.py b/tests/topotests/bfd_topo1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bfd_topo1/r1/bfdd.conf b/tests/topotests/bfd_topo1/r1/bfdd.conf new file mode 100644 index 0000000..b9efbaf --- /dev/null +++ b/tests/topotests/bfd_topo1/r1/bfdd.conf @@ -0,0 +1,11 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 192.168.0.2 + echo-mode + no shutdown + ! +! diff --git a/tests/topotests/bfd_topo1/r1/bgp_prefixes.json b/tests/topotests/bfd_topo1/r1/bgp_prefixes.json new file mode 100644 index 0000000..1262f5e --- /dev/null +++ b/tests/topotests/bfd_topo1/r1/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.2/32": [ + { + "path": "102", + "prefix": "10.254.254.2", + "valid": true, + "peerId": "192.168.0.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.3/32": [ + { + "path": "102 103", + "prefix": "10.254.254.3", + "valid": true, + "peerId": "192.168.0.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.4/32": [ + { + "path": "102 104", + "prefix": "10.254.254.4", + "valid": true, + "peerId": "192.168.0.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.2", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd_topo1/r1/bgp_summary.json b/tests/topotests/bfd_topo1/r1/bgp_summary.json new file mode 100644 index 0000000..fa07d60 --- /dev/null +++ b/tests/topotests/bfd_topo1/r1/bgp_summary.json @@ -0,0 +1,11 @@ +{ + "ipv4Unicast": { + "as": 101, + "peers": { + "192.168.0.2": { + "remoteAs": 102, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd_topo1/r1/bgpd.conf b/tests/topotests/bfd_topo1/r1/bgpd.conf new file mode 100644 index 0000000..57bde1f --- /dev/null +++ b/tests/topotests/bfd_topo1/r1/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 101 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.0.2 remote-as 102 + neighbor 192.168.0.2 timers 3 10 + neighbor 192.168.0.2 bfd + address-family ipv4 unicast + network 10.254.254.1/32 + exit-address-family +! diff --git a/tests/topotests/bfd_topo1/r1/peers.json b/tests/topotests/bfd_topo1/r1/peers.json new file mode 100644 index 0000000..f49768f --- /dev/null +++ b/tests/topotests/bfd_topo1/r1/peers.json @@ -0,0 +1,8 @@ +[ + { + "remote-receive-interval": 1000, + "remote-transmit-interval": 500, + "peer": "192.168.0.2", + "status": "up" + } +] diff --git a/tests/topotests/bfd_topo1/r1/zebra.conf b/tests/topotests/bfd_topo1/r1/zebra.conf new file mode 100644 index 0000000..a14cd7a --- /dev/null +++ b/tests/topotests/bfd_topo1/r1/zebra.conf @@ -0,0 +1,3 @@ +interface r1-eth0 + ip address 192.168.0.1/24 +! diff --git a/tests/topotests/bfd_topo1/r2/bfdd.conf b/tests/topotests/bfd_topo1/r2/bfdd.conf new file mode 100644 index 0000000..0d1e17e --- /dev/null +++ b/tests/topotests/bfd_topo1/r2/bfdd.conf @@ -0,0 +1,17 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 192.168.0.1 + receive-interval 1000 + transmit-interval 500 + echo-mode + no shutdown + ! + peer 192.168.1.1 + echo-mode + no shutdown + ! +! diff --git a/tests/topotests/bfd_topo1/r2/bgp_prefixes.json b/tests/topotests/bfd_topo1/r2/bgp_prefixes.json new file mode 100644 index 0000000..0d47c0f --- /dev/null +++ b/tests/topotests/bfd_topo1/r2/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.1/32": [ + { + "path": "101", + "prefix": "10.254.254.1", + "valid": true, + "peerId": "192.168.0.1", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.1", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.3/32": [ + { + "path": "103", + "prefix": "10.254.254.3", + "valid": true, + "peerId": "192.168.1.1", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.1", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.4/32": [ + { + "path": "104", + "prefix": "10.254.254.4", + "valid": true, + "peerId": "192.168.2.1", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.1", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd_topo1/r2/bgp_summary.json b/tests/topotests/bfd_topo1/r2/bgp_summary.json new file mode 100644 index 0000000..c0ef11a --- /dev/null +++ b/tests/topotests/bfd_topo1/r2/bgp_summary.json @@ -0,0 +1,19 @@ +{ + "ipv4Unicast": { + "as": 102, + "peers": { + "192.168.0.1": { + "remoteAs": 101, + "state": "Established" + }, + "192.168.1.1": { + "remoteAs": 103, + "state": "Established" + }, + "192.168.2.1": { + "remoteAs": 104, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd_topo1/r2/bgpd.conf b/tests/topotests/bfd_topo1/r2/bgpd.conf new file mode 100644 index 0000000..50d75ab --- /dev/null +++ b/tests/topotests/bfd_topo1/r2/bgpd.conf @@ -0,0 +1,16 @@ +router bgp 102 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.0.1 remote-as 101 + neighbor 192.168.0.1 timers 3 10 + neighbor 192.168.0.1 bfd + neighbor 192.168.1.1 remote-as 103 + neighbor 192.168.1.1 timers 3 10 + neighbor 192.168.1.1 bfd + neighbor 192.168.2.1 remote-as 104 + neighbor 192.168.2.1 timers 3 10 + neighbor 192.168.2.1 bfd + address-family ipv4 unicast + network 10.254.254.2/32 + exit-address-family +! diff --git a/tests/topotests/bfd_topo1/r2/peers.json b/tests/topotests/bfd_topo1/r2/peers.json new file mode 100644 index 0000000..267459c --- /dev/null +++ b/tests/topotests/bfd_topo1/r2/peers.json @@ -0,0 +1,17 @@ +[ + { + "peer": "192.168.0.1", + "status": "up" + }, + { + "remote-echo-receive-interval": 100, + "peer": "192.168.1.1", + "status": "up" + }, + { + "remote-transmit-interval": 2000, + "remote-receive-interval": 2000, + "peer": "192.168.2.1", + "status": "up" + } +] diff --git a/tests/topotests/bfd_topo1/r2/zebra.conf b/tests/topotests/bfd_topo1/r2/zebra.conf new file mode 100644 index 0000000..568abe7 --- /dev/null +++ b/tests/topotests/bfd_topo1/r2/zebra.conf @@ -0,0 +1,9 @@ +interface r2-eth0 + ip address 192.168.0.2/24 +! +interface r2-eth1 + ip address 192.168.1.2/24 +! +interface r2-eth2 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/bfd_topo1/r3/bfdd.conf b/tests/topotests/bfd_topo1/r3/bfdd.conf new file mode 100644 index 0000000..e091a1c --- /dev/null +++ b/tests/topotests/bfd_topo1/r3/bfdd.conf @@ -0,0 +1,12 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 192.168.1.2 + echo-interval 100 + echo-mode + no shutdown + ! +! diff --git a/tests/topotests/bfd_topo1/r3/bgp_prefixes.json b/tests/topotests/bfd_topo1/r3/bgp_prefixes.json new file mode 100644 index 0000000..36fca17 --- /dev/null +++ b/tests/topotests/bfd_topo1/r3/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.1/32": [ + { + "path": "102 101", + "prefix": "10.254.254.1", + "valid": true, + "peerId": "192.168.1.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.2/32": [ + { + "path": "102", + "prefix": "10.254.254.2", + "valid": true, + "peerId": "192.168.1.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.4/32": [ + { + "path": "102 104", + "prefix": "10.254.254.4", + "valid": true, + "peerId": "192.168.1.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.2", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd_topo1/r3/bgp_summary.json b/tests/topotests/bfd_topo1/r3/bgp_summary.json new file mode 100644 index 0000000..d478333 --- /dev/null +++ b/tests/topotests/bfd_topo1/r3/bgp_summary.json @@ -0,0 +1,11 @@ +{ + "ipv4Unicast": { + "as": 103, + "peers": { + "192.168.1.2": { + "remoteAs": 102, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd_topo1/r3/bgpd.conf b/tests/topotests/bfd_topo1/r3/bgpd.conf new file mode 100644 index 0000000..ce6055d --- /dev/null +++ b/tests/topotests/bfd_topo1/r3/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 103 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as 102 + neighbor 192.168.1.2 timers 3 10 + neighbor 192.168.1.2 bfd + address-family ipv4 unicast + network 10.254.254.3/32 + exit-address-family +! diff --git a/tests/topotests/bfd_topo1/r3/peers.json b/tests/topotests/bfd_topo1/r3/peers.json new file mode 100644 index 0000000..ef38008 --- /dev/null +++ b/tests/topotests/bfd_topo1/r3/peers.json @@ -0,0 +1,6 @@ +[ + { + "peer": "192.168.1.2", + "status": "up" + } +] diff --git a/tests/topotests/bfd_topo1/r3/zebra.conf b/tests/topotests/bfd_topo1/r3/zebra.conf new file mode 100644 index 0000000..b4fd80f --- /dev/null +++ b/tests/topotests/bfd_topo1/r3/zebra.conf @@ -0,0 +1,3 @@ +interface r3-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bfd_topo1/r4/bfdd.conf b/tests/topotests/bfd_topo1/r4/bfdd.conf new file mode 100644 index 0000000..63dd738 --- /dev/null +++ b/tests/topotests/bfd_topo1/r4/bfdd.conf @@ -0,0 +1,12 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 192.168.2.2 + transmit-interval 2000 + receive-interval 2000 + no shutdown + ! +! diff --git a/tests/topotests/bfd_topo1/r4/bgp_prefixes.json b/tests/topotests/bfd_topo1/r4/bgp_prefixes.json new file mode 100644 index 0000000..efe7d47 --- /dev/null +++ b/tests/topotests/bfd_topo1/r4/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.1/32": [ + { + "path": "102 101", + "prefix": "10.254.254.1", + "valid": true, + "peerId": "192.168.2.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.2/32": [ + { + "path": "102", + "prefix": "10.254.254.2", + "valid": true, + "peerId": "192.168.2.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.3/32": [ + { + "path": "102 103", + "prefix": "10.254.254.3", + "valid": true, + "peerId": "192.168.2.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.2", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd_topo1/r4/bgp_summary.json b/tests/topotests/bfd_topo1/r4/bgp_summary.json new file mode 100644 index 0000000..7d81784 --- /dev/null +++ b/tests/topotests/bfd_topo1/r4/bgp_summary.json @@ -0,0 +1,11 @@ +{ + "ipv4Unicast": { + "as": 104, + "peers": { + "192.168.2.2": { + "remoteAs": 102, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd_topo1/r4/bgpd.conf b/tests/topotests/bfd_topo1/r4/bgpd.conf new file mode 100644 index 0000000..0d032b4 --- /dev/null +++ b/tests/topotests/bfd_topo1/r4/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 104 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.2.2 remote-as 102 + neighbor 192.168.2.2 timers 3 10 + neighbor 192.168.2.2 bfd + address-family ipv4 unicast + network 10.254.254.4/32 + exit-address-family +! diff --git a/tests/topotests/bfd_topo1/r4/peers.json b/tests/topotests/bfd_topo1/r4/peers.json new file mode 100644 index 0000000..3714008 --- /dev/null +++ b/tests/topotests/bfd_topo1/r4/peers.json @@ -0,0 +1,6 @@ +[ + { + "peer": "192.168.2.2", + "status": "up" + } +] diff --git a/tests/topotests/bfd_topo1/r4/zebra.conf b/tests/topotests/bfd_topo1/r4/zebra.conf new file mode 100644 index 0000000..afdd44b --- /dev/null +++ b/tests/topotests/bfd_topo1/r4/zebra.conf @@ -0,0 +1,3 @@ +interface r4-eth0 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/bfd_topo1/test_bfd_topo1.dot b/tests/topotests/bfd_topo1/test_bfd_topo1.dot new file mode 100644 index 0000000..c84ace2 --- /dev/null +++ b/tests/topotests/bfd_topo1/test_bfd_topo1.dot @@ -0,0 +1,73 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo1"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n192.168.0.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n192.168.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n192.168.2.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0\n.1"]; + r2 -- sw1 [label="eth0\n.2"]; + + r3 -- sw2 [label="eth0\n.1"]; + r2 -- sw2 [label="eth1\n.2"]; + + r4 -- sw3 [label="eth0\n.1"]; + r2 -- sw3 [label="eth2\n.2"]; +} diff --git a/tests/topotests/bfd_topo1/test_bfd_topo1.jpg b/tests/topotests/bfd_topo1/test_bfd_topo1.jpg new file mode 100644 index 0000000..4d6d56e Binary files /dev/null and b/tests/topotests/bfd_topo1/test_bfd_topo1.jpg differ diff --git a/tests/topotests/bfd_topo1/test_bfd_topo1.py b/tests/topotests/bfd_topo1/test_bfd_topo1.py new file mode 100644 index 0000000..1b77493 --- /dev/null +++ b/tests/topotests/bfd_topo1/test_bfd_topo1.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bfd_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2018 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_bfd_topo1.py: Test the FRR BFD daemon. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd] + + +def setup_module(mod): + "Sets up the pytest environment" + topodef = { + "s1": ("r1", "r2"), + "s2": ("r2", "r3"), + "s3": ("r2", "r4"), + } + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + # Verify that we are using the proper version and that the BFD + # daemon exists. + for router in router_list.values(): + # Check for Version + if router.has_version("<", "5.1"): + tgen.set_error("Unsupported FRR version") + break + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_bfd_connection(): + "Assert that the BFD peers can find themselves." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bfd peers to go up") + + for router in tgen.routers().values(): + json_file = "{}/{}/peers.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_bgp_convergence(): + "Assert that BGP is converging." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bgp peers to go up") + + for router in tgen.routers().values(): + ref_file = "{}/{}/bgp_summary.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=125, wait=1.0) + assertmsg = "{}: bgp did not converge".format(router.name) + assert res is None, assertmsg + + +def test_bgp_fast_convergence(): + "Assert that BGP is converging before setting a link down." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bgp peers converge") + + for router in tgen.routers().values(): + ref_file = "{}/{}/bgp_prefixes.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=40, wait=0.5) + assertmsg = "{}: bgp did not converge".format(router.name) + assert res is None, assertmsg + + +def test_bfd_fast_convergence(): + """ + Assert that BFD notices the link down after simulating network + failure. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Disable r1-eth0 link. + tgen.gears["r1"].link_enable("r1-eth0", enabled=False) + + # Wait the minimum time we can before checking that BGP/BFD + # converged. + logger.info("waiting for BFD converge") + + # Check that BGP converged quickly. + for router in tgen.routers().values(): + json_file = "{}/{}/peers.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + # Load the same file as previous test, but expect R1 to be down. + if router.name == "r1": + for peer in expected: + if peer["peer"] == "192.168.0.2": + peer["status"] = "down" + else: + for peer in expected: + if peer["peer"] == "192.168.0.1": + peer["status"] = "down" + + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=20, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert res is None, assertmsg + + +def test_bgp_fast_reconvergence(): + "Assert that BGP is converging after setting a link down." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for BGP re convergence") + + # Check that BGP converged quickly. + for router in tgen.routers().values(): + ref_file = "{}/{}/bgp_prefixes.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + + # Load the same file as previous test, but set networks to None + # to test absence. + if router.name == "r1": + expected["routes"]["10.254.254.2/32"] = None + expected["routes"]["10.254.254.3/32"] = None + expected["routes"]["10.254.254.4/32"] = None + else: + expected["routes"]["10.254.254.1/32"] = None + + test_func = partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=5, wait=1) + assertmsg = "{}: bgp did not converge".format(router.name) + assert res is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd_topo2/__init__.py b/tests/topotests/bfd_topo2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bfd_topo2/r1/bfdd.conf b/tests/topotests/bfd_topo2/r1/bfdd.conf new file mode 100644 index 0000000..df8baeb --- /dev/null +++ b/tests/topotests/bfd_topo2/r1/bfdd.conf @@ -0,0 +1,10 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 2001:db8:4::1 multihop local-address 2001:db8:1::1 + no shutdown + ! +! diff --git a/tests/topotests/bfd_topo2/r1/bgpd.conf b/tests/topotests/bfd_topo2/r1/bgpd.conf new file mode 100644 index 0000000..0918796 --- /dev/null +++ b/tests/topotests/bfd_topo2/r1/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 101 + bgp router-id 10.254.254.1 + no bgp ebgp-requires-policy + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r1-eth0 interface peer-group r2g + neighbor r1-eth0 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bfd_topo2/r1/ipv4_routes.json b/tests/topotests/bfd_topo2/r1/ipv4_routes.json new file mode 100644 index 0000000..650c0a8 --- /dev/null +++ b/tests/topotests/bfd_topo2/r1/ipv4_routes.json @@ -0,0 +1,59 @@ +{ + "10.0.3.0/24": [ + { + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "installed": true, + "prefix": "10.0.3.0/24", + "nexthops": [ + { + "interfaceName": "r1-eth0", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ], + "10.254.254.2/32": [ + { + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "installed": true, + "prefix": "10.254.254.2/32", + "nexthops": [ + { + "interfaceName": "r1-eth0", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ], + "10.254.254.1/32": [ + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "installed": true, + "prefix": "10.254.254.1/32", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "lo", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_topo2/r1/ipv6_routes.json b/tests/topotests/bfd_topo2/r1/ipv6_routes.json new file mode 100644 index 0000000..50c1f9a --- /dev/null +++ b/tests/topotests/bfd_topo2/r1/ipv6_routes.json @@ -0,0 +1,53 @@ +{ + "2001:db8:4::/64": [ + { + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "installed": true, + "prefix": "2001:db8:4::/64", + "nexthops": [ + { + "interfaceName": "r1-eth0", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ], + "2001:db8:1::/64": [ + { + "distance": 20, + "protocol": "bgp", + "metric": 0, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "interfaceName": "r1-eth0", + "active": true, + "afi": "ipv6" + } + ] + }, + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "installed": true, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r1-eth0", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_topo2/r1/peers.json b/tests/topotests/bfd_topo2/r1/peers.json new file mode 100644 index 0000000..9bce991 --- /dev/null +++ b/tests/topotests/bfd_topo2/r1/peers.json @@ -0,0 +1,31 @@ +[ + { + "multihop":true, + "peer":"2001:db8:4::1", + "local":"2001:db8:1::1", + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":300, + "transmit-interval":300, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "remote-receive-interval":300, + "remote-transmit-interval":300, + "remote-echo-receive-interval":50 + }, + { + "multihop":false, + "interface":"r1-eth0", + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":300, + "transmit-interval":300, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "remote-receive-interval":300, + "remote-transmit-interval":300, + "remote-echo-receive-interval":50 + } +] diff --git a/tests/topotests/bfd_topo2/r1/zebra.conf b/tests/topotests/bfd_topo2/r1/zebra.conf new file mode 100644 index 0000000..7fe5eb2 --- /dev/null +++ b/tests/topotests/bfd_topo2/r1/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bfd_topo2/r2/bfdd.conf b/tests/topotests/bfd_topo2/r2/bfdd.conf new file mode 100644 index 0000000..ee7144d --- /dev/null +++ b/tests/topotests/bfd_topo2/r2/bfdd.conf @@ -0,0 +1,5 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! diff --git a/tests/topotests/bfd_topo2/r2/bgpd.conf b/tests/topotests/bfd_topo2/r2/bgpd.conf new file mode 100644 index 0000000..55d4856 --- /dev/null +++ b/tests/topotests/bfd_topo2/r2/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 102 + bgp router-id 10.254.254.2 + no bgp ebgp-requires-policy + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r2-eth0 interface peer-group r2g + neighbor r2-eth0 timers 3 10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bfd_topo2/r2/ipv4_routes.json b/tests/topotests/bfd_topo2/r2/ipv4_routes.json new file mode 100644 index 0000000..3d49b17 --- /dev/null +++ b/tests/topotests/bfd_topo2/r2/ipv4_routes.json @@ -0,0 +1,92 @@ +{ + "10.0.3.0/24": [ + { + "distance": 110, + "protocol": "ospf", + "metric": 10, + "prefix": "10.0.3.0/24", + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "interfaceName": "r2-eth1" + } + ] + }, + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "installed": true, + "prefix": "10.0.3.0/24", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r2-eth1", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "10.254.254.3/32": [ + { + "distance": 110, + "protocol": "ospf", + "metric": 20, + "selected": true, + "installed": true, + "prefix": "10.254.254.3/32", + "nexthops": [ + { + "interfaceName": "r2-eth1", + "ip": "10.0.3.1", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.2/32": [ + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "installed": true, + "prefix": "10.254.254.2/32", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "lo", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "installed": true, + "prefix": "10.254.254.1/32", + "nexthops": [ + { + "interfaceName": "r2-eth0", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ] +} diff --git a/tests/topotests/bfd_topo2/r2/ipv6_routes.json b/tests/topotests/bfd_topo2/r2/ipv6_routes.json new file mode 100644 index 0000000..4f3c74c --- /dev/null +++ b/tests/topotests/bfd_topo2/r2/ipv6_routes.json @@ -0,0 +1,53 @@ +{ + "2001:db8:4::/64": [ + { + "distance": 110, + "protocol": "ospf6", + "metric": 10, + "prefix": "2001:db8:4::/64", + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "interfaceName": "r2-eth2" + } + ] + }, + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "installed": true, + "prefix": "2001:db8:4::/64", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r2-eth2", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "2001:db8:1::/64": [ + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "installed": true, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r2-eth0", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_topo2/r2/ospf6d.conf b/tests/topotests/bfd_topo2/r2/ospf6d.conf new file mode 100644 index 0000000..524e2c9 --- /dev/null +++ b/tests/topotests/bfd_topo2/r2/ospf6d.conf @@ -0,0 +1,11 @@ +interface r2-eth2 + ipv6 ospf6 area 0.0.0.1 + ipv6 ospf6 bfd + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +router ospf6 + ospf6 router-id 10.254.254.2 + redistribute connected + redistribute bgp +! diff --git a/tests/topotests/bfd_topo2/r2/ospfd.conf b/tests/topotests/bfd_topo2/r2/ospfd.conf new file mode 100644 index 0000000..c786f1f --- /dev/null +++ b/tests/topotests/bfd_topo2/r2/ospfd.conf @@ -0,0 +1,11 @@ +interface r2-eth1 + ip ospf area 0.0.0.1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf bfd +! +router ospf + ospf router-id 10.254.254.2 + redistribute connected + redistribute bgp +! diff --git a/tests/topotests/bfd_topo2/r2/peers.json b/tests/topotests/bfd_topo2/r2/peers.json new file mode 100644 index 0000000..ec2135c --- /dev/null +++ b/tests/topotests/bfd_topo2/r2/peers.json @@ -0,0 +1,45 @@ +[ + { + "status": "up", + "transmit-interval": 300, + "remote-receive-interval": 300, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "diagnostic": "ok", + "multihop": false, + "interface": "r2-eth0", + "remote-transmit-interval": 300, + "receive-interval": 300, + "remote-echo-receive-interval": 50, + "remote-diagnostic": "ok" + }, + { + "status": "up", + "transmit-interval": 300, + "remote-receive-interval": 300, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "diagnostic": "ok", + "multihop": false, + "interface": "r2-eth2", + "remote-transmit-interval": 300, + "receive-interval": 300, + "remote-echo-receive-interval": 50, + "remote-diagnostic": "ok" + }, + { + "status": "up", + "transmit-interval": 300, + "remote-receive-interval": 300, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "diagnostic": "ok", + "multihop": false, + "interface": "r2-eth1", + "remote-transmit-interval": 300, + "receive-interval": 300, + "remote-echo-receive-interval": 50, + "remote-diagnostic": "ok", + "peer": "10.0.3.1" + } +] diff --git a/tests/topotests/bfd_topo2/r2/zebra.conf b/tests/topotests/bfd_topo2/r2/zebra.conf new file mode 100644 index 0000000..cccbf65 --- /dev/null +++ b/tests/topotests/bfd_topo2/r2/zebra.conf @@ -0,0 +1,15 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ipv6 address 2001:db8:1::2/64 +! +interface r2-eth1 + ip address 10.0.3.2/24 +! +interface r2-eth2 + ipv6 address 2001:db8:4::2/64 +! diff --git a/tests/topotests/bfd_topo2/r3/bfdd.conf b/tests/topotests/bfd_topo2/r3/bfdd.conf new file mode 100644 index 0000000..ee7144d --- /dev/null +++ b/tests/topotests/bfd_topo2/r3/bfdd.conf @@ -0,0 +1,5 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! diff --git a/tests/topotests/bfd_topo2/r3/ipv4_routes.json b/tests/topotests/bfd_topo2/r3/ipv4_routes.json new file mode 100644 index 0000000..e96fdc0 --- /dev/null +++ b/tests/topotests/bfd_topo2/r3/ipv4_routes.json @@ -0,0 +1,93 @@ +{ + "10.0.3.0/24": [ + { + "distance": 110, + "protocol": "ospf", + "metric": 10, + "prefix": "10.0.3.0/24", + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "interfaceName": "r3-eth0" + } + ] + }, + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "installed": true, + "prefix": "10.0.3.0/24", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r3-eth0", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "10.254.254.3/32": [ + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "installed": true, + "prefix": "10.254.254.3/32", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "lo", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "10.254.254.2/32": [ + { + "distance": 110, + "protocol": "ospf", + "metric": 20, + "selected": true, + "installed": true, + "prefix": "10.254.254.2/32", + "nexthops": [ + { + "interfaceName": "r3-eth0", + "ip": "10.0.3.2", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.1/32": [ + { + "distance": 110, + "protocol": "ospf", + "metric": 20, + "selected": true, + "installed": true, + "prefix": "10.254.254.1/32", + "nexthops": [ + { + "interfaceName": "r3-eth0", + "ip": "10.0.3.2", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv4" + } + ] + } + ] +} diff --git a/tests/topotests/bfd_topo2/r3/ipv6_routes.json b/tests/topotests/bfd_topo2/r3/ipv6_routes.json new file mode 100644 index 0000000..2c63c08 --- /dev/null +++ b/tests/topotests/bfd_topo2/r3/ipv6_routes.json @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/bfd_topo2/r3/ospfd.conf b/tests/topotests/bfd_topo2/r3/ospfd.conf new file mode 100644 index 0000000..932ab4d --- /dev/null +++ b/tests/topotests/bfd_topo2/r3/ospfd.conf @@ -0,0 +1,10 @@ +interface r3-eth0 + ip ospf area 0.0.0.1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf bfd +! +router ospf + ospf router-id 10.254.254.3 + redistribute connected +! diff --git a/tests/topotests/bfd_topo2/r3/peers.json b/tests/topotests/bfd_topo2/r3/peers.json new file mode 100644 index 0000000..c19c980 --- /dev/null +++ b/tests/topotests/bfd_topo2/r3/peers.json @@ -0,0 +1,17 @@ +[ + { + "status": "up", + "transmit-interval": 300, + "remote-receive-interval": 300, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "diagnostic": "ok", + "multihop": false, + "interface": "r3-eth0", + "remote-transmit-interval": 300, + "receive-interval": 300, + "remote-echo-receive-interval": 50, + "remote-diagnostic": "ok", + "peer": "10.0.3.2" + } +] diff --git a/tests/topotests/bfd_topo2/r3/zebra.conf b/tests/topotests/bfd_topo2/r3/zebra.conf new file mode 100644 index 0000000..96fd08c --- /dev/null +++ b/tests/topotests/bfd_topo2/r3/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ip address 10.0.3.1/24 +! diff --git a/tests/topotests/bfd_topo2/r4/bfdd.conf b/tests/topotests/bfd_topo2/r4/bfdd.conf new file mode 100644 index 0000000..c1e8d28 --- /dev/null +++ b/tests/topotests/bfd_topo2/r4/bfdd.conf @@ -0,0 +1,10 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 2001:db8:1::1 multihop local-address 2001:db8:4::1 + no shutdown + ! +! diff --git a/tests/topotests/bfd_topo2/r4/ipv4_routes.json b/tests/topotests/bfd_topo2/r4/ipv4_routes.json new file mode 100644 index 0000000..dc394aa --- /dev/null +++ b/tests/topotests/bfd_topo2/r4/ipv4_routes.json @@ -0,0 +1,21 @@ +{ + "10.254.254.4/32": [ + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "installed": true, + "prefix": "10.254.254.4/32", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "lo", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bfd_topo2/r4/ipv6_routes.json b/tests/topotests/bfd_topo2/r4/ipv6_routes.json new file mode 100644 index 0000000..eb571d5 --- /dev/null +++ b/tests/topotests/bfd_topo2/r4/ipv6_routes.json @@ -0,0 +1,53 @@ +{ + "2001:db8:4::/64": [ + { + "distance": 110, + "protocol": "ospf6", + "metric": 10, + "prefix": "2001:db8:4::/64", + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "interfaceName": "r4-eth0" + } + ] + }, + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "installed": true, + "prefix": "2001:db8:4::/64", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r4-eth0", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "2001:db8:1::/64": [ + { + "distance": 110, + "protocol": "ospf6", + "metric": 20, + "selected": true, + "installed": true, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "interfaceName": "r4-eth0", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ] +} diff --git a/tests/topotests/bfd_topo2/r4/ospf6d.conf b/tests/topotests/bfd_topo2/r4/ospf6d.conf new file mode 100644 index 0000000..2f38c51 --- /dev/null +++ b/tests/topotests/bfd_topo2/r4/ospf6d.conf @@ -0,0 +1,10 @@ +interface r4-eth0 + ipv6 ospf6 area 0.0.0.1 + ipv6 ospf6 bfd + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +router ospf6 + ospf6 router-id 10.254.254.4 + redistribute connected +! diff --git a/tests/topotests/bfd_topo2/r4/peers.json b/tests/topotests/bfd_topo2/r4/peers.json new file mode 100644 index 0000000..dd26b9b --- /dev/null +++ b/tests/topotests/bfd_topo2/r4/peers.json @@ -0,0 +1,31 @@ +[ + { + "multihop":true, + "peer":"2001:db8:1::1", + "local":"2001:db8:4::1", + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":300, + "transmit-interval":300, + "echo-receive-interval": 50, + "echo-transmit-interval":0, + "remote-receive-interval":300, + "remote-transmit-interval":300, + "remote-echo-receive-interval":50 + }, + { + "multihop":false, + "interface":"r4-eth0", + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":300, + "transmit-interval":300, + "echo-receive-interval": 50, + "echo-transmit-interval":0, + "remote-receive-interval":300, + "remote-transmit-interval":300, + "remote-echo-receive-interval":50 + } +] diff --git a/tests/topotests/bfd_topo2/r4/zebra.conf b/tests/topotests/bfd_topo2/r4/zebra.conf new file mode 100644 index 0000000..e4f8fd8 --- /dev/null +++ b/tests/topotests/bfd_topo2/r4/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.4/32 +! +interface r4-eth0 + ipv6 address 2001:db8:4::1/64 +! diff --git a/tests/topotests/bfd_topo2/test_bfd_topo2.dot b/tests/topotests/bfd_topo2/test_bfd_topo2.dot new file mode 100644 index 0000000..6b68fb3 --- /dev/null +++ b/tests/topotests/bfd_topo2/test_bfd_topo2.dot @@ -0,0 +1,73 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n10.0.3.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n2001:db8:4::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + + r2 -- sw2 [label="eth1"]; + r3 -- sw2 [label="eth0"]; + + r2 -- sw3 [label="eth2"]; + r4 -- sw3 [label="eth0"]; +} diff --git a/tests/topotests/bfd_topo2/test_bfd_topo2.jpg b/tests/topotests/bfd_topo2/test_bfd_topo2.jpg new file mode 100644 index 0000000..35fe562 Binary files /dev/null and b/tests/topotests/bfd_topo2/test_bfd_topo2.jpg differ diff --git a/tests/topotests/bfd_topo2/test_bfd_topo2.py b/tests/topotests/bfd_topo2/test_bfd_topo2.py new file mode 100644 index 0000000..636dbf3 --- /dev/null +++ b/tests/topotests/bfd_topo2/test_bfd_topo2.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bfd_topo2.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_bfd_topo2.py: Test the FRR BFD daemon with multihop and BGP +unnumbered. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd, pytest.mark.ospfd] + + +def setup_module(mod): + "Sets up the pytest environment" + topodef = { + "s1": ("r1", "r2"), + "s2": ("r2", "r3"), + "s3": ("r2", "r4"), + } + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) + router.load_config(TopoRouter.RD_ZEBRA, daemon_file) + + daemon_file = "{}/{}/bfdd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BFD, daemon_file) + + daemon_file = "{}/{}/bgpd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BGP, daemon_file) + + daemon_file = "{}/{}/ospfd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_OSPF, daemon_file) + + daemon_file = "{}/{}/ospf6d.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_OSPF6, daemon_file) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged before checking for the BFD + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv4 routing tables. + logger.info("Checking IPv4 routes for convergence") + for router in tgen.routers().values(): + json_file = "{}/{}/ipv4_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show ip route json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check IPv6 routing tables. + logger.info("Checking IPv6 routes for convergence") + for router in tgen.routers().values(): + json_file = "{}/{}/ipv6_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show ipv6 route json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_bfd_connection(): + "Assert that the BFD peers can find themselves." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bfd peers to go up") + + for router in tgen.routers().values(): + json_file = "{}/{}/peers.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd_topo3/__init__.py b/tests/topotests/bfd_topo3/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bfd_topo3/r1/bfd-peers.json b/tests/topotests/bfd_topo3/r1/bfd-peers.json new file mode 100644 index 0000000..3ce8d97 --- /dev/null +++ b/tests/topotests/bfd_topo3/r1/bfd-peers.json @@ -0,0 +1,68 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "id": "*", + "local": "2001:db8:1::1", + "minimum-ttl": 253, + "multihop": true, + "passive-mode": true, + "peer": "2001:db8:3::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "uptime": "*", + "transmit-interval": 2000 + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "id": "*", + "interface": "r1-eth0", + "local": "2001:db8:1::1", + "multihop": false, + "passive-mode": true, + "peer": "2001:db8:1::2", + "receive-interval": 600, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 600, + "remote-transmit-interval": 600, + "status": "up", + "uptime": "*", + "transmit-interval": 600 + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "id": "*", + "local": "192.168.1.1", + "minimum-ttl": 254, + "multihop": true, + "passive-mode": true, + "peer": "192.168.2.1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "uptime": "*", + "transmit-interval": 2000 + } +] diff --git a/tests/topotests/bfd_topo3/r1/bfdd.conf b/tests/topotests/bfd_topo3/r1/bfdd.conf new file mode 100644 index 0000000..60f129b --- /dev/null +++ b/tests/topotests/bfd_topo3/r1/bfdd.conf @@ -0,0 +1,17 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + profile fast-tx + receive-interval 600 + transmit-interval 600 + passive-mode + ! + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + passive-mode + ! +! diff --git a/tests/topotests/bfd_topo3/r1/bgpd.conf b/tests/topotests/bfd_topo3/r1/bgpd.conf new file mode 100644 index 0000000..4c75d66 --- /dev/null +++ b/tests/topotests/bfd_topo3/r1/bgpd.conf @@ -0,0 +1,23 @@ +router bgp 100 + no bgp ebgp-requires-policy + neighbor 2001:db8:1::2 remote-as internal + neighbor 2001:db8:1::2 timers 3 10 + neighbor 2001:db8:1::2 bfd profile fast-tx + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 3 10 + neighbor 192.168.2.1 ebgp-multihop 2 + neighbor 192.168.2.1 bfd profile slow-tx + neighbor 2001:db8:3::1 remote-as external + neighbor 2001:db8:3::1 timers 3 10 + neighbor 2001:db8:3::1 ebgp-multihop 3 + neighbor 2001:db8:3::1 bfd profile slow-tx + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::2 activate + neighbor 192.168.2.1 activate + neighbor 2001:db8:3::1 activate + exit-address-family +! diff --git a/tests/topotests/bfd_topo3/r1/zebra.conf b/tests/topotests/bfd_topo3/r1/zebra.conf new file mode 100644 index 0000000..64aee48 --- /dev/null +++ b/tests/topotests/bfd_topo3/r1/zebra.conf @@ -0,0 +1,10 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ip address 192.168.1.1/24 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bfd_topo3/r2/bfd-peers.json b/tests/topotests/bfd_topo3/r2/bfd-peers.json new file mode 100644 index 0000000..a40f7e4 --- /dev/null +++ b/tests/topotests/bfd_topo3/r2/bfd-peers.json @@ -0,0 +1,46 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "id": "*", + "interface": "r2-eth0", + "local": "2001:db8:1::2", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:1::1", + "receive-interval": 600, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 600, + "remote-transmit-interval": 600, + "status": "up", + "uptime": "*", + "transmit-interval": 600 + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "id": "*", + "interface": "r2-eth1", + "local": "2001:db8:2::2", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:2::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "uptime": "*", + "transmit-interval": 2000 + } +] diff --git a/tests/topotests/bfd_topo3/r2/bfdd.conf b/tests/topotests/bfd_topo3/r2/bfdd.conf new file mode 100644 index 0000000..8297043 --- /dev/null +++ b/tests/topotests/bfd_topo3/r2/bfdd.conf @@ -0,0 +1,15 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + profile fast-tx + receive-interval 600 + transmit-interval 600 + ! + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + ! +! diff --git a/tests/topotests/bfd_topo3/r2/bgpd.conf b/tests/topotests/bfd_topo3/r2/bgpd.conf new file mode 100644 index 0000000..7522576 --- /dev/null +++ b/tests/topotests/bfd_topo3/r2/bgpd.conf @@ -0,0 +1,17 @@ +router bgp 100 + no bgp ebgp-requires-policy + neighbor 2001:db8:1::1 remote-as internal + neighbor 2001:db8:1::1 timers 3 10 + neighbor 2001:db8:1::1 bfd profile fast-tx + neighbor 2001:db8:2::1 remote-as external + neighbor 2001:db8:2::1 timers 3 10 + neighbor 2001:db8:2::1 bfd profile slow-tx + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::1 activate + neighbor 2001:db8:2::1 activate + exit-address-family +! diff --git a/tests/topotests/bfd_topo3/r2/zebra.conf b/tests/topotests/bfd_topo3/r2/zebra.conf new file mode 100644 index 0000000..c7e22d4 --- /dev/null +++ b/tests/topotests/bfd_topo3/r2/zebra.conf @@ -0,0 +1,14 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ip address 192.168.1.2/24 + ipv6 address 2001:db8:1::2/64 +! +interface r2-eth1 + ip address 192.168.2.2/24 + ipv6 address 2001:db8:2::2/64 +! diff --git a/tests/topotests/bfd_topo3/r3/bfd-peers.json b/tests/topotests/bfd_topo3/r3/bfd-peers.json new file mode 100644 index 0000000..2182b26 --- /dev/null +++ b/tests/topotests/bfd_topo3/r3/bfd-peers.json @@ -0,0 +1,68 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "id": "*", + "interface": "r3-eth1", + "local": "2001:db8:3::2", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:3::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "uptime": "*", + "transmit-interval": 2000 + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "id": "*", + "interface": "r3-eth0", + "local": "2001:db8:2::1", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:2::2", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "uptime": "*", + "transmit-interval": 2000 + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "id": "*", + "local": "192.168.2.1", + "minimum-ttl": 254, + "multihop": true, + "passive-mode": false, + "peer": "192.168.1.1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "uptime": "*", + "transmit-interval": 2000 + } +] diff --git a/tests/topotests/bfd_topo3/r3/bfd-static-down.json b/tests/topotests/bfd_topo3/r3/bfd-static-down.json new file mode 100644 index 0000000..60752d3 --- /dev/null +++ b/tests/topotests/bfd_topo3/r3/bfd-static-down.json @@ -0,0 +1,12 @@ +{ + "path-list": { + "ipv4-multicast": [], + "ipv4-unicast": [], + "ipv6-unicast": [ + { + "prefix": "2001:db8:5::\/64", + "vrf": "default", + "installed": false + } + ] + }} diff --git a/tests/topotests/bfd_topo3/r3/bfd-static.json b/tests/topotests/bfd_topo3/r3/bfd-static.json new file mode 100644 index 0000000..c65060c --- /dev/null +++ b/tests/topotests/bfd_topo3/r3/bfd-static.json @@ -0,0 +1,13 @@ +{ + "path-list": { + "ipv4-multicast": [], + "ipv4-unicast": [], + "ipv6-unicast": [ + { + "prefix": "2001:db8:5::\/64", + "vrf": "default", + "installed": true + } + ] + } +} diff --git a/tests/topotests/bfd_topo3/r3/bfdd.conf b/tests/topotests/bfd_topo3/r3/bfdd.conf new file mode 100644 index 0000000..51ce2ac --- /dev/null +++ b/tests/topotests/bfd_topo3/r3/bfdd.conf @@ -0,0 +1,11 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + ! +! diff --git a/tests/topotests/bfd_topo3/r3/bgpd.conf b/tests/topotests/bfd_topo3/r3/bgpd.conf new file mode 100644 index 0000000..82adf8b --- /dev/null +++ b/tests/topotests/bfd_topo3/r3/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 300 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 3 10 + neighbor 192.168.1.1 ebgp-multihop 2 + neighbor 192.168.1.1 bfd profile slow-tx + neighbor 2001:db8:2::2 remote-as external + neighbor 2001:db8:2::2 timers 3 10 + neighbor 2001:db8:2::2 bfd profile slow-tx + neighbor 2001:db8:3::1 remote-as external + neighbor 2001:db8:3::1 timers 3 10 + neighbor 2001:db8:3::1 bfd profile slow-tx + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 192.168.1.1 activate + neighbor 2001:db8:2::2 activate + neighbor 2001:db8:3::1 activate + exit-address-family +! diff --git a/tests/topotests/bfd_topo3/r3/staticd.conf b/tests/topotests/bfd_topo3/r3/staticd.conf new file mode 100644 index 0000000..44f91f3 --- /dev/null +++ b/tests/topotests/bfd_topo3/r3/staticd.conf @@ -0,0 +1 @@ +ipv6 route 2001:db8:5::/64 2001:db8:4::3 bfd multi-hop profile slow-tx diff --git a/tests/topotests/bfd_topo3/r3/zebra.conf b/tests/topotests/bfd_topo3/r3/zebra.conf new file mode 100644 index 0000000..14248fb --- /dev/null +++ b/tests/topotests/bfd_topo3/r3/zebra.conf @@ -0,0 +1,14 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ip address 192.168.2.1/24 + ipv6 address 2001:db8:2::1/64 +! +interface r3-eth1 + ip address 192.168.3.2/24 + ipv6 address 2001:db8:3::2/64 +! diff --git a/tests/topotests/bfd_topo3/r4/bfd-peers.json b/tests/topotests/bfd_topo3/r4/bfd-peers.json new file mode 100644 index 0000000..4f71d75 --- /dev/null +++ b/tests/topotests/bfd_topo3/r4/bfd-peers.json @@ -0,0 +1,90 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "id": "*", + "local": "2001:db8:3::1", + "minimum-ttl": 253, + "multihop": true, + "passive-mode": false, + "peer": "2001:db8:1::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "uptime": "*", + "transmit-interval": 2000, + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "id": "*", + "interface": "r4-eth0", + "local": "2001:db8:3::1", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:3::2", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "uptime": "*", + "transmit-interval": 2000, + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "id": "*", + "multihop": false, + "passive-mode": false, + "peer": "192.168.4.3", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "id": "*", + "multihop": false, + "passive-mode": false, + "peer": "192.168.4.2", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd_topo3/r4/bfdd.conf b/tests/topotests/bfd_topo3/r4/bfdd.conf new file mode 100644 index 0000000..e5fc164 --- /dev/null +++ b/tests/topotests/bfd_topo3/r4/bfdd.conf @@ -0,0 +1,16 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + ! + profile slow-tx-mh + receive-interval 2000 + transmit-interval 2000 + minimum-ttl 250 + ! +! diff --git a/tests/topotests/bfd_topo3/r4/bgpd.conf b/tests/topotests/bfd_topo3/r4/bgpd.conf new file mode 100644 index 0000000..bfad78a --- /dev/null +++ b/tests/topotests/bfd_topo3/r4/bgpd.conf @@ -0,0 +1,19 @@ +router bgp 400 + no bgp ebgp-requires-policy + neighbor 2001:db8:3::2 remote-as external + neighbor 2001:db8:3::2 timers 3 10 + neighbor 2001:db8:3::2 bfd profile slow-tx + neighbor 2001:db8:1::1 remote-as external + neighbor 2001:db8:1::1 timers 3 10 + neighbor 2001:db8:1::1 ebgp-multihop 3 + neighbor 2001:db8:1::1 bfd profile slow-tx-mh + address-family ipv4 unicast + redistribute connected + redistribute static + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::1 activate + neighbor 2001:db8:3::2 activate + exit-address-family +! diff --git a/tests/topotests/bfd_topo3/r4/staticd.conf b/tests/topotests/bfd_topo3/r4/staticd.conf new file mode 100644 index 0000000..3b1c5bf --- /dev/null +++ b/tests/topotests/bfd_topo3/r4/staticd.conf @@ -0,0 +1,2 @@ +ip route 10.254.254.5/32 192.168.4.2 bfd profile slow-tx +ip route 10.254.254.6/32 192.168.4.3 bfd profile slow-tx diff --git a/tests/topotests/bfd_topo3/r4/zebra.conf b/tests/topotests/bfd_topo3/r4/zebra.conf new file mode 100644 index 0000000..2574941 --- /dev/null +++ b/tests/topotests/bfd_topo3/r4/zebra.conf @@ -0,0 +1,14 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.4/32 +! +interface r4-eth0 + ip address 192.168.3.1/24 + ipv6 address 2001:db8:3::1/64 +! +interface r4-eth1 + ip address 192.168.4.1/24 + ipv6 address 2001:db8:4::1/64 +! diff --git a/tests/topotests/bfd_topo3/r5/bfd-peers.json b/tests/topotests/bfd_topo3/r5/bfd-peers.json new file mode 100644 index 0000000..777b1dd --- /dev/null +++ b/tests/topotests/bfd_topo3/r5/bfd-peers.json @@ -0,0 +1,23 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "id": "*", + "multihop": false, + "passive-mode": false, + "peer": "192.168.4.1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd_topo3/r5/bfdd.conf b/tests/topotests/bfd_topo3/r5/bfdd.conf new file mode 100644 index 0000000..ec62d8d --- /dev/null +++ b/tests/topotests/bfd_topo3/r5/bfdd.conf @@ -0,0 +1,11 @@ +!debug bfd network +!debug bfd peer +!debug bfd zebra +! +bfd + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + minimum-ttl 250 + ! +! diff --git a/tests/topotests/bfd_topo3/r5/staticd.conf b/tests/topotests/bfd_topo3/r5/staticd.conf new file mode 100644 index 0000000..9828cff --- /dev/null +++ b/tests/topotests/bfd_topo3/r5/staticd.conf @@ -0,0 +1,2 @@ +ip route 0.0.0.0/0 192.168.4.1 +ip route 10.254.254.4/32 192.168.4.1 bfd profile slow-tx diff --git a/tests/topotests/bfd_topo3/r5/zebra.conf b/tests/topotests/bfd_topo3/r5/zebra.conf new file mode 100644 index 0000000..f84ce7e --- /dev/null +++ b/tests/topotests/bfd_topo3/r5/zebra.conf @@ -0,0 +1,10 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.5/32 +! +interface r5-eth0 + ip address 192.168.4.2/24 + ipv6 address 2001:db8:4::2/64 +! diff --git a/tests/topotests/bfd_topo3/r6/bfd-peers.json b/tests/topotests/bfd_topo3/r6/bfd-peers.json new file mode 100644 index 0000000..4de451d --- /dev/null +++ b/tests/topotests/bfd_topo3/r6/bfd-peers.json @@ -0,0 +1,46 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "id": "*", + "multihop": false, + "passive-mode": false, + "peer": "192.168.4.1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-receive-interval": 50, + "echo-transmit-interval": 0, + "id": "*", + "local": "2001:db8:4::3", + "minimum-ttl": 2, + "multihop": true, + "passive-mode": false, + "peer": "2001:db8:3::2", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-receive-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd_topo3/r6/bfd-static-down.json b/tests/topotests/bfd_topo3/r6/bfd-static-down.json new file mode 100644 index 0000000..4dadff2 --- /dev/null +++ b/tests/topotests/bfd_topo3/r6/bfd-static-down.json @@ -0,0 +1,19 @@ +{ + "path-list": { + "ipv4-multicast": [], + "ipv4-unicast": [ + { + "installed": true, + "prefix": "10.254.254.4/32", + "vrf": "default" + } + ], + "ipv6-unicast": [ + { + "prefix": "2001:db8:1::\/64", + "vrf": "default", + "installed": false + } + ] + } +} diff --git a/tests/topotests/bfd_topo3/r6/bfd-static.json b/tests/topotests/bfd_topo3/r6/bfd-static.json new file mode 100644 index 0000000..d042889 --- /dev/null +++ b/tests/topotests/bfd_topo3/r6/bfd-static.json @@ -0,0 +1,19 @@ +{ + "path-list": { + "ipv4-multicast": [], + "ipv4-unicast": [ + { + "installed": true, + "prefix": "10.254.254.4/32", + "vrf": "default" + } + ], + "ipv6-unicast": [ + { + "prefix": "2001:db8:1::\/64", + "vrf": "default", + "installed": true + } + ] + } +} diff --git a/tests/topotests/bfd_topo3/r6/bfdd.conf b/tests/topotests/bfd_topo3/r6/bfdd.conf new file mode 100644 index 0000000..ec62d8d --- /dev/null +++ b/tests/topotests/bfd_topo3/r6/bfdd.conf @@ -0,0 +1,11 @@ +!debug bfd network +!debug bfd peer +!debug bfd zebra +! +bfd + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + minimum-ttl 250 + ! +! diff --git a/tests/topotests/bfd_topo3/r6/staticd.conf b/tests/topotests/bfd_topo3/r6/staticd.conf new file mode 100644 index 0000000..28da508 --- /dev/null +++ b/tests/topotests/bfd_topo3/r6/staticd.conf @@ -0,0 +1,5 @@ +ip route 0.0.0.0/0 192.168.4.1 +ip route 10.254.254.4/32 192.168.4.1 bfd profile slow-tx +! +ipv6 route 2001:db8:3::/64 2001:db8:4::1 +ipv6 route 2001:db8:1::/64 2001:db8:3::2 bfd multi-hop source 2001:db8:4::3 profile slow-tx diff --git a/tests/topotests/bfd_topo3/r6/zebra.conf b/tests/topotests/bfd_topo3/r6/zebra.conf new file mode 100644 index 0000000..b8f2ea4 --- /dev/null +++ b/tests/topotests/bfd_topo3/r6/zebra.conf @@ -0,0 +1,10 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.6/32 +! +interface r6-eth0 + ip address 192.168.4.3/24 + ipv6 address 2001:db8:4::3/64 +! diff --git a/tests/topotests/bfd_topo3/test_bfd_topo3.dot b/tests/topotests/bfd_topo3/test_bfd_topo3.dot new file mode 100644 index 0000000..8d18783 --- /dev/null +++ b/tests/topotests/bfd_topo3/test_bfd_topo3.dot @@ -0,0 +1,95 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo3"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + r5 [ + shape=doubleoctagon + label="r5", + fillcolor="#f08080", + style=filled, + ]; + r6 [ + shape=doubleoctagon + label="r6", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n192.168.1.0/24\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n192.168.2.0/24\n2001:db8:2::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n192.168.3.0/24\n2001:db8:3::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw4 [ + shape=oval, + label="sw4\n192.168.4.0/24\n2001:db8:4::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0\n.1"]; + r2 -- sw1 [label="eth0\n.2"]; + + r3 -- sw2 [label="eth0\n.1"]; + r2 -- sw2 [label="eth1\n.2"]; + + r4 -- sw3 [label="eth0\n.1"]; + r3 -- sw3 [label="eth2\n.2"]; + + r4 -- sw4 [label="eth1\n.1"]; + r5 -- sw4 [label="eth0\n.2"]; + r6 -- sw4 [label="eth0\n.3"]; +} diff --git a/tests/topotests/bfd_topo3/test_bfd_topo3.jpg b/tests/topotests/bfd_topo3/test_bfd_topo3.jpg new file mode 100644 index 0000000..f100eae Binary files /dev/null and b/tests/topotests/bfd_topo3/test_bfd_topo3.jpg differ diff --git a/tests/topotests/bfd_topo3/test_bfd_topo3.py b/tests/topotests/bfd_topo3/test_bfd_topo3.py new file mode 100644 index 0000000..c0dc052 --- /dev/null +++ b/tests/topotests/bfd_topo3/test_bfd_topo3.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bfd_topo3.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_bfd_topo3.py: Test the FRR BFD daemon multi hop. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd] + + +def setup_module(mod): + "Sets up the pytest environment" + topodef = { + "s1": ("r1", "r2"), + "s2": ("r2", "r3"), + "s3": ("r3", "r4"), + "s4": ("r4", "r5", "r6"), + } + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + daemon_file = "{}/{}/bfdd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BFD, daemon_file) + + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_ZEBRA, daemon_file) + + daemon_file = "{}/{}/bgpd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BGP, daemon_file) + + daemon_file = "{}/{}/staticd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_STATIC, daemon_file) + + # Initialize all routers. + tgen.start_router() + + +def test_wait_bgp_convergence(): + "Wait for BGP to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + def expect_loopback_route(router, iptype, route, proto): + "Wait until route is present on RIB for protocol." + logger.info("waiting route {} in {}".format(route, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show {} route json".format(iptype), + {route: [{"protocol": proto}]}, + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" OSPF convergence failure'.format(router) + assert result is None, assertmsg + + # Wait for R1 <-> R2 convergence. + expect_loopback_route("r1", "ip", "10.254.254.2/32", "bgp") + # Wait for R1 <-> R3 convergence. + expect_loopback_route("r1", "ip", "10.254.254.3/32", "bgp") + # Wait for R1 <-> R4 convergence. + expect_loopback_route("r1", "ip", "10.254.254.4/32", "bgp") + # Wait for R1 <-> R5 convergence. + expect_loopback_route("r1", "ip", "10.254.254.5/32", "bgp") + # Wait for R1 <-> R6 convergence. + expect_loopback_route("r1", "ip", "10.254.254.6/32", "bgp") + + # Wait for R2 <-> R1 convergence. + expect_loopback_route("r2", "ip", "10.254.254.1/32", "bgp") + # Wait for R2 <-> R3 convergence. + expect_loopback_route("r2", "ip", "10.254.254.3/32", "bgp") + # Wait for R2 <-> R4 convergence. + expect_loopback_route("r2", "ip", "10.254.254.4/32", "bgp") + # Wait for R2 <-> R5 convergence. + expect_loopback_route("r2", "ip", "10.254.254.5/32", "bgp") + # Wait for R2 <-> R6 convergence. + expect_loopback_route("r2", "ip", "10.254.254.6/32", "bgp") + + # Wait for R3 <-> R1 convergence. + expect_loopback_route("r3", "ip", "10.254.254.1/32", "bgp") + # Wait for R3 <-> R2 convergence. + expect_loopback_route("r3", "ip", "10.254.254.2/32", "bgp") + # Wait for R3 <-> R4 convergence. + expect_loopback_route("r3", "ip", "10.254.254.4/32", "bgp") + # Wait for R3 <-> R5 convergence. + expect_loopback_route("r3", "ip", "10.254.254.5/32", "bgp") + # Wait for R3 <-> R6 convergence. + expect_loopback_route("r3", "ip", "10.254.254.6/32", "bgp") + + # Wait for R4 <-> R1 convergence. + expect_loopback_route("r4", "ip", "10.254.254.1/32", "bgp") + # Wait for R4 <-> R2 convergence. + expect_loopback_route("r4", "ip", "10.254.254.2/32", "bgp") + # Wait for R4 <-> R3 convergence. + expect_loopback_route("r4", "ip", "10.254.254.3/32", "bgp") + # Wait for R4 <-> R5 convergence. + expect_loopback_route("r4", "ip", "10.254.254.5/32", "static") + # Wait for R4 <-> R6 convergence. + expect_loopback_route("r4", "ip", "10.254.254.6/32", "static") + + # Wait for R5 <-> R6 convergence. + expect_loopback_route("r3", "ipv6", "2001:db8:5::/64", "static") + # Wait for R6 <-> R5 convergence. + expect_loopback_route("r6", "ipv6", "2001:db8:1::/64", "static") + + +def test_wait_bfd_convergence(): + "Wait for BFD to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("test BFD configurations") + + def expect_bfd_configuration(router): + "Load JSON file and compare with 'show bfd peer json'" + logger.info("waiting BFD configuration on router {}".format(router)) + bfd_config = json.loads(open("{}/{}/bfd-peers.json".format(CWD, router)).read()) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show bfd peers json", + bfd_config, + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" BFD configuration failure'.format(router) + assert result is None, assertmsg + + expect_bfd_configuration("r1") + expect_bfd_configuration("r2") + expect_bfd_configuration("r3") + expect_bfd_configuration("r4") + expect_bfd_configuration("r5") + expect_bfd_configuration("r6") + + +def test_static_route_monitoring(): + "Test static route monitoring output." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("test BFD static route status") + + def expect_static_bfd_output(router, filename): + "Load JSON file and compare with 'show bfd peer json'" + logger.info("waiting BFD configuration on router {}".format(router)) + bfd_config = json.loads( + open("{}/{}/{}.json".format(CWD, router, filename)).read() + ) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show bfd static route json", + bfd_config, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assertmsg = '"{}" BFD static route status failure'.format(router) + assert result is None, assertmsg + + expect_static_bfd_output("r3", "bfd-static") + expect_static_bfd_output("r6", "bfd-static") + + logger.info("Setting r4 link down ...") + + tgen.gears["r4"].link_enable("r4-eth0", False) + + expect_static_bfd_output("r3", "bfd-static-down") + expect_static_bfd_output("r6", "bfd-static-down") + + +def test_expect_static_rib_removal(): + "Test that route got removed from RIB (staticd and bgpd)." + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def expect_route_missing(router, iptype, route): + "Wait until route is present on RIB for protocol." + logger.info("waiting route {} to disapear in {}".format(route, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show {} route json".format(iptype), + {route: None}, + ) + rv, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assertmsg = '"{}" convergence failure'.format(router) + assert result is None, assertmsg + + expect_route_missing("r1", "ip", "10.254.254.5/32") + expect_route_missing("r2", "ip", "10.254.254.5/32") + expect_route_missing("r3", "ip", "10.254.254.5/32") + expect_route_missing("r3", "ipv6", "2001:db8:5::/64") + expect_route_missing("r6", "ipv6", "2001:db8:1::/64") + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd_vrf_topo1/__init__.py b/tests/topotests/bfd_vrf_topo1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bfd_vrf_topo1/r1/bfdd.conf b/tests/topotests/bfd_vrf_topo1/r1/bfdd.conf new file mode 100644 index 0000000..0476df7 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r1/bfdd.conf @@ -0,0 +1,15 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 192.168.0.2 vrf r1-bfd-cust1 + receive-interval 1000 + transmit-interval 1000 + echo-mode + echo transmit-interval 1000 + echo receive-interval 1000 + no shutdown + ! +! diff --git a/tests/topotests/bfd_vrf_topo1/r1/bgp_prefixes.json b/tests/topotests/bfd_vrf_topo1/r1/bgp_prefixes.json new file mode 100644 index 0000000..1262f5e --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r1/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.2/32": [ + { + "path": "102", + "prefix": "10.254.254.2", + "valid": true, + "peerId": "192.168.0.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.3/32": [ + { + "path": "102 103", + "prefix": "10.254.254.3", + "valid": true, + "peerId": "192.168.0.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.4/32": [ + { + "path": "102 104", + "prefix": "10.254.254.4", + "valid": true, + "peerId": "192.168.0.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.2", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd_vrf_topo1/r1/bgp_summary.json b/tests/topotests/bfd_vrf_topo1/r1/bgp_summary.json new file mode 100644 index 0000000..fa07d60 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r1/bgp_summary.json @@ -0,0 +1,11 @@ +{ + "ipv4Unicast": { + "as": 101, + "peers": { + "192.168.0.2": { + "remoteAs": 102, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd_vrf_topo1/r1/bgpd.conf b/tests/topotests/bfd_vrf_topo1/r1/bgpd.conf new file mode 100644 index 0000000..cf72f30 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r1/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 101 vrf r1-bfd-cust1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.0.2 remote-as 102 + neighbor 192.168.0.2 timers 3 10 +! neighbor 192.168.0.2 ebgp-multihop 10 + neighbor 192.168.0.2 bfd + address-family ipv4 unicast + network 10.254.254.1/32 + exit-address-family +! diff --git a/tests/topotests/bfd_vrf_topo1/r1/peers.json b/tests/topotests/bfd_vrf_topo1/r1/peers.json new file mode 100644 index 0000000..57cea71 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r1/peers.json @@ -0,0 +1,8 @@ +[ + { + "remote-receive-interval": 1000, + "remote-transmit-interval": 1000, + "peer": "192.168.0.2", + "status": "up" + } +] diff --git a/tests/topotests/bfd_vrf_topo1/r1/zebra.conf b/tests/topotests/bfd_vrf_topo1/r1/zebra.conf new file mode 100644 index 0000000..62ed36f --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r1/zebra.conf @@ -0,0 +1,3 @@ +interface r1-eth0 vrf r1-bfd-cust1 + ip address 192.168.0.1/24 +! diff --git a/tests/topotests/bfd_vrf_topo1/r2/bfdd.conf b/tests/topotests/bfd_vrf_topo1/r2/bfdd.conf new file mode 100644 index 0000000..69edd15 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r2/bfdd.conf @@ -0,0 +1,23 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 192.168.0.1 vrf r2-bfd-cust1 + receive-interval 1000 + transmit-interval 1000 + echo-mode + echo transmit-interval 1000 + echo receive-interval 1000 + no shutdown + ! + peer 192.168.1.1 vrf r2-bfd-cust1 + receive-interval 1000 + transmit-interval 1000 + echo-mode + echo transmit-interval 1000 + echo receive-interval 1000 + no shutdown + ! +! diff --git a/tests/topotests/bfd_vrf_topo1/r2/bgp_prefixes.json b/tests/topotests/bfd_vrf_topo1/r2/bgp_prefixes.json new file mode 100644 index 0000000..0d47c0f --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r2/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.1/32": [ + { + "path": "101", + "prefix": "10.254.254.1", + "valid": true, + "peerId": "192.168.0.1", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.1", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.3/32": [ + { + "path": "103", + "prefix": "10.254.254.3", + "valid": true, + "peerId": "192.168.1.1", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.1", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.4/32": [ + { + "path": "104", + "prefix": "10.254.254.4", + "valid": true, + "peerId": "192.168.2.1", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.1", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd_vrf_topo1/r2/bgp_summary.json b/tests/topotests/bfd_vrf_topo1/r2/bgp_summary.json new file mode 100644 index 0000000..c0ef11a --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r2/bgp_summary.json @@ -0,0 +1,19 @@ +{ + "ipv4Unicast": { + "as": 102, + "peers": { + "192.168.0.1": { + "remoteAs": 101, + "state": "Established" + }, + "192.168.1.1": { + "remoteAs": 103, + "state": "Established" + }, + "192.168.2.1": { + "remoteAs": 104, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd_vrf_topo1/r2/bgpd.conf b/tests/topotests/bfd_vrf_topo1/r2/bgpd.conf new file mode 100644 index 0000000..132011c --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r2/bgpd.conf @@ -0,0 +1,16 @@ +router bgp 102 vrf r2-bfd-cust1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.0.1 remote-as 101 + neighbor 192.168.0.1 timers 3 10 + neighbor 192.168.0.1 bfd + neighbor 192.168.1.1 remote-as 103 + neighbor 192.168.1.1 timers 3 10 + neighbor 192.168.1.1 bfd + neighbor 192.168.2.1 remote-as 104 + neighbor 192.168.2.1 timers 3 10 + neighbor 192.168.2.1 bfd + address-family ipv4 unicast + network 10.254.254.2/32 + exit-address-family +! diff --git a/tests/topotests/bfd_vrf_topo1/r2/peers.json b/tests/topotests/bfd_vrf_topo1/r2/peers.json new file mode 100644 index 0000000..0a1c342 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r2/peers.json @@ -0,0 +1,17 @@ +[ + { + "peer": "192.168.0.1", + "status": "up" + }, + { + "remote-echo-receive-interval": 1000, + "peer": "192.168.1.1", + "status": "up" + }, + { + "remote-transmit-interval": 2000, + "remote-receive-interval": 2000, + "peer": "192.168.2.1", + "status": "up" + } +] diff --git a/tests/topotests/bfd_vrf_topo1/r2/zebra.conf b/tests/topotests/bfd_vrf_topo1/r2/zebra.conf new file mode 100644 index 0000000..1e817b1 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r2/zebra.conf @@ -0,0 +1,9 @@ +interface r2-eth0 vrf r2-bfd-cust1 + ip address 192.168.0.2/24 +! +interface r2-eth1 vrf r2-bfd-cust1 + ip address 192.168.1.2/24 +! +interface r2-eth2 vrf r2-bfd-cust1 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/bfd_vrf_topo1/r3/bfdd.conf b/tests/topotests/bfd_vrf_topo1/r3/bfdd.conf new file mode 100644 index 0000000..00162b5 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r3/bfdd.conf @@ -0,0 +1,14 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 192.168.1.2 vrf r3-bfd-cust1 + receive-interval 1000 + transmit-interval 1000 + echo-interval 1000 + echo-mode + no shutdown + ! +! diff --git a/tests/topotests/bfd_vrf_topo1/r3/bgp_prefixes.json b/tests/topotests/bfd_vrf_topo1/r3/bgp_prefixes.json new file mode 100644 index 0000000..36fca17 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r3/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.1/32": [ + { + "path": "102 101", + "prefix": "10.254.254.1", + "valid": true, + "peerId": "192.168.1.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.2/32": [ + { + "path": "102", + "prefix": "10.254.254.2", + "valid": true, + "peerId": "192.168.1.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.4/32": [ + { + "path": "102 104", + "prefix": "10.254.254.4", + "valid": true, + "peerId": "192.168.1.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.2", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd_vrf_topo1/r3/bgp_summary.json b/tests/topotests/bfd_vrf_topo1/r3/bgp_summary.json new file mode 100644 index 0000000..d478333 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r3/bgp_summary.json @@ -0,0 +1,11 @@ +{ + "ipv4Unicast": { + "as": 103, + "peers": { + "192.168.1.2": { + "remoteAs": 102, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd_vrf_topo1/r3/bgpd.conf b/tests/topotests/bfd_vrf_topo1/r3/bgpd.conf new file mode 100644 index 0000000..f764647 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r3/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 103 vrf r3-bfd-cust1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as 102 + neighbor 192.168.1.2 timers 3 10 + neighbor 192.168.1.2 bfd + address-family ipv4 unicast + network 10.254.254.3/32 + exit-address-family +! diff --git a/tests/topotests/bfd_vrf_topo1/r3/peers.json b/tests/topotests/bfd_vrf_topo1/r3/peers.json new file mode 100644 index 0000000..ef38008 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r3/peers.json @@ -0,0 +1,6 @@ +[ + { + "peer": "192.168.1.2", + "status": "up" + } +] diff --git a/tests/topotests/bfd_vrf_topo1/r3/zebra.conf b/tests/topotests/bfd_vrf_topo1/r3/zebra.conf new file mode 100644 index 0000000..e673459 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r3/zebra.conf @@ -0,0 +1,3 @@ +interface r3-eth0 vrf r3-bfd-cust1 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bfd_vrf_topo1/r4/bfdd.conf b/tests/topotests/bfd_vrf_topo1/r4/bfdd.conf new file mode 100644 index 0000000..119e5e5 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r4/bfdd.conf @@ -0,0 +1,12 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 192.168.2.2 vrf r4-bfd-cust1 + transmit-interval 2000 + receive-interval 2000 + no shutdown + ! +! diff --git a/tests/topotests/bfd_vrf_topo1/r4/bgp_prefixes.json b/tests/topotests/bfd_vrf_topo1/r4/bgp_prefixes.json new file mode 100644 index 0000000..efe7d47 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r4/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.1/32": [ + { + "path": "102 101", + "prefix": "10.254.254.1", + "valid": true, + "peerId": "192.168.2.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.2/32": [ + { + "path": "102", + "prefix": "10.254.254.2", + "valid": true, + "peerId": "192.168.2.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.3/32": [ + { + "path": "102 103", + "prefix": "10.254.254.3", + "valid": true, + "peerId": "192.168.2.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.2", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd_vrf_topo1/r4/bgp_summary.json b/tests/topotests/bfd_vrf_topo1/r4/bgp_summary.json new file mode 100644 index 0000000..7d81784 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r4/bgp_summary.json @@ -0,0 +1,11 @@ +{ + "ipv4Unicast": { + "as": 104, + "peers": { + "192.168.2.2": { + "remoteAs": 102, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd_vrf_topo1/r4/bgpd.conf b/tests/topotests/bfd_vrf_topo1/r4/bgpd.conf new file mode 100644 index 0000000..03353e4 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r4/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 104 vrf r4-bfd-cust1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.2.2 remote-as 102 + neighbor 192.168.2.2 timers 3 10 + neighbor 192.168.2.2 bfd + address-family ipv4 unicast + network 10.254.254.4/32 + exit-address-family +! diff --git a/tests/topotests/bfd_vrf_topo1/r4/peers.json b/tests/topotests/bfd_vrf_topo1/r4/peers.json new file mode 100644 index 0000000..3714008 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r4/peers.json @@ -0,0 +1,6 @@ +[ + { + "peer": "192.168.2.2", + "status": "up" + } +] diff --git a/tests/topotests/bfd_vrf_topo1/r4/zebra.conf b/tests/topotests/bfd_vrf_topo1/r4/zebra.conf new file mode 100644 index 0000000..15d3ec1 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/r4/zebra.conf @@ -0,0 +1,3 @@ +interface r4-eth0 vrf r4-bfd-cust1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.dot b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.dot new file mode 100644 index 0000000..c84ace2 --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.dot @@ -0,0 +1,73 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo1"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n192.168.0.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n192.168.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n192.168.2.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0\n.1"]; + r2 -- sw1 [label="eth0\n.2"]; + + r3 -- sw2 [label="eth0\n.1"]; + r2 -- sw2 [label="eth1\n.2"]; + + r4 -- sw3 [label="eth0\n.1"]; + r2 -- sw3 [label="eth2\n.2"]; +} diff --git a/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.jpg b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.jpg new file mode 100644 index 0000000..4d6d56e Binary files /dev/null and b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.jpg differ diff --git a/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py new file mode 100644 index 0000000..a532f3a --- /dev/null +++ b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bfd_vrf_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2018 by +# Network Device Education Foundation, Inc. ("NetDEF") +# Copyright (c) 2019 by 6WIND +# + +""" +test_bfd_vrf_topo1.py: Test the FRR BFD daemon. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd] + + +def build_topo(tgen): + # Create 4 routers + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # check for zebra capability + for rname, router in router_list.items(): + if router.check_capability(TopoRouter.RD_ZEBRA, "--vrfwnetns") == False: + return pytest.skip( + "Skipping BFD Topo1 VRF NETNS feature. VRF NETNS backend not available on FRR" + ) + + if os.system("ip netns list") != 0: + return pytest.skip( + "Skipping BFD Topo1 VRF NETNS Test. NETNS not available on System" + ) + + logger.info("Testing with VRF Namespace support") + + for rname, router in router_list.items(): + # create VRF rx-bfd-cust1 and link rx-eth0 to rx-bfd-cust1 + ns = "{}-bfd-cust1".format(rname) + router.net.add_netns(ns) + router.net.set_intf_netns(rname + "-eth0", ns, up=True) + if rname == "r2": + router.net.set_intf_netns(rname + "-eth1", ns, up=True) + router.net.set_intf_netns(rname + "-eth2", ns, up=True) + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, "{}/zebra.conf".format(rname)), + "--vrfwnetns", + ) + router.load_config( + TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # Move interfaces out of vrf namespace and delete the namespace + router_list = tgen.routers() + for rname, router in router_list.items(): + if rname == "r2": + router.net.reset_intf_netns(rname + "-eth2") + router.net.reset_intf_netns(rname + "-eth1") + router.net.reset_intf_netns(rname + "-eth0") + router.net.delete_netns("{}-bfd-cust1".format(rname)) + tgen.stop_topology() + + +def test_bfd_connection(): + "Assert that the BFD peers can find themselves." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bfd peers to go up") + for router in tgen.routers().values(): + json_file = "{}/{}/peers.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=16, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_bgp_convergence(): + "Assert that BGP is converging." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bgp peers to go up") + + for router in tgen.routers().values(): + ref_file = "{}/{}/bgp_summary.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip bgp vrf {}-bfd-cust1 summary json".format(router.name), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=125, wait=1.0) + assertmsg = "{}: bgp did not converge".format(router.name) + assert res is None, assertmsg + + +def test_bgp_fast_convergence(): + "Assert that BGP is converging before setting a link down." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bgp peers converge") + + for router in tgen.routers().values(): + ref_file = "{}/{}/bgp_prefixes.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip bgp vrf {}-bfd-cust1 json".format(router.name), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=40, wait=1) + assertmsg = "{}: bgp did not converge".format(router.name) + assert res is None, assertmsg + + +def test_bfd_fast_convergence(): + """ + Assert that BFD notices the link down after simulating network + failure. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Disable r2-eth0 link + router2 = tgen.gears["r2"] + topotest.interface_set_status( + router2, "r2-eth0", ifaceaction=False, vrf_name="r2-bfd-cust1" + ) + + # Wait the minimum time we can before checking that BGP/BFD + # converged. + logger.info("waiting for BFD converge") + + # Check that BGP converged quickly. + for router in tgen.routers().values(): + json_file = "{}/{}/peers.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + # Load the same file as previous test, but expect R1 to be down. + if router.name == "r1": + for peer in expected: + if peer["peer"] == "192.168.0.2": + peer["status"] = "down" + else: + for peer in expected: + if peer["peer"] == "192.168.0.1": + peer["status"] = "down" + + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=40, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert res is None, assertmsg + + +def test_bgp_fast_reconvergence(): + "Assert that BGP is converging after setting a link down." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for BGP re convergence") + + # Check that BGP converged quickly. + for router in tgen.routers().values(): + ref_file = "{}/{}/bgp_prefixes.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + + # Load the same file as previous test, but set networks to None + # to test absence. + if router.name == "r1": + expected["routes"]["10.254.254.2/32"] = None + expected["routes"]["10.254.254.3/32"] = None + expected["routes"]["10.254.254.4/32"] = None + else: + expected["routes"]["10.254.254.1/32"] = None + + test_func = partial( + topotest.router_json_cmp, + router, + "show ip bgp vrf {}-bfd-cust1 json".format(router.name), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=16, wait=1) + assertmsg = "{}: bgp did not converge".format(router.name) + assert res is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd_vrflite_topo1/__init__.py b/tests/topotests/bfd_vrflite_topo1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bfd_vrflite_topo1/r1/bfd_peers_status.json b/tests/topotests/bfd_vrflite_topo1/r1/bfd_peers_status.json new file mode 100644 index 0000000..07e96d7 --- /dev/null +++ b/tests/topotests/bfd_vrflite_topo1/r1/bfd_peers_status.json @@ -0,0 +1,96 @@ +[ + { + "multihop":false, + "peer":"192.168.0.2", + "vrf":"vrf1", + "passive-mode":false, + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":1000, + "transmit-interval":1000, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "detect-multiplier":3, + "remote-receive-interval":1000, + "remote-transmit-interval":1000, + "remote-echo-receive-interval":50, + "remote-detect-multiplier":3 + }, + { + "multihop":true, + "peer":"192.0.2.2", + "local":"192.0.2.1", + "vrf":"vrf2", + "passive-mode":false, + "minimum-ttl":254, + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":1000, + "transmit-interval":1000, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "detect-multiplier":3, + "remote-receive-interval":1000, + "remote-transmit-interval":1000, + "remote-echo-receive-interval":50, + "remote-detect-multiplier":3 + }, + { + "multihop":false, + "peer":"192.168.0.2", + "vrf":"vrf2", + "passive-mode":false, + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":1000, + "transmit-interval":1000, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "detect-multiplier":3, + "remote-receive-interval":1000, + "remote-transmit-interval":1000, + "remote-echo-receive-interval":50, + "remote-detect-multiplier":3 + }, + { + "multihop":false, + "peer":"192.168.0.2", + "vrf":"default", + "passive-mode":false, + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":1000, + "transmit-interval":1000, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "detect-multiplier":3, + "remote-receive-interval":1000, + "remote-transmit-interval":1000, + "remote-echo-receive-interval":50, + "remote-detect-multiplier":3 + }, + { + "multihop":true, + "peer":"192.0.2.2", + "local":"192.0.2.1", + "vrf":"vrf1", + "passive-mode":false, + "minimum-ttl":254, + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":1000, + "transmit-interval":1000, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "detect-multiplier":3, + "remote-receive-interval":1000, + "remote-transmit-interval":1000, + "remote-echo-receive-interval":50, + "remote-detect-multiplier":3 + } +] diff --git a/tests/topotests/bfd_vrflite_topo1/r1/bfdd.conf b/tests/topotests/bfd_vrflite_topo1/r1/bfdd.conf new file mode 100644 index 0000000..96e8ff4 --- /dev/null +++ b/tests/topotests/bfd_vrflite_topo1/r1/bfdd.conf @@ -0,0 +1,26 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 192.168.0.2 vrf vrf1 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.168.0.2 vrf vrf2 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.168.0.2 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.0.2.2 multihop local-address 192.0.2.1 vrf vrf1 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.0.2.2 multihop local-address 192.0.2.1 vrf vrf2 + transmit-interval 1000 + receive-interval 1000 + ! \ No newline at end of file diff --git a/tests/topotests/bfd_vrflite_topo1/r1/zebra.conf b/tests/topotests/bfd_vrflite_topo1/r1/zebra.conf new file mode 100644 index 0000000..ebb4e63 --- /dev/null +++ b/tests/topotests/bfd_vrflite_topo1/r1/zebra.conf @@ -0,0 +1,24 @@ +vrf vrf1 + ip route 192.0.2.2/32 192.168.0.2 +! +vrf vrf2 + ip route 192.0.2.2/32 192.168.0.2 +! +interface r1-eth0 vrf vrf3 + ip address 192.168.0.1/24 +! +interface r1-eth0.100 vrf vrf1 + ip address 192.168.0.1/24 +! +interface r1-eth0.200 vrf vrf2 + ip address 192.168.0.1/24 +! +interface r1-eth0.300 + ip address 192.168.0.1/24 +! +interface r1-loop1 vrf vrf1 + ip address 192.0.2.1/32 +! +interface r1-loop2 vrf vrf2 + ip address 192.0.2.1/32 +! \ No newline at end of file diff --git a/tests/topotests/bfd_vrflite_topo1/r2/bfdd.conf b/tests/topotests/bfd_vrflite_topo1/r2/bfdd.conf new file mode 100644 index 0000000..7b11a47 --- /dev/null +++ b/tests/topotests/bfd_vrflite_topo1/r2/bfdd.conf @@ -0,0 +1,26 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 192.168.0.1 vrf vrf1 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.168.0.1 vrf vrf2 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.168.0.1 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.0.2.1 multihop local-address 192.0.2.2 vrf vrf1 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.0.2.1 multihop local-address 192.0.2.2 vrf vrf2 + transmit-interval 1000 + receive-interval 1000 + ! \ No newline at end of file diff --git a/tests/topotests/bfd_vrflite_topo1/r2/zebra.conf b/tests/topotests/bfd_vrflite_topo1/r2/zebra.conf new file mode 100644 index 0000000..d8b996e --- /dev/null +++ b/tests/topotests/bfd_vrflite_topo1/r2/zebra.conf @@ -0,0 +1,24 @@ +vrf vrf1 + ip route 192.0.2.1/32 192.168.0.1 +! +vrf vrf2 + ip route 192.0.2.1/32 192.168.0.1 +! +interface r2-eth0 vrf vrf3 + ip address 192.168.0.2/24 +! +interface r2-eth0.100 vrf vrf1 + ip address 192.168.0.2/24 +! +interface r2-eth0.200 vrf vrf2 + ip address 192.168.0.2/24 +! +interface r2-eth0.300 + ip address 192.168.0.2/24 +! +interface r2-loop1 vrf vrf1 + ip address 192.0.2.2/32 +! +interface r2-loop2 vrf vrf2 + ip address 192.0.2.2/32 +! \ No newline at end of file diff --git a/tests/topotests/bfd_vrflite_topo1/test_bfd_vrflite_topo1.py b/tests/topotests/bfd_vrflite_topo1/test_bfd_vrflite_topo1.py new file mode 100644 index 0000000..30f4a2f --- /dev/null +++ b/tests/topotests/bfd_vrflite_topo1/test_bfd_vrflite_topo1.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bfd_vrflite_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2018 by +# Network Device Education Foundation, Inc. ("NetDEF") +# Copyright (c) 2022 by 6WIND +# + +""" +test_bfd_vrflite_topo1.py: Test the FRR BFD daemon. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd] + + +def build_topo(tgen): + # Create 2 routers + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r1"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + logger.info("Testing with Linux VRF support and udp_l3mdev=0") + if os.system("echo 0 > /proc/sys/net/ipv4/udp_l3mdev_accept") != 0: + return pytest.skip( + "Skipping BFD vrflite Topo1 Test. Linux VRF not available on System" + ) + + for rname, router in router_list.items(): + router.net.add_l3vrf("vrf1", 10) + router.net.add_l3vrf("vrf2", 20) + router.net.add_l3vrf("vrf3", 30) + router.net.add_vlan(rname + "-eth0.100", rname + "-eth0", 100) + router.net.add_vlan(rname + "-eth0.200", rname + "-eth0", 200) + router.net.add_vlan(rname + "-eth0.300", rname + "-eth0", 300) + router.net.attach_iface_to_l3vrf(rname + "-eth0.100", "vrf1") + router.net.attach_iface_to_l3vrf(rname + "-eth0.200", "vrf2") + router.net.add_loop(rname + "-loop1") + router.net.add_loop(rname + "-loop2") + router.net.attach_iface_to_l3vrf(rname + "-loop1", "vrf1") + router.net.attach_iface_to_l3vrf(rname + "-loop2", "vrf2") + router.net.attach_iface_to_l3vrf(rname + "-eth0", "vrf3") + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # Move interfaces out of vrf namespace and delete the namespace + router_list = tgen.routers() + for rname, router in router_list.items(): + router.net.del_iface(rname + "-eth0.100") + router.net.del_iface(rname + "-eth0.200") + router.net.del_iface(rname + "-eth0.300") + router.net.del_iface(rname + "-loop1") + router.net.del_iface(rname + "-loop2") + + tgen.stop_topology() + + +def test_bfd_connection(): + "Assert that the BFD peers can find themselves." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("waiting for bfd peers to go up") + router = tgen.gears['r1'] + json_file = "{}/{}/bfd_peers_status.json".format(CWD, 'r1') + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=16, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_accept_own/__init__.py b/tests/topotests/bgp_accept_own/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_accept_own/ce1/bgpd.conf b/tests/topotests/bgp_accept_own/ce1/bgpd.conf new file mode 100644 index 0000000..44f95b9 --- /dev/null +++ b/tests/topotests/bgp_accept_own/ce1/bgpd.conf @@ -0,0 +1,12 @@ +! +!debug bgp updates +! +router bgp 65010 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_accept_own/ce1/zebra.conf b/tests/topotests/bgp_accept_own/ce1/zebra.conf new file mode 100644 index 0000000..7863ae1 --- /dev/null +++ b/tests/topotests/bgp_accept_own/ce1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.1/32 +! +interface ce1-eth0 + ip address 192.168.1.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_accept_own/ce2/bgpd.conf b/tests/topotests/bgp_accept_own/ce2/bgpd.conf new file mode 100644 index 0000000..d60fdcf --- /dev/null +++ b/tests/topotests/bgp_accept_own/ce2/bgpd.conf @@ -0,0 +1,12 @@ +! +!debug bgp updates +! +router bgp 65020 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_accept_own/ce2/zebra.conf b/tests/topotests/bgp_accept_own/ce2/zebra.conf new file mode 100644 index 0000000..829967e --- /dev/null +++ b/tests/topotests/bgp_accept_own/ce2/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.2/32 +! +interface ce2-eth0 + ip address 192.168.2.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_accept_own/pe1/bgpd.conf b/tests/topotests/bgp_accept_own/pe1/bgpd.conf new file mode 100644 index 0000000..15466b4 --- /dev/null +++ b/tests/topotests/bgp_accept_own/pe1/bgpd.conf @@ -0,0 +1,50 @@ +! +!debug bgp updates +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp nht +! +router bgp 65001 + bgp router-id 10.10.10.10 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 10.10.10.101 remote-as internal + neighbor 10.10.10.101 update-source 10.10.10.10 + neighbor 10.10.10.101 timers 1 3 + neighbor 10.10.10.101 timers connect 1 + address-family ipv4 vpn + neighbor 10.10.10.101 activate + neighbor 10.10.10.101 attribute-unchanged + exit-address-family +! +router bgp 65001 vrf Customer + bgp router-id 192.168.1.2 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + address-family ipv4 unicast + redistribute connected + label vpn export 10 + rd vpn export 192.168.1.2:2 + rt vpn import 192.168.1.2:2 + rt vpn export 192.168.1.2:2 + export vpn + import vpn + exit-address-family +! +router bgp 65001 vrf Service + bgp router-id 192.168.2.2 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 + address-family ipv4 unicast + label vpn export 20 + rd vpn export 192.168.2.2:2 + rt vpn import 192.168.2.2:2 + rt vpn export 192.168.2.2:2 + export vpn + import vpn + exit-address-family +! diff --git a/tests/topotests/bgp_accept_own/pe1/ldpd.conf b/tests/topotests/bgp_accept_own/pe1/ldpd.conf new file mode 100644 index 0000000..7c1ea33 --- /dev/null +++ b/tests/topotests/bgp_accept_own/pe1/ldpd.conf @@ -0,0 +1,12 @@ +mpls ldp + router-id 10.10.10.10 + ! + address-family ipv4 + discovery transport-address 10.10.10.10 + ! + interface pe1-eth2 + exit + ! + exit-address-family + ! +exit diff --git a/tests/topotests/bgp_accept_own/pe1/ospfd.conf b/tests/topotests/bgp_accept_own/pe1/ospfd.conf new file mode 100644 index 0000000..1a5e1a0 --- /dev/null +++ b/tests/topotests/bgp_accept_own/pe1/ospfd.conf @@ -0,0 +1,7 @@ +interface pe1-eth2 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +! +router ospf + router-id 10.10.10.10 + network 0.0.0.0/0 area 0 diff --git a/tests/topotests/bgp_accept_own/pe1/zebra.conf b/tests/topotests/bgp_accept_own/pe1/zebra.conf new file mode 100644 index 0000000..2b7aefa --- /dev/null +++ b/tests/topotests/bgp_accept_own/pe1/zebra.conf @@ -0,0 +1,18 @@ +! +interface lo + ip address 10.10.10.10/32 +! +interface pe1-eth0 vrf Customer + ip address 192.168.1.2/24 +! +interface pe1-eth1 vrf Service + ip address 192.168.2.2/24 +! +interface pe1-eth2 + ip address 10.0.1.1/24 +! +interface pe1-eth3 vrf Customer + ip address 192.0.2.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_accept_own/rr1/bgpd.conf b/tests/topotests/bgp_accept_own/rr1/bgpd.conf new file mode 100644 index 0000000..ad0ee3e --- /dev/null +++ b/tests/topotests/bgp_accept_own/rr1/bgpd.conf @@ -0,0 +1,25 @@ +! +!debug bgp updates +! +router bgp 65001 + bgp router-id 10.10.10.101 + bgp route-reflector allow-outbound-policy + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 10.10.10.10 remote-as internal + neighbor 10.10.10.10 update-source 10.10.10.101 + neighbor 10.10.10.10 timers 1 3 + neighbor 10.10.10.10 timers connect 1 + address-family ipv4 vpn + neighbor 10.10.10.10 activate + neighbor 10.10.10.10 route-reflector-client + neighbor 10.10.10.10 route-map pe1 out + exit-address-family +! +route-map pe1 permit 10 + set extcommunity rt 192.168.1.2:2 192.168.2.2:2 + set community 65001:111 accept-own additive + set ip next-hop unchanged +route-map pe1 permit 20 +exit +! diff --git a/tests/topotests/bgp_accept_own/rr1/ldpd.conf b/tests/topotests/bgp_accept_own/rr1/ldpd.conf new file mode 100644 index 0000000..0369901 --- /dev/null +++ b/tests/topotests/bgp_accept_own/rr1/ldpd.conf @@ -0,0 +1,12 @@ +mpls ldp + router-id 10.10.10.101 + ! + address-family ipv4 + discovery transport-address 10.10.10.101 + ! + interface rr1-eth0 + exit + ! + exit-address-family + ! +exit diff --git a/tests/topotests/bgp_accept_own/rr1/ospfd.conf b/tests/topotests/bgp_accept_own/rr1/ospfd.conf new file mode 100644 index 0000000..b598246 --- /dev/null +++ b/tests/topotests/bgp_accept_own/rr1/ospfd.conf @@ -0,0 +1,7 @@ +interface rr1-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +! +router ospf + router-id 10.10.10.101 + network 0.0.0.0/0 area 0 diff --git a/tests/topotests/bgp_accept_own/rr1/zebra.conf b/tests/topotests/bgp_accept_own/rr1/zebra.conf new file mode 100644 index 0000000..aa3f633 --- /dev/null +++ b/tests/topotests/bgp_accept_own/rr1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 10.10.10.101/32 +! +interface rr1-eth0 + ip address 10.0.1.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_accept_own/test_bgp_accept_own.py b/tests/topotests/bgp_accept_own/test_bgp_accept_own.py new file mode 100644 index 0000000..d294da0 --- /dev/null +++ b/tests/topotests/bgp_accept_own/test_bgp_accept_own.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" + +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("ce1") + tgen.add_router("ce2") + tgen.add_router("pe1") + tgen.add_router("rr1") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["ce1"]) + switch.add_link(tgen.gears["pe1"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["ce2"]) + switch.add_link(tgen.gears["pe1"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["pe1"]) + switch.add_link(tgen.gears["rr1"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["pe1"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + pe1 = tgen.gears["pe1"] + rr1 = tgen.gears["rr1"] + + pe1.run("ip link add Customer type vrf table 1001") + pe1.run("ip link set up dev Customer") + pe1.run("ip link set pe1-eth0 master Customer") + pe1.run("ip link add Service type vrf table 1002") + pe1.run("ip link set up dev Service") + pe1.run("ip link set pe1-eth1 master Service") + pe1.run("ip link set pe1-eth3 master Customer") + pe1.run("sysctl -w net.mpls.conf.pe1-eth2.input=1") + rr1.run("sysctl -w net.mpls.conf.rr1-eth0.input=1") + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_accept_own(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["pe1"] + ce2 = tgen.gears["ce2"] + + step("Check if routes are not installed in PE1 from RR1 (due to ORIGINATOR_ID)") + + def _bgp_check_received_routes_due_originator_id(): + output = json.loads(pe1.vtysh_cmd("show bgp ipv4 vpn summary json")) + expected = {"peers": {"10.10.10.101": {"pfxRcd": 0, "pfxSnt": 5}}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_received_routes_due_originator_id) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Failed, received routes from RR1 regardless ORIGINATOR_ID" + + step("Enable ACCEPT_OWN for RR1") + + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 vpn + neighbor 10.10.10.101 accept-own + """ + ) + + step("Check if we received routes due to ACCEPT_OWN from RR1") + + def _bgp_check_received_routes_with_modified_rts(): + output = json.loads(pe1.vtysh_cmd("show bgp ipv4 vpn summary json")) + expected = {"peers": {"10.10.10.101": {"pfxRcd": 5, "pfxSnt": 5}}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_received_routes_with_modified_rts) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert ( + result is None + ), "Failed, didn't receive routes from RR1 with ACCEPT_OWN enabled" + + step( + "Check if 172.16.255.1/32 is imported into vrf Service due to modified RT list at RR1" + ) + + def _bgp_check_received_routes_with_changed_rts(): + output = json.loads( + pe1.vtysh_cmd("show bgp vrf Service ipv4 unicast 172.16.255.1/32 json") + ) + expected = { + "paths": [ + { + "community": {"string": "65001:111"}, + "extendedCommunity": { + "string": "RT:192.168.1.2:2 RT:192.168.2.2:2" + }, + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_received_routes_with_changed_rts) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert ( + result is None + ), "Failed, routes are not imported from RR1 with modified RT list" + + step("Check if 192.0.2.0/24 is imported to vrf Service from vrf Customer") + + def _bgp_check_imported_local_routes_from_vrf_service(): + output = json.loads( + pe1.vtysh_cmd("show ip route vrf Service 192.0.2.0/24 json") + ) + expected = { + "192.0.2.0/24": [ + { + "vrfName": "Service", + "table": 1002, + "installed": True, + "selected": True, + "nexthops": [ + { + "fib": True, + "vrf": "Customer", + "active": True, + } + ], + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_imported_local_routes_from_vrf_service) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert ( + result is None + ), "Failed, didn't import local route 192.0.2.0/24 from vrf Customer to vrf Service" + + step("Check if 172.16.255.1/32 is announced to CE2") + + def _bgp_check_received_routes_from_pe(): + output = json.loads(ce2.vtysh_cmd("show ip route 172.16.255.1/32 json")) + expected = { + "172.16.255.1/32": [ + { + "protocol": "bgp", + "installed": True, + "nexthops": [{"ip": "192.168.2.2"}], + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_received_routes_from_pe) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Failed, didn't receive 172.16.255.1/32 from PE1" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_addpath_best_selected/__init__.py b/tests/topotests/bgp_addpath_best_selected/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_addpath_best_selected/r1/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r1/bgpd.conf new file mode 100644 index 0000000..ba10f7b --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r1/bgpd.conf @@ -0,0 +1,7 @@ +! +router bgp 65001 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers connect 5 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r1/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r1/zebra.conf new file mode 100644 index 0000000..b29940f --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r1/zebra.conf @@ -0,0 +1,4 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r2/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r2/bgpd.conf new file mode 100644 index 0000000..cdef611 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r2/bgpd.conf @@ -0,0 +1,27 @@ +router bgp 65002 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.7.7 remote-as external + neighbor 192.168.7.7 timers connect 5 + neighbor 192.168.2.3 remote-as external + neighbor 192.168.2.3 timers connect 5 + neighbor 192.168.2.3 weight 3 + neighbor 192.168.2.4 remote-as external + neighbor 192.168.2.4 timers connect 5 + neighbor 192.168.2.4 weight 4 + neighbor 192.168.2.5 remote-as external + neighbor 192.168.2.5 timers connect 5 + neighbor 192.168.2.5 weight 5 + neighbor 192.168.2.6 remote-as external + neighbor 192.168.2.6 timers connect 5 + neighbor 192.168.2.6 weight 6 + address-family ipv4 unicast + neighbor 192.168.1.1 addpath-tx-best-selected 1 + neighbor 192.168.1.1 prefix-list announce out + neighbor 192.168.7.7 addpath-tx-best-selected 2 + neighbor 192.168.7.7 prefix-list announce out + exit-address-family +! +ip prefix-list announce seq 5 permit 172.16.16.254/32 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r2/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r2/zebra.conf new file mode 100644 index 0000000..90587d2 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r2/zebra.conf @@ -0,0 +1,10 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! +int r2-eth1 + ip address 192.168.2.2/24 +! +int r2-eth2 + ip address 192.168.7.2/24 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r3/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r3/bgpd.conf new file mode 100644 index 0000000..98eb2e1 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r3/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65003 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers connect 5 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_addpath_best_selected/r3/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r3/zebra.conf new file mode 100644 index 0000000..417a484 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r3/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.16.254/32 +! +int r3-eth0 + ip address 192.168.2.3/24 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r4/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r4/bgpd.conf new file mode 100644 index 0000000..68245c4 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r4/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65004 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers connect 5 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_addpath_best_selected/r4/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r4/zebra.conf new file mode 100644 index 0000000..241e386 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r4/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.16.254/32 +! +int r4-eth0 + ip address 192.168.2.4/24 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r5/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r5/bgpd.conf new file mode 100644 index 0000000..f36e2bd --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r5/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65005 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers connect 5 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_addpath_best_selected/r5/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r5/zebra.conf new file mode 100644 index 0000000..203d229 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r5/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.16.254/32 +! +int r5-eth0 + ip address 192.168.2.5/24 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r6/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r6/bgpd.conf new file mode 100644 index 0000000..0d83ef8 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r6/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65006 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers connect 5 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_addpath_best_selected/r6/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r6/zebra.conf new file mode 100644 index 0000000..894dd30 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r6/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.16.254/32 +! +int r6-eth0 + ip address 192.168.2.6/24 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r7/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r7/bgpd.conf new file mode 100644 index 0000000..090846a --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r7/bgpd.conf @@ -0,0 +1,7 @@ +! +router bgp 65007 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.7.2 remote-as external + neighbor 192.168.7.2 timers connect 5 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r7/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r7/zebra.conf new file mode 100644 index 0000000..55c70ba --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r7/zebra.conf @@ -0,0 +1,4 @@ +! +int r7-eth0 + ip address 192.168.7.7/24 +! diff --git a/tests/topotests/bgp_addpath_best_selected/test_bgp_addpath_best_selected.py b/tests/topotests/bgp_addpath_best_selected/test_bgp_addpath_best_selected.py new file mode 100644 index 0000000..2a610c9 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/test_bgp_addpath_best_selected.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if Add-Path best selected paths are announced per neighbor. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 8): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["r5"]) + switch.add_link(tgen.gears["r6"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r7"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_addpath_best_selected(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast 172.16.16.254/32 json")) + expected = { + "paths": [ + { + "aspath": { + "string": "65006", + }, + "weight": 6, + }, + { + "aspath": { + "string": "65005", + }, + "weight": 5, + }, + { + "aspath": { + "string": "65004", + }, + "weight": 4, + }, + { + "aspath": { + "string": "65003", + }, + "weight": 3, + }, + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't converge initially" + + def check_bgp_advertised_routes_to_r1(): + output = json.loads( + r2.vtysh_cmd( + "show bgp ipv4 neighbors 192.168.1.1 advertised-routes detail json" + ) + ) + expected = { + "advertisedRoutes": { + "172.16.16.254/32": { + "paths": [ + { + "aspath": { + "string": "65005", + } + }, + { + "aspath": { + "string": "65006", + } + }, + ] + } + }, + "totalPrefixCounter": 2, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(check_bgp_advertised_routes_to_r1) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "Received more/less Add-Path best paths, but should be only 1+1 (real best path)" + + def check_bgp_advertised_routes_to_r7(): + output = json.loads( + r2.vtysh_cmd( + "show bgp ipv4 neighbors 192.168.7.7 advertised-routes detail json" + ) + ) + expected = { + "advertisedRoutes": { + "172.16.16.254/32": { + "paths": [ + { + "aspath": { + "string": "65004", + } + }, + { + "aspath": { + "string": "65005", + } + }, + { + "aspath": { + "string": "65006", + } + }, + ] + } + }, + "totalPrefixCounter": 3, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(check_bgp_advertised_routes_to_r7) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "Received more/less Add-Path best paths, but should be only 2+1 (real best path)" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_aggregate_address_matching_med/__init__.py b/tests/topotests/bgp_aggregate_address_matching_med/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_aggregate_address_matching_med/r1/bgpd.conf b/tests/topotests/bgp_aggregate_address_matching_med/r1/bgpd.conf new file mode 100644 index 0000000..3559760 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_matching_med/r1/bgpd.conf @@ -0,0 +1,21 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + neighbor 192.168.1.2 route-map r2 out + exit-address-family +! +ip prefix-list p1 seq 5 permit 172.16.255.1/32 +ip prefix-list p1 seq 10 permit 172.16.255.2/32 +ip prefix-list p2 seq 15 permit 172.16.255.3/32 +! +route-map r2 permit 10 + match ip address prefix-list p1 + set metric 300 +route-map r2 permit 20 + match ip address prefix-list p2 + set metric 400 +! diff --git a/tests/topotests/bgp_aggregate_address_matching_med/r1/zebra.conf b/tests/topotests/bgp_aggregate_address_matching_med/r1/zebra.conf new file mode 100644 index 0000000..685adb3 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_matching_med/r1/zebra.conf @@ -0,0 +1,11 @@ +! +interface lo + ip address 172.16.255.1/32 + ip address 172.16.255.2/32 + ip address 172.16.255.3/32 +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_aggregate_address_matching_med/r2/bgpd.conf b/tests/topotests/bgp_aggregate_address_matching_med/r2/bgpd.conf new file mode 100644 index 0000000..9bc9a31 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_matching_med/r2/bgpd.conf @@ -0,0 +1,11 @@ +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 3 10 + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 3 10 + address-family ipv4 unicast + aggregate-address 172.16.255.0/24 summary-only matching-MED-only + exit-address-family +! diff --git a/tests/topotests/bgp_aggregate_address_matching_med/r2/zebra.conf b/tests/topotests/bgp_aggregate_address_matching_med/r2/zebra.conf new file mode 100644 index 0000000..f229954 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_matching_med/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface r2-eth0 + ip address 192.168.1.2/24 +! +interface r2-eth1 + ip address 192.168.2.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_aggregate_address_matching_med/r3/bgpd.conf b/tests/topotests/bgp_aggregate_address_matching_med/r3/bgpd.conf new file mode 100644 index 0000000..dfb5ac7 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_matching_med/r3/bgpd.conf @@ -0,0 +1,6 @@ +! +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 3 10 +! diff --git a/tests/topotests/bgp_aggregate_address_matching_med/r3/zebra.conf b/tests/topotests/bgp_aggregate_address_matching_med/r3/zebra.conf new file mode 100644 index 0000000..11e06d4 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_matching_med/r3/zebra.conf @@ -0,0 +1,6 @@ +! +interface r3-eth0 + ip address 192.168.2.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_aggregate_address_matching_med/test_bgp_aggregate_address_matching_med.py b/tests/topotests/bgp_aggregate_address_matching_med/test_bgp_aggregate_address_matching_med.py new file mode 100644 index 0000000..5a4a5fb --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_matching_med/test_bgp_aggregate_address_matching_med.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Test if aggregate-address command works fine when suppressing summary-only +and using matching-MED-only together. +""" + +import os +import sys +import json +import pytest +import functools +from lib.common_config import ( + step, +) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + + +def build_topo(tgen): + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_aggregate_address_matching_med(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r3 = tgen.gears["r3"] + + def _bgp_converge(): + output = json.loads(r3.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "172.16.255.0/24": None, + "172.16.255.1/32": [{"path": "65002 65001"}], + "172.16.255.2/32": [{"path": "65002 65001"}], + "172.16.255.3/32": [{"path": "65002 65001"}], + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed to see unsuppressed routes from R2" + + step("Change MED for 172.16.255.3/32 from 400 to 300") + r1.vtysh_cmd( + """ + configure terminal + route-map r2 permit 20 + set metric 300 + """ + ) + + step("Check if 172.16.255.0/24 aggregated route was created and others suppressed") + + def _bgp_aggregated_summary_only_med_match(): + output = json.loads(r3.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "172.16.255.0/24": [{"path": "65002"}], + "172.16.255.1/32": None, + "172.16.255.2/32": None, + "172.16.255.3/32": None, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_aggregated_summary_only_med_match) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed to see unsuppressed routes from R2" + + step("Change MED for 172.16.255.3/32 back to 400 from 300") + r1.vtysh_cmd( + """ + configure terminal + route-map r2 permit 20 + set metric 400 + """ + ) + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed to see unsuppressed routes from R2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_aggregate_address_origin/__init__.py b/tests/topotests/bgp_aggregate_address_origin/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_aggregate_address_origin/r1/bgpd.conf b/tests/topotests/bgp_aggregate_address_origin/r1/bgpd.conf new file mode 100644 index 0000000..3486c64 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_origin/r1/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + aggregate-address 172.16.255.0/24 origin igp + exit-address-family +! diff --git a/tests/topotests/bgp_aggregate_address_origin/r1/zebra.conf b/tests/topotests/bgp_aggregate_address_origin/r1/zebra.conf new file mode 100644 index 0000000..0a283c0 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_origin/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_aggregate_address_origin/r2/bgpd.conf b/tests/topotests/bgp_aggregate_address_origin/r2/bgpd.conf new file mode 100644 index 0000000..b2d9455 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_origin/r2/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + exit-address-family +! diff --git a/tests/topotests/bgp_aggregate_address_origin/r2/zebra.conf b/tests/topotests/bgp_aggregate_address_origin/r2/zebra.conf new file mode 100644 index 0000000..606c17b --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_origin/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_aggregate_address_origin/test_bgp_aggregate-address_origin.py b/tests/topotests/bgp_aggregate_address_origin/test_bgp_aggregate-address_origin.py new file mode 100644 index 0000000..739685d --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_origin/test_bgp_aggregate-address_origin.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# bgp_aggregate-address_origin.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Donatas Abraitis +# + +""" +bgp_aggregate-address_origin.py: + +Test if works the following commands: +router bgp 65031 + address-family ipv4 unicast + aggregate-address 192.168.255.0/24 origin igp +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_aggregate_address_origin(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 3}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_aggregate_address_has_metric(router): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.0/24 json")) + expected = {"paths": [{"origin": "IGP"}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_aggregate_address_has_metric, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert ( + result is None + ), 'Failed to see applied ORIGIN (igp) for aggregated prefix in "{}"'.format(router) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_aggregate_address_route_map/__init__.py b/tests/topotests/bgp_aggregate_address_route_map/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_aggregate_address_route_map/r1/bgpd.conf b/tests/topotests/bgp_aggregate_address_route_map/r1/bgpd.conf new file mode 100644 index 0000000..7fb55cf --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_route_map/r1/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + aggregate-address 172.16.255.0/24 route-map aggr-rmap + exit-address-family +! +route-map aggr-rmap permit 10 + set metric 123 +! diff --git a/tests/topotests/bgp_aggregate_address_route_map/r1/zebra.conf b/tests/topotests/bgp_aggregate_address_route_map/r1/zebra.conf new file mode 100644 index 0000000..0a283c0 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_route_map/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_aggregate_address_route_map/r2/bgpd.conf b/tests/topotests/bgp_aggregate_address_route_map/r2/bgpd.conf new file mode 100644 index 0000000..b2d9455 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_route_map/r2/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + exit-address-family +! diff --git a/tests/topotests/bgp_aggregate_address_route_map/r2/zebra.conf b/tests/topotests/bgp_aggregate_address_route_map/r2/zebra.conf new file mode 100644 index 0000000..606c17b --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_route_map/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py b/tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py new file mode 100644 index 0000000..cec0692 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# bgp_aggregate-address_route-map.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +bgp_aggregate-address_route-map.py: + +Test if works the following commands: +router bgp 65031 + address-family ipv4 unicast + aggregate-address 192.168.255.0/24 route-map aggr-rmap + +route-map aggr-rmap permit 10 + set metric 123 +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_maximum_prefix_invalid(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 3}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_aggregate_address_has_metric(router): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.0/24 json")) + expected = {"paths": [{"metric": 123}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_aggregate_address_has_metric, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert ( + result is None + ), 'Failed to see applied metric for aggregated prefix in "{}"'.format(router) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_aggregate_address_topo1/__init__.py b/tests/topotests/bgp_aggregate_address_topo1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_aggregate_address_topo1/exabgp.env b/tests/topotests/bgp_aggregate_address_topo1/exabgp.env new file mode 100644 index 0000000..28e6423 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_topo1/exabgp.env @@ -0,0 +1,53 @@ +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' +##daemonize = false + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_aggregate_address_topo1/peer1/exabgp.cfg b/tests/topotests/bgp_aggregate_address_topo1/peer1/exabgp.cfg new file mode 100644 index 0000000..e0f6ab6 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_topo1/peer1/exabgp.cfg @@ -0,0 +1,21 @@ +neighbor 10.0.0.1 { + router-id 10.254.254.3; + local-address 10.0.0.2; + local-as 65001; + peer-as 65000; + static { + route 10.254.254.3/32 next-hop 10.0.0.2; + + route 192.168.0.1/32 next-hop 10.0.0.2 med 10; + route 192.168.0.2/32 next-hop 10.0.0.2 med 10; + route 192.168.0.3/32 next-hop 10.0.0.2 med 10; + + route 192.168.1.1/32 next-hop 10.0.0.2 med 10; + route 192.168.1.2/32 next-hop 10.0.0.2 med 10; + route 192.168.1.3/32 next-hop 10.0.0.2 med 20; + + route 192.168.2.1/32 next-hop 10.0.0.2; + route 192.168.2.2/32 next-hop 10.0.0.2; + route 192.168.2.3/32 next-hop 10.0.0.2; + } +} diff --git a/tests/topotests/bgp_aggregate_address_topo1/r1/bgpd.conf b/tests/topotests/bgp_aggregate_address_topo1/r1/bgpd.conf new file mode 100644 index 0000000..c7cf4a5 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_topo1/r1/bgpd.conf @@ -0,0 +1,30 @@ +! debug bgp updates +! +access-list acl-sup-one seq 5 permit 192.168.2.1/32 +access-list acl-sup-one seq 10 deny any +! +access-list acl-sup-two seq 5 permit 192.168.2.2/32 +access-list acl-sup-two seq 10 deny any +! +access-list acl-sup-three seq 5 permit 192.168.2.3/32 +access-list acl-sup-three seq 10 deny any +! +route-map rm-sup-one permit 10 + match ip address acl-sup-one +! +route-map rm-sup-two permit 10 + match ip address acl-sup-two +! +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 10.0.0.2 remote-as 65001 + neighbor 10.0.0.2 timers 3 10 + neighbor 10.0.1.2 remote-as internal + neighbor 10.0.1.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + aggregate-address 192.168.0.0/24 matching-MED-only + aggregate-address 192.168.1.0/24 matching-MED-only + aggregate-address 192.168.2.0/24 suppress-map rm-sup-one + exit-address-family +! diff --git a/tests/topotests/bgp_aggregate_address_topo1/r1/zebra.conf b/tests/topotests/bgp_aggregate_address_topo1/r1/zebra.conf new file mode 100644 index 0000000..931c73d --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_topo1/r1/zebra.conf @@ -0,0 +1,13 @@ +! +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ip address 10.0.0.1/24 +! +interface r1-eth1 + ip address 10.0.1.1/24 +! +ip forwarding +ipv6 forwarding +! diff --git a/tests/topotests/bgp_aggregate_address_topo1/r2/bgpd.conf b/tests/topotests/bgp_aggregate_address_topo1/r2/bgpd.conf new file mode 100644 index 0000000..acacd86 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_topo1/r2/bgpd.conf @@ -0,0 +1,7 @@ +router bgp 65000 + neighbor 10.0.1.1 remote-as internal + neighbor 10.0.1.1 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_aggregate_address_topo1/r2/zebra.conf b/tests/topotests/bgp_aggregate_address_topo1/r2/zebra.conf new file mode 100644 index 0000000..38e0c44 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_topo1/r2/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ip address 10.0.1.2/24 +! +ip forwarding +ipv6 forwarding +! diff --git a/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py b/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py new file mode 100644 index 0000000..370d01e --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py @@ -0,0 +1,279 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_aggregate_address_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +Test BGP aggregate address features. +""" + +import os +import sys +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + r1 = tgen.add_router("r1") + r2 = tgen.add_router("r2") + peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.2", defaultRoute="via 10.0.0.1") + + switch = tgen.add_switch("s1") + switch.add_link(r1) + switch.add_link(peer1) + + switch = tgen.add_switch("s2") + switch.add_link(r1) + switch.add_link(r2) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router = tgen.gears["r1"] + router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf")) + router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf")) + router.start() + + router = tgen.gears["r2"] + router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r2/zebra.conf")) + router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r2/bgpd.conf")) + router.start() + + peer = tgen.gears["peer1"] + peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env")) + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def expect_route(router_name, routes_expected): + "Helper function to avoid repeated code." + tgen = get_topogen() + test_func = functools.partial( + topotest.router_json_cmp, + tgen.gears[router_name], + "show ip route json", + routes_expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=120, wait=1) + assertmsg = '"{}" BGP convergence failure'.format(router_name) + assert result is None, assertmsg + + +def test_expect_convergence(): + "Test that BGP protocol converged." + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + def expect_loopback_route(router, iptype, route, proto): + "Wait until route is present on RIB for protocol." + logger.info("waiting route {} in {}".format(route, router)) + test_func = functools.partial( + topotest.router_json_cmp, + tgen.gears[router], + "show {} route json".format(iptype), + {route: [{"protocol": proto}]}, + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" BGP convergence failure'.format(router) + assert result is None, assertmsg + + expect_loopback_route("r2", "ip", "10.254.254.1/32", "bgp") + expect_loopback_route("r2", "ip", "10.254.254.3/32", "bgp") + + +def test_bgp_aggregate_address_matching_med_only(): + "Test that the command matching-MED-only works." + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + routes_expected = { + # All MED matches, aggregation must exist. + "192.168.0.0/24": [{"protocol": "bgp", "metric": 0}], + "192.168.0.1/32": [{"protocol": "bgp", "metric": 10}], + "192.168.0.2/32": [{"protocol": "bgp", "metric": 10}], + "192.168.0.3/32": [{"protocol": "bgp", "metric": 10}], + # Non matching MED: aggregation must not exist. + "192.168.1.0/24": None, + "192.168.1.1/32": [{"protocol": "bgp", "metric": 10}], + "192.168.1.2/32": [{"protocol": "bgp", "metric": 10}], + "192.168.1.3/32": [{"protocol": "bgp", "metric": 20}], + } + + test_func = functools.partial( + topotest.router_json_cmp, + tgen.gears["r2"], + "show ip route json", + routes_expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assertmsg = '"r2" BGP convergence failure' + assert result is None, assertmsg + + +def test_bgp_aggregate_address_match_and_suppress(): + "Test that the command matching-MED-only with suppression works." + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r1"].vtysh_multicmd( + """ +configure terminal +router bgp 65000 +address-family ipv4 unicast +no aggregate-address 192.168.0.0/24 matching-MED-only +no aggregate-address 192.168.1.0/24 matching-MED-only +aggregate-address 192.168.0.0/24 matching-MED-only summary-only +aggregate-address 192.168.1.0/24 matching-MED-only summary-only +""" + ) + + routes_expected = { + # All MED matches, aggregation must exist. + "192.168.0.0/24": [{"protocol": "bgp", "metric": 0}], + "192.168.0.1/32": None, + "192.168.0.2/32": None, + "192.168.0.3/32": None, + # Non matching MED: aggregation must not exist. + "192.168.1.0/24": None, + "192.168.1.1/32": [{"protocol": "bgp", "metric": 10}], + "192.168.1.2/32": [{"protocol": "bgp", "metric": 10}], + "192.168.1.3/32": [{"protocol": "bgp", "metric": 20}], + } + + test_func = functools.partial( + topotest.router_json_cmp, + tgen.gears["r2"], + "show ip route json", + routes_expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=120, wait=1) + assertmsg = '"r2" BGP convergence failure' + assert result is None, assertmsg + + +def test_bgp_aggregate_address_suppress_map(): + "Test that the command suppress-map works." + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + expect_route( + "r2", + { + "192.168.2.0/24": [{"protocol": "bgp"}], + "192.168.2.1/32": None, + "192.168.2.2/32": [{"protocol": "bgp"}], + "192.168.2.3/32": [{"protocol": "bgp"}], + }, + ) + + # Change route map and test again. + tgen.gears["r1"].vtysh_multicmd( + """ +configure terminal +router bgp 65000 +address-family ipv4 unicast +no aggregate-address 192.168.2.0/24 suppress-map rm-sup-one +aggregate-address 192.168.2.0/24 suppress-map rm-sup-two +""" + ) + + expect_route( + "r2", + { + "192.168.2.0/24": [{"protocol": "bgp"}], + "192.168.2.1/32": [{"protocol": "bgp"}], + "192.168.2.2/32": None, + "192.168.2.3/32": [{"protocol": "bgp"}], + }, + ) + + +def test_bgp_aggregate_address_suppress_map_update_route_map(): + "Test that the suppress-map late route map creation works." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r1"].vtysh_multicmd( + """ +configure terminal +router bgp 65000 +address-family ipv4 unicast +no aggregate-address 192.168.2.0/24 suppress-map rm-sup-two +aggregate-address 192.168.2.0/24 suppress-map rm-sup-three +""" + ) + + expect_route( + "r2", + { + "192.168.2.0/24": [{"protocol": "bgp"}], + "192.168.2.1/32": [{"protocol": "bgp"}], + "192.168.2.2/32": [{"protocol": "bgp"}], + "192.168.2.3/32": [{"protocol": "bgp"}], + }, + ) + + # Create missing route map and test again. + tgen.gears["r1"].vtysh_multicmd( + """ +configure terminal +route-map rm-sup-three permit 10 +match ip address acl-sup-three +""" + ) + + expect_route( + "r2", + { + "192.168.2.0/24": [{"protocol": "bgp"}], + "192.168.2.1/32": [{"protocol": "bgp"}], + "192.168.2.2/32": [{"protocol": "bgp"}], + "192.168.2.3/32": None, + }, + ) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_aggregator_zero/__init__.py b/tests/topotests/bgp_aggregator_zero/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_aggregator_zero/exabgp.env b/tests/topotests/bgp_aggregator_zero/exabgp.env new file mode 100644 index 0000000..28e6423 --- /dev/null +++ b/tests/topotests/bgp_aggregator_zero/exabgp.env @@ -0,0 +1,53 @@ +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' +##daemonize = false + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_aggregator_zero/peer1/exabgp.cfg b/tests/topotests/bgp_aggregator_zero/peer1/exabgp.cfg new file mode 100644 index 0000000..b3f2527 --- /dev/null +++ b/tests/topotests/bgp_aggregator_zero/peer1/exabgp.cfg @@ -0,0 +1,18 @@ +neighbor 10.0.0.1 { + router-id 10.0.0.2; + local-address 10.0.0.2; + local-as 65001; + peer-as 65534; + + static { + route 192.168.100.101/32 { + aggregator (0:10.0.0.2); + next-hop 10.0.0.2; + } + + route 192.168.100.102/32 { + aggregator (65001:10.0.0.2); + next-hop 10.0.0.2; + } + } +} diff --git a/tests/topotests/bgp_aggregator_zero/r1/bgpd.conf b/tests/topotests/bgp_aggregator_zero/r1/bgpd.conf new file mode 100644 index 0000000..002a5c7 --- /dev/null +++ b/tests/topotests/bgp_aggregator_zero/r1/bgpd.conf @@ -0,0 +1,6 @@ +! +router bgp 65534 + no bgp ebgp-requires-policy + neighbor 10.0.0.2 remote-as external + neighbor 10.0.0.2 timers 3 10 +! diff --git a/tests/topotests/bgp_aggregator_zero/r1/zebra.conf b/tests/topotests/bgp_aggregator_zero/r1/zebra.conf new file mode 100644 index 0000000..22a26ac --- /dev/null +++ b/tests/topotests/bgp_aggregator_zero/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 10.0.0.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_aggregator_zero/test_bgp_aggregator_zero.py b/tests/topotests/bgp_aggregator_zero/test_bgp_aggregator_zero.py new file mode 100644 index 0000000..d9ef3e1 --- /dev/null +++ b/tests/topotests/bgp_aggregator_zero/test_bgp_aggregator_zero.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by +# Donatas Abraitis +# + +""" +Test if BGP UPDATE with AGGREGATOR AS attribute with value zero (0) +is continued to be processed, but AGGREGATOR attribute is discarded. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + r1 = tgen.add_router("r1") + peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.2", defaultRoute="via 10.0.0.1") + + switch = tgen.add_switch("s1") + switch.add_link(r1) + switch.add_link(peer1) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router = tgen.gears["r1"] + router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf")) + router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf")) + router.start() + + peer = tgen.gears["peer1"] + peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env")) + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_aggregator_zero(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show ip bgp neighbor 10.0.0.2 json") + ) + expected = { + "10.0.0.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r1"]) + + def _bgp_has_correct_aggregator_route_with_asn_0(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show ip bgp 192.168.100.101/32 json") + ) + + if "aggregatorAs" in output["paths"][0].keys(): + return False + else: + return True + + assert ( + _bgp_has_correct_aggregator_route_with_asn_0() is True + ), 'Aggregator AS attribute with ASN 0 found in "{}"'.format(tgen.gears["r1"]) + + def _bgp_has_correct_aggregator_route_with_good_asn(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show ip bgp 192.168.100.102/32 json") + ) + expected = {"paths": [{"aggregatorAs": 65001, "aggregatorId": "10.0.0.2"}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_has_correct_aggregator_route_with_good_asn) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Aggregator AS attribute not found in "{}"'.format( + tgen.gears["r1"] + ) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_aigp/__init__.py b/tests/topotests/bgp_aigp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_aigp/r1/bgpd.conf b/tests/topotests/bgp_aigp/r1/bgpd.conf new file mode 100644 index 0000000..74a0215 --- /dev/null +++ b/tests/topotests/bgp_aigp/r1/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.0.2 remote-as internal + neighbor 10.0.0.2 update-source lo + neighbor 10.0.0.2 timers 1 3 + neighbor 10.0.0.2 timers connect 1 + neighbor 10.0.0.3 remote-as internal + neighbor 10.0.0.3 timers 1 3 + neighbor 10.0.0.3 timers connect 1 + neighbor 10.0.0.3 update-source lo +! diff --git a/tests/topotests/bgp_aigp/r1/ospfd.conf b/tests/topotests/bgp_aigp/r1/ospfd.conf new file mode 100644 index 0000000..38aa11d --- /dev/null +++ b/tests/topotests/bgp_aigp/r1/ospfd.conf @@ -0,0 +1,17 @@ +! +interface lo + ip ospf cost 10 +! +interface r1-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 10 +! +interface r1-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 30 +! +router ospf + router-id 10.0.0.1 + network 0.0.0.0/0 area 0 diff --git a/tests/topotests/bgp_aigp/r1/zebra.conf b/tests/topotests/bgp_aigp/r1/zebra.conf new file mode 100644 index 0000000..0ed22d3 --- /dev/null +++ b/tests/topotests/bgp_aigp/r1/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 10.0.0.1/32 +! +interface r1-eth0 + ip address 192.168.12.1/24 +! +interface r1-eth1 + ip address 192.168.13.1/24 +! diff --git a/tests/topotests/bgp_aigp/r2/bgpd.conf b/tests/topotests/bgp_aigp/r2/bgpd.conf new file mode 100644 index 0000000..4db4687 --- /dev/null +++ b/tests/topotests/bgp_aigp/r2/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.0.1 remote-as internal + neighbor 10.0.0.1 timers 1 3 + neighbor 10.0.0.1 timers connect 1 + neighbor 10.0.0.1 route-reflector-client + neighbor 192.168.24.4 remote-as internal + neighbor 192.168.24.4 timers 1 3 + neighbor 192.168.24.4 timers connect 1 + neighbor 192.168.24.4 aigp + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_aigp/r2/ospfd.conf b/tests/topotests/bgp_aigp/r2/ospfd.conf new file mode 100644 index 0000000..ed31941 --- /dev/null +++ b/tests/topotests/bgp_aigp/r2/ospfd.conf @@ -0,0 +1,13 @@ +! +interface lo + ip ospf cost 10 +! +interface r2-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 10 +! +router ospf + router-id 10.0.0.2 + network 192.168.12.0/24 area 0 + network 10.0.0.2/32 area 0 diff --git a/tests/topotests/bgp_aigp/r2/zebra.conf b/tests/topotests/bgp_aigp/r2/zebra.conf new file mode 100644 index 0000000..6d6a557 --- /dev/null +++ b/tests/topotests/bgp_aigp/r2/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 10.0.0.2/32 +! +interface r2-eth0 + ip address 192.168.12.2/24 +! +interface r2-eth1 + ip address 192.168.24.2/24 +! diff --git a/tests/topotests/bgp_aigp/r3/bgpd.conf b/tests/topotests/bgp_aigp/r3/bgpd.conf new file mode 100644 index 0000000..5ab712e --- /dev/null +++ b/tests/topotests/bgp_aigp/r3/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.0.1 remote-as internal + neighbor 10.0.0.1 timers 1 3 + neighbor 10.0.0.1 timers connect 1 + neighbor 10.0.0.1 route-reflector-client + neighbor 192.168.35.5 remote-as internal + neighbor 192.168.35.5 timers 1 3 + neighbor 192.168.35.5 timers connect 1 + neighbor 192.168.35.5 aigp + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_aigp/r3/ospfd.conf b/tests/topotests/bgp_aigp/r3/ospfd.conf new file mode 100644 index 0000000..f971ad6 --- /dev/null +++ b/tests/topotests/bgp_aigp/r3/ospfd.conf @@ -0,0 +1,13 @@ +! +interface lo + ip ospf cost 10 +! +interface r3-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 30 +! +router ospf + router-id 10.0.0.3 + network 192.168.13.0/24 area 0 + network 10.0.0.3/32 area 0 diff --git a/tests/topotests/bgp_aigp/r3/zebra.conf b/tests/topotests/bgp_aigp/r3/zebra.conf new file mode 100644 index 0000000..82c87d5 --- /dev/null +++ b/tests/topotests/bgp_aigp/r3/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 10.0.0.3/32 +! +interface r3-eth0 + ip address 192.168.13.3/24 +! +interface r3-eth1 + ip address 192.168.35.3/24 +! diff --git a/tests/topotests/bgp_aigp/r4/bgpd.conf b/tests/topotests/bgp_aigp/r4/bgpd.conf new file mode 100644 index 0000000..aa88bac --- /dev/null +++ b/tests/topotests/bgp_aigp/r4/bgpd.conf @@ -0,0 +1,17 @@ +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.24.2 remote-as internal + neighbor 192.168.24.2 timers 1 3 + neighbor 192.168.24.2 timers connect 1 + neighbor 192.168.24.2 aigp + neighbor 192.168.24.2 route-reflector-client + neighbor 10.0.0.6 remote-as internal + neighbor 10.0.0.6 timers 1 3 + neighbor 10.0.0.6 timers connect 1 + neighbor 10.0.0.6 update-source lo + address-family ipv4 + redistribute connected + redistribute ospf + exit-address-family +! diff --git a/tests/topotests/bgp_aigp/r4/ospfd.conf b/tests/topotests/bgp_aigp/r4/ospfd.conf new file mode 100644 index 0000000..c9e6796 --- /dev/null +++ b/tests/topotests/bgp_aigp/r4/ospfd.conf @@ -0,0 +1,13 @@ +! +interface lo + ip ospf cost 10 +! +interface r4-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 20 +! +router ospf + router-id 10.0.0.4 + network 192.168.46.0/24 area 0 + network 10.0.0.4/32 area 0 diff --git a/tests/topotests/bgp_aigp/r4/zebra.conf b/tests/topotests/bgp_aigp/r4/zebra.conf new file mode 100644 index 0000000..5f544e8 --- /dev/null +++ b/tests/topotests/bgp_aigp/r4/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 10.0.0.4/32 +! +interface r4-eth0 + ip address 192.168.24.4/24 +! +interface r4-eth1 + ip address 192.168.46.4/24 +! diff --git a/tests/topotests/bgp_aigp/r5/bgpd.conf b/tests/topotests/bgp_aigp/r5/bgpd.conf new file mode 100644 index 0000000..4fde262 --- /dev/null +++ b/tests/topotests/bgp_aigp/r5/bgpd.conf @@ -0,0 +1,17 @@ +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.35.3 remote-as internal + neighbor 192.168.35.3 timers 1 3 + neighbor 192.168.35.3 timers connect 1 + neighbor 192.168.35.3 aigp + neighbor 192.168.35.3 route-reflector-client + neighbor 10.0.0.6 remote-as internal + neighbor 10.0.0.6 timers 1 3 + neighbor 10.0.0.6 timers connect 1 + neighbor 10.0.0.6 update-source lo + address-family ipv4 + redistribute connected + redistribute ospf + exit-address-family +! diff --git a/tests/topotests/bgp_aigp/r5/ospfd.conf b/tests/topotests/bgp_aigp/r5/ospfd.conf new file mode 100644 index 0000000..b853c74 --- /dev/null +++ b/tests/topotests/bgp_aigp/r5/ospfd.conf @@ -0,0 +1,13 @@ +! +interface lo + ip ospf cost 10 +! +interface r5-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 10 +! +router ospf + router-id 10.0.0.5 + network 192.168.56.0/24 area 0 + network 10.0.0.5/32 area 0 diff --git a/tests/topotests/bgp_aigp/r5/zebra.conf b/tests/topotests/bgp_aigp/r5/zebra.conf new file mode 100644 index 0000000..69b8bf2 --- /dev/null +++ b/tests/topotests/bgp_aigp/r5/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 10.0.0.5/32 +! +interface r5-eth0 + ip address 192.168.35.5/24 +! +interface r5-eth1 + ip address 192.168.56.5/24 +! diff --git a/tests/topotests/bgp_aigp/r6/bgpd.conf b/tests/topotests/bgp_aigp/r6/bgpd.conf new file mode 100644 index 0000000..2faae77 --- /dev/null +++ b/tests/topotests/bgp_aigp/r6/bgpd.conf @@ -0,0 +1,20 @@ +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.0.4 remote-as internal + neighbor 10.0.0.4 timers 1 3 + neighbor 10.0.0.4 timers connect 1 + neighbor 10.0.0.4 route-reflector-client + neighbor 10.0.0.4 update-source lo + neighbor 10.0.0.5 remote-as internal + neighbor 10.0.0.5 timers 1 3 + neighbor 10.0.0.5 timers connect 1 + neighbor 10.0.0.5 route-reflector-client + neighbor 10.0.0.5 update-source lo + neighbor 192.168.67.7 remote-as internal + neighbor 192.168.67.7 timers 1 3 + neighbor 192.168.67.7 timers connect 1 + address-family ipv4 + redistribute ospf + exit-address-family +! diff --git a/tests/topotests/bgp_aigp/r6/ospfd.conf b/tests/topotests/bgp_aigp/r6/ospfd.conf new file mode 100644 index 0000000..46b2933 --- /dev/null +++ b/tests/topotests/bgp_aigp/r6/ospfd.conf @@ -0,0 +1,17 @@ +! +interface lo + ip ospf cost 10 +! +interface r6-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 20 +! +interface r6-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 10 +! +router ospf + router-id 10.0.0.6 + network 0.0.0.0/0 area 0 diff --git a/tests/topotests/bgp_aigp/r6/zebra.conf b/tests/topotests/bgp_aigp/r6/zebra.conf new file mode 100644 index 0000000..f8ca5f8 --- /dev/null +++ b/tests/topotests/bgp_aigp/r6/zebra.conf @@ -0,0 +1,13 @@ +! +interface lo + ip address 10.0.0.6/32 +! +interface r6-eth0 + ip address 192.168.46.6/24 +! +interface r6-eth1 + ip address 192.168.56.6/24 +! +interface r6-eth2 + ip address 192.168.67.6/24 +! diff --git a/tests/topotests/bgp_aigp/r7/bgpd.conf b/tests/topotests/bgp_aigp/r7/bgpd.conf new file mode 100644 index 0000000..639dcfe --- /dev/null +++ b/tests/topotests/bgp_aigp/r7/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.67.6 remote-as internal + neighbor 192.168.67.6 timers 1 3 + neighbor 192.168.67.6 timers connect 1 + address-family ipv4 + redistribute connected route-map rmap metric 71 + exit-address-family +! +ip prefix-list p71 seq 5 permit 10.0.0.71/32 +ip prefix-list p72 seq 5 permit 10.0.0.72/32 +! +route-map rmap permit 10 + match ip address prefix-list p71 + set aigp igp-metric +! +route-map rmap permit 20 + match ip address prefix-list p72 + set aigp 72 +exit +! diff --git a/tests/topotests/bgp_aigp/r7/zebra.conf b/tests/topotests/bgp_aigp/r7/zebra.conf new file mode 100644 index 0000000..4c05df8 --- /dev/null +++ b/tests/topotests/bgp_aigp/r7/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 10.0.0.7/32 + ip address 10.0.0.71/32 + ip address 10.0.0.72/32 +! +interface r7-eth0 + ip address 192.168.67.7/24 +! diff --git a/tests/topotests/bgp_aigp/test_bgp_aigp.py b/tests/topotests/bgp_aigp/test_bgp_aigp.py new file mode 100644 index 0000000..655e9ad --- /dev/null +++ b/tests/topotests/bgp_aigp/test_bgp_aigp.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +r7 sets aigp-metric for 10.0.0.71/32 to 71, and 72 for 10.0.0.72/32. + +r6 receives those routes with aigp-metric TLV. + +r2 and r3 receives those routes with aigp-metric TLV increased by 20, +and 30 appropriately. + +r1 receives routes with aigp-metric TLV 111,131 and 112,132 appropriately. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 8): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r5"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["r6"]) + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r5"]) + switch.add_link(tgen.gears["r6"]) + + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["r6"]) + switch.add_link(tgen.gears["r7"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_aigp(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + r4 = tgen.gears["r4"] + r5 = tgen.gears["r5"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 10.0.0.71/32 json")) + expected = { + "paths": [ + { + "aigpMetric": 111, + "valid": True, + "nexthops": [{"hostname": "r3", "accessible": True}], + }, + { + "aigpMetric": 131, + "valid": True, + "bestpath": {"selectionReason": "Neighbor IP"}, + "nexthops": [{"hostname": "r2", "accessible": True}], + }, + ] + } + return topotest.json_cmp(output, expected) + + def _bgp_check_aigp_metric(router, prefix, aigp): + output = json.loads( + router.vtysh_cmd("show bgp ipv4 unicast {} json".format(prefix)) + ) + expected = {"paths": [{"aigpMetric": aigp, "valid": True}]} + return topotest.json_cmp(output, expected) + + def _bgp_check_aigp_metric_bestpath(): + output = json.loads( + r1.vtysh_cmd( + "show bgp ipv4 unicast 10.0.0.64/28 longer-prefixes json detail" + ) + ) + expected = { + "routes": { + "10.0.0.71/32": { + "paths": [ + { + "aigpMetric": 111, + "bestpath": {"selectionReason": "AIGP"}, + "valid": True, + "nexthops": [{"hostname": "r3", "accessible": True}], + }, + { + "aigpMetric": 131, + "valid": True, + "nexthops": [{"hostname": "r2", "accessible": True}], + }, + ], + }, + "10.0.0.72/32": { + "paths": [ + { + "aigpMetric": 112, + "bestpath": {"selectionReason": "AIGP"}, + "valid": True, + "nexthops": [{"hostname": "r3", "accessible": True}], + }, + { + "aigpMetric": 132, + "valid": True, + "nexthops": [{"hostname": "r2", "accessible": True}], + }, + ], + }, + } + } + return topotest.json_cmp(output, expected) + + # Initial converge, AIGP is not involved in best-path selection process + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "can't converge initially" + + # Enable `bgp bestpath aigp` + r1.vtysh_cmd( + """ + configure terminal + router bgp + bgp bestpath aigp + """ + ) + + # r4, 10.0.0.71/32 with aigp-metric 71 + test_func = functools.partial(_bgp_check_aigp_metric, r4, "10.0.0.71/32", 71) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "aigp-metric for 10.0.0.71/32 is not 71" + + # r5, 10.0.0.72/32 with aigp-metric 72 + test_func = functools.partial(_bgp_check_aigp_metric, r5, "10.0.0.72/32", 72) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "aigp-metric for 10.0.0.72/32 is not 72" + + # r2, 10.0.0.71/32 with aigp-metric 101 (71 + 30) + test_func = functools.partial(_bgp_check_aigp_metric, r2, "10.0.0.71/32", 101) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "aigp-metric for 10.0.0.71/32 is not 101" + + # r3, 10.0.0.72/32 with aigp-metric 92 (72 + 20) + test_func = functools.partial(_bgp_check_aigp_metric, r3, "10.0.0.72/32", 92) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "aigp-metric for 10.0.0.72/32 is not 92" + + # r1, check if AIGP is considered in best-path selection (lowest wins) + test_func = functools.partial(_bgp_check_aigp_metric_bestpath) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "AIGP attribute is not considered in best-path selection" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_always_compare_med/bgp_always_compare_med_topo1.json b/tests/topotests/bgp_always_compare_med/bgp_always_compare_med_topo1.json new file mode 100644 index 0000000..4156c6d --- /dev/null +++ b/tests/topotests/bgp_always_compare_med/bgp_always_compare_med_topo1.json @@ -0,0 +1,152 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "192.168.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start":{"ipv4":"192.168.0.0", "v4mask":24, "ipv6":"fd00::", "v6mask":64}, + "lo_prefix":{"ipv4":"1.0.", "v4mask":32, "ipv6":"2001:DB8:F::", "v6mask":128}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {}}}, + "r3": {"dest_link": {"r1": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }}}, + "r3": {"dest_link": {"r1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }}} + } + } + } + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + }, + "static_routes":[ + { + "network":"192.168.20.1/32", + "next_hop":"Null0" + }, + { + "network":"192:168:20::1/128", + "next_hop":"Null0" + }] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r4": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r4": {"dest_link": {"r2": {}}} + } + } + } + } + } + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r4": {}}}, + "r3": {"dest_link": {"r4": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r4": {}}}, + "r3": {"dest_link": {"r4": {}}} + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_always_compare_med/test_bgp_always_compare_med_topo1.py b/tests/topotests/bgp_always_compare_med/test_bgp_always_compare_med_topo1.py new file mode 100644 index 0000000..fb72f43 --- /dev/null +++ b/tests/topotests/bgp_always_compare_med/test_bgp_always_compare_med_topo1.py @@ -0,0 +1,1118 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2023 by VMware, Inc. ("VMware") +# +# +################################################################################ +# Following tests are performed to validate BGP always compare MED functionality +################################################################################ +""" +1. Verify the BGP always compare MED functionality in between eBGP Peers +2. Verify the BGP always compare MED functionality in between eBGP Peers with by changing different AD values +3. Verify the BGP always compare MED functionality in between eBGP Peers by changing MED values in middle routers +4. Verify that BGP Always compare MED functionality by restarting BGP, Zebra and FRR services and clear BGP and + shutdown BGP neighbor +5. Verify BGP always compare MED functionality by performing shut/noshut on the interfaces in between BGP neighbors +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + create_static_routes, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + check_address_types, + check_router_status, + create_static_routes, + create_prefix_lists, + create_route_maps, + kill_router_daemons, + shutdown_bringup_interface, + stop_router, + start_router, + delete_route_maps, +) + +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, verify_bgp_rib, create_router_bgp, clear_bgp +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Reading the data from JSON File for topology creation +topo = None + +# Global variables +ADDR_TYPES = check_address_types() +NETWORK1_1 = {"ipv4": "192.168.20.1/32", "ipv6": "192:168:20::1/128"} +NETWORK1_2 = {"ipv4": "192.168.30.1/32", "ipv6": "192:168:30::1/128"} +NETWORK1_3 = {"ipv4": "192.168.40.1/32", "ipv6": "192:168:40::1/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_always_compare_med_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +########################################################################################################## +# +# Local API +# +########################################################################################################## + + +def initial_configuration(tgen, tc_name): + """ + API to do initial set of configuration + """ + + step( + "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config" + ) + + step("Configure static routes in R4") + for addr_type in ADDR_TYPES: + input_static_r4 = { + "r4": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure redistribute static in R4") + input_static_redist_r4 = { + "r4": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + # Create prefix list + input_dict_23 = { + "r2": { + "prefix_lists": { + addr_type: { + "pf_ls_r2_{}".format(addr_type): [ + {"network": NETWORK1_1[addr_type], "action": "permit"} + ] + } + } + }, + "r3": { + "prefix_lists": { + "ipv4": { + "pf_ls_r3_{}".format(addr_type): [ + {"network": NETWORK1_1[addr_type], "action": "permit"} + ] + } + } + }, + } + result = create_prefix_lists(tgen, input_dict_23) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + input_dict_23 = { + "r2": { + "route_maps": { + "RMAP_MED_R2": [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_ls_r2_{}".format(addr_type) + } + }, + "set": {"med": 300}, + } + ] + } + }, + "r3": { + "route_maps": { + "RMAP_MED_R3": [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_ls_r3_{}".format(addr_type) + } + }, + "set": {"med": 200}, + } + ] + } + }, + } + result = create_route_maps(tgen, input_dict_23) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_r2_r3 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RMAP_MED_R2", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "RMAP_MED_R3", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_r2_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + +########################################################################################################## +# +# Testcases +# +########################################################################################################## + + +def test_verify_bgp_always_compare_med_functionality_bw_eBGP_peers_p0(request): + """ + Verify the BGP always compare MED functionality in between eBGP Peers + """ + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + tgen = get_topogen() + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + initial_configuration(tgen, tc_name) + + step( + "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config" + ) + step( + "Verify that IPv4 and IPv6 eBGP neighbors are configured in between routers by following " + "commands and verify that best path chosen by lowest MED value" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'multi-path as-path relax' command at R1.") + configure_bgp = { + "r1": {"bgp": {"local_as": "100", "bestpath": {"aspath": "multipath-relax"}}} + } + result = create_router_bgp(tgen, topo, configure_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'multi-path as-path relax' command, " + "its also chooses lowest MED to reach destination." + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh1 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + nh2 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=[nh1, nh2]) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'bgp always-compare-med' command at R1.") + input_dict_r1 = {"r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": True}}} + result = create_router_bgp(tgen, topo, input_dict_r1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'bgp always-compare-med', its chooses lowest MED value path" + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove 'bgp always-compare-med' command at R1.") + input_dict_r1 = { + "r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": False}} + } + result = create_router_bgp(tgen, topo, input_dict_r1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify that 'bgp always-compare-med' command is removed") + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh1 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + nh2 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=[nh1, nh2]) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove 'multi-path as-path relax' command at R1") + configure_bgp = { + "r1": { + "bgp": { + "local_as": "100", + "bestpath": {"aspath": "multipath-relax", "delete": True}, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify route selection after removing 'multi-path as-path relax' command") + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_always_compare_med_functionality_bw_eBGP_peers_by_changing_AD_values_p0( + request, +): + """ + Verify the BGP always compare MED functionality in between eBGP Peers with by changing different AD values. + """ + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + tgen = get_topogen() + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + initial_configuration(tgen, tc_name) + + step( + "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config" + ) + step( + "Verify that IPv4 and IPv6 eBGP neighbors are configured in between routers by following " + "commands and verify that best path chosen by lowest MED value" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'bgp always-compare-med' command at R1.") + input_dict_r1 = {"r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": True}}} + result = create_router_bgp(tgen, topo, input_dict_r1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'bgp always-compare-med', its chooses lowest MED value path" + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure AD value=100 at R2 and AD value=200 at R3 towards R1") + input_dict_1 = { + "r2": { + "bgp": { + "local_as": 200, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 100, "ibgp": 100, "local": 100} + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 100, "ibgp": 100, "local": 100} + } + }, + }, + } + }, + "r3": { + "bgp": { + "local_as": 300, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200} + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200} + } + }, + }, + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that inspite of AD values, always lowest MED value is getting " + "selected at destination router R1" + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_always_compare_med_functionality_bw_eBGP_peers_by_changing_MED_values_p1( + request, +): + """ + Verify the BGP always compare MED functionality in between eBGP Peers by changing MED values in middle routers + """ + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + tgen = get_topogen() + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + initial_configuration(tgen, tc_name) + + step( + "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config" + ) + step( + "Verify that IPv4 and IPv6 eBGP neighbors are configured in between routers by following " + "commands and verify that best path chosen by lowest MED value" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'multi-path as-path relax' command at R1.") + configure_bgp = { + "r1": {"bgp": {"local_as": "100", "bestpath": {"aspath": "multipath-relax"}}} + } + result = create_router_bgp(tgen, topo, configure_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'multi-path as-path relax' command, " + "its also chooses lowest MED to reach destination." + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh1 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + nh2 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=[nh1, nh2]) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'bgp always-compare-med' command at R1.") + input_dict_r1 = {"r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": True}}} + result = create_router_bgp(tgen, topo, input_dict_r1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'bgp always-compare-med', its chooses lowest MED value path" + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Change the MED value 150 in R2 router.") + input_dict = {"r2": {"route_maps": ["RMAP_MED_R2"]}} + result = delete_route_maps(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "route_maps": { + "RMAP_MED_R2": [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_ls_r2_{}".format(addr_type) + } + }, + "set": {"med": 150}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that after changing MED, its chooses lowest MED value path") + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Change the MED value 100 in R3 router.") + input_dict = {"r3": {"route_maps": ["RMAP_MED_R3"]}} + result = delete_route_maps(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "RMAP_MED_R3": [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_ls_r3_{}".format(addr_type) + } + }, + "set": {"med": 100}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that after changing MED, its chooses lowest MED value path") + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_always_compare_med_functionality_by_restarting_daemons_clear_bgp_shut_neighbors_p1( + request, +): + """ + Verify that BGP Always compare MED functionality by restarting BGP, Zebra and FRR services and clear BGP and shutdown BGP neighbor + """ + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + tgen = get_topogen() + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + initial_configuration(tgen, tc_name) + + step( + "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config" + ) + step( + "Verify that IPv4 and IPv6 eBGP neighbors are configured in between routers by following " + "commands and verify that best path chosen by lowest MED value" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'multi-path as-path relax' command at R1.") + configure_bgp = { + "r1": {"bgp": {"local_as": "100", "bestpath": {"aspath": "multipath-relax"}}} + } + result = create_router_bgp(tgen, topo, configure_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'multi-path as-path relax' command, " + "its also chooses lowest MED to reach destination." + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh1 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + nh2 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=[nh1, nh2]) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'bgp always-compare-med' command at R1.") + input_dict_r1 = {"r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": True}}} + result = create_router_bgp(tgen, topo, input_dict_r1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'bgp always-compare-med', its chooses lowest MED value path" + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Restart the BGPd/Zebra/FRR service on R1") + for daemon in ["bgpd", "zebra", "frr"]: + if daemon == "frr": + stop_router(tgen, "r1") + start_router(tgen, "r1") + else: + kill_router_daemons(tgen, "r1", daemon) + + step( + "Verify after restarting dameons and frr services, its chooses lowest MED value path" + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Clear bgp on R1") + clear_bgp(tgen, None, "r1") + + step("Verify after clearing BGP, its chooses lowest MED value path") + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Perform BGP neighborship shut/no shut") + for action, keyword in zip([True, False], ["shut", "noshut"]): + for addr_type in ADDR_TYPES: + input_dict = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": {"r1": {"shutdown": action}} + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify after {} BGP, its chooses lowest MED value path".format(keyword)) + if action: + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + else: + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_always_compare_med_functionality_by_shut_noshut_interfaces_bw_bgp_neighbors_p1( + request, +): + """ + Verify BGP always compare MED functionality by performing shut/noshut on the interfaces in between BGP neighbors + """ + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + tgen = get_topogen() + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + initial_configuration(tgen, tc_name) + + step( + "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config" + ) + step( + "Verify that IPv4 and IPv6 eBGP neighbors are configured in between routers by following " + "commands and verify that best path chosen by lowest MED value" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'multi-path as-path relax' command at R1.") + configure_bgp = { + "r1": {"bgp": {"local_as": "100", "bestpath": {"aspath": "multipath-relax"}}} + } + result = create_router_bgp(tgen, topo, configure_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'multi-path as-path relax' command, " + "its also chooses lowest MED to reach destination." + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh1 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + nh2 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=[nh1, nh2]) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'bgp always-compare-med' command at R1.") + input_dict_r1 = {"r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": True}}} + result = create_router_bgp(tgen, topo, input_dict_r1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'bgp always-compare-med', its chooses lowest MED value path" + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for action, keyword in zip([False, True], ["Shut", "No Shut"]): + step( + "{} the interface on the link between R3 & R4 and R2 & R4 routers".format( + keyword + ) + ) + intf2_4 = topo["routers"]["r2"]["links"]["r4"]["interface"] + intf3_4 = topo["routers"]["r3"]["links"]["r4"]["interface"] + for dut, intf in zip(["r2", "r3"], [intf2_4, intf3_4]): + shutdown_bringup_interface(tgen, dut, intf, action) + + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + if action: + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + else: + result = verify_bgp_rib( + tgen, addr_type, "r1", input_static_r1, expected=False + ) + assert ( + result is not True + ), "Testcase {} :Failed \n Routes are still present in BGP table\n Error {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, "r1", input_static_r1, next_hop=nh, expected=False + ) + assert ( + result is not True + ), "Testcase {} :Failed \n Routes are still present in FIB \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_as_allow_in/bgp_as_allow_in.json b/tests/topotests/bgp_as_allow_in/bgp_as_allow_in.json new file mode 100644 index 0000000..943876c --- /dev/null +++ b/tests/topotests/bgp_as_allow_in/bgp_as_allow_in.json @@ -0,0 +1,266 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r5": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "500", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py b/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py new file mode 100644 index 0000000..c49a2e5 --- /dev/null +++ b/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py @@ -0,0 +1,957 @@ +#!/usr/bin/python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test bgp allowas-in functionality: + +- Verify that routes coming from same AS are accepted only when + '"allowas-in" is configuerd. +- Verify that "allowas-in" feature works per address-family/VRF + 'basis and doesn't impact the other AFIs. +- Verify that the if number of occurrences of AS number in path is + 'more than the configured allowas-in value then we do not accept + 'the route. +- Verify that when we advertise a network, learned from the same AS + 'via allowas-in command, to an iBGP neighbor we see multiple + 'occurrences. +- Verify that when we advertise a network, learned from the same AS + 'via allowas-in command, to an eBGP neighbor we see multiple + 'occurrences of our own AS based on configured value+1. +""" + +import os +import sys +import time +import json +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + create_route_maps, + check_address_types, + step, + required_linux_kernel_version, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, +) +from lib.topojson import build_topo_from_json, build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = {"ipv4": "2.2.2.2/32", "ipv6": "22:22::2/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_as_allow_in.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global BGP_CONVERGENCE + global ADDR_TYPES + + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_bgp_allowas_in_p0(request): + """ + Verify that routes coming from same AS are accepted only when + "allowas-in" is configuerd. + + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Advertise prefix 2.2.2.2/32 from Router-1(AS-200).") + step("Advertise an ipv6 prefix 22:22::2/128 from Router-1(AS-200).") + # configure static routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_4 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + 'Check BGP table of router R3 using "sh bgp ipv4" and "sh bgp ' + 'ipv6" command.' + ) + step( + "We should not see prefix advertised from R1 in R3's BGP " + "table without allowas-in." + ) + logger.info("Verifying %s routes on r3, route should not be present", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=NEXT_HOP_IP[addr_type], + protocol=protocol, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n".format(tc_name) + + "Expected behavior: routes should not present in rib \n" + + "Error: {}".format(result) + ) + + step("Configure allowas-in on R3 for R2.") + step("We should see the prefix advertised from R1 in R3's BGP table.") + # Api call to enable allowas-in in bgp process. + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "allowas-in": {"number_occurences": 1} + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_bgp_allowas_in_per_addr_family_p0(request): + """ + Verify that "allowas-in" feature works per address-family/VRF + basis and doesn't impact the other AFIs. + + """ + + # This test is applicable only for dual stack. + if "ipv4" not in ADDR_TYPES or "ipv6" not in ADDR_TYPES: + pytest.skip("NOT APPLICABLE") + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Advertise prefix 2.2.2.2/32 from Router-1(AS-200).") + step("Advertise an ipv6 prefix 22:22::2/128 from Router-1(AS-200).") + # configure static routes routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_4 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure allowas-in on R3 for R2 under IPv4 addr-family only") + # Api call to enable allowas-in in bgp process. + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"allowas-in": {"number_occurences": 1}} + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + static_route_ipv4 = { + "r1": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": NEXT_HOP_IP["ipv4"]} + ] + } + } + + static_route_ipv6 = { + "r1": { + "static_routes": [ + {"network": NETWORK["ipv6"], "next_hop": NEXT_HOP_IP["ipv6"]} + ] + } + } + step("We should see R1 advertised prefix only in IPv4 AFI " "not in IPv6 AFI.") + result = verify_rib(tgen, "ipv4", dut, static_route_ipv4, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_rib( + tgen, "ipv6", dut, static_route_ipv6, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n".format(tc_name) + + "Expected behavior: routes are should not be present in ipv6 rib\n" + + " Error: {}".format(result) + ) + + step("Repeat the same test for IPv6 AFI.") + step("Configure allowas-in on R3 for R2 under IPv6 addr-family only") + # Api call to enable allowas-in in bgp process. + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "allowas-in": { + "number_occurences": 2, + "delete": True, + } + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"allowas-in": {"number_occurences": 2}} + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step("We should see R1 advertised prefix only in IPv6 AFI " "not in IPv4 AFI.") + result = verify_rib( + tgen, "ipv4", dut, static_route_ipv4, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n".format(tc_name) + + "Expected behavior: routes should not be present in ipv4 rib\n" + + " Error: {}".format(result) + ) + result = verify_rib(tgen, "ipv6", dut, static_route_ipv6, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_bgp_allowas_in_no_of_occurrences_p0(request): + """ + Verify that the if number of occurrences of AS number in path is + more than the configured allowas-in value then we do not accept + the route. + + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + # Enable static routes + static_routes = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure a route-map on R1 to prepend AS 4 times.") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "route_maps": { + "ASP_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "path": { + "as_num": "200 200 200 200", + "as_action": "prepend", + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure route map in out direction on R1") + # Configure neighbor for route map + input_dict_7 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "ASP_{}".format( + addr_type + ), + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step('Configure "allowas-in 4" on R3 for R2.') + # Api call to enable allowas-in in bgp process. + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "allowas-in": {"number_occurences": 4} + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_rib( + tgen, addr_type, dut, static_routes, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n ".format(tc_name) + + "Expected behavior: routes are should not be present in rib\n" + + "Error: {}".format(result) + ) + + for addr_type in ADDR_TYPES: + step('Configure "allowas-in 5" on R3 for R2.') + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "allowas-in": {"number_occurences": 5} + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + static_routes = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + result = verify_rib(tgen, addr_type, dut, static_routes, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_bgp_allowas_in_sameastoibgp_p1(request): + """ + Verify that when we advertise a network, learned from the same AS + via allowas-in command, to an iBGP neighbor we see multiple + occurrences. + + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + # Enable static routes + static_routes = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure a route-map on R2 to prepend AS 2 times.") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "route_maps": { + "ASP_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "path": {"as_num": "200 200", "as_action": "prepend"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure route map in out direction on R2") + # Configure neighbor for route map + input_dict_7 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "ASP_{}".format( + addr_type + ), + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step('Configure "allowas-in 3" on R3 for R1.') + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "allowas-in": {"number_occurences": 3} + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_1 = { + "r4": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "allowas-in": {"number_occurences": 3} + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + static_routes = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + dut = "r4" + path = "100 200 200 200" + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, aspath=path) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_bgp_allowas_in_sameastoebgp_p1(request): + """ + Verify that when we advertise a network, learned from the same AS + via allowas-in command, to an eBGP neighbor we see multiple + occurrences of our own AS based on configured value+1. + + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + # Enable static routes + static_routes = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure a route-map on R2 to prepend AS 2 times.") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "route_maps": { + "ASP_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "path": {"as_num": "200 200", "as_action": "prepend"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure route map in out direction on R2") + # Configure neighbor for route map + input_dict_7 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "ASP_{}".format( + addr_type + ), + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step('Configure "allowas-in 3" on R3 for R1.') + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "allowas-in": {"number_occurences": 3} + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + static_routes = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + dut = "r5" + path = "200 100 200 200 200" + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, aspath=path) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_as_override/__init__.py b/tests/topotests/bgp_as_override/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_as_override/r1/bgpd.conf b/tests/topotests/bgp_as_override/r1/bgpd.conf new file mode 100644 index 0000000..3cfb7a2 --- /dev/null +++ b/tests/topotests/bgp_as_override/r1/bgpd.conf @@ -0,0 +1,10 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_as_override/r1/zebra.conf b/tests/topotests/bgp_as_override/r1/zebra.conf new file mode 100644 index 0000000..63728eb --- /dev/null +++ b/tests/topotests/bgp_as_override/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.1/32 +! +interface r1-eth0 + ip address 192.168.1.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_as_override/r2/bgpd.conf b/tests/topotests/bgp_as_override/r2/bgpd.conf new file mode 100644 index 0000000..5e3b0c7 --- /dev/null +++ b/tests/topotests/bgp_as_override/r2/bgpd.conf @@ -0,0 +1,10 @@ +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 +! diff --git a/tests/topotests/bgp_as_override/r2/zebra.conf b/tests/topotests/bgp_as_override/r2/zebra.conf new file mode 100644 index 0000000..5bdfd02 --- /dev/null +++ b/tests/topotests/bgp_as_override/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface r2-eth0 + ip address 192.168.1.1/30 +! +interface r2-eth1 + ip address 192.168.2.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_as_override/r3/bgpd.conf b/tests/topotests/bgp_as_override/r3/bgpd.conf new file mode 100644 index 0000000..6bbe56b --- /dev/null +++ b/tests/topotests/bgp_as_override/r3/bgpd.conf @@ -0,0 +1,13 @@ +! +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 + neighbor 192.168.3.1 remote-as external + neighbor 192.168.3.1 timers 1 3 + neighbor 192.168.3.1 timers connect 1 + address-family ipv4 unicast + neighbor 192.168.3.1 as-override + exit-address-family +! diff --git a/tests/topotests/bgp_as_override/r3/zebra.conf b/tests/topotests/bgp_as_override/r3/zebra.conf new file mode 100644 index 0000000..77782be --- /dev/null +++ b/tests/topotests/bgp_as_override/r3/zebra.conf @@ -0,0 +1,9 @@ +! +interface r3-eth0 + ip address 192.168.2.2/30 +! +interface r3-eth1 + ip address 192.168.3.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_as_override/r4/bgpd.conf b/tests/topotests/bgp_as_override/r4/bgpd.conf new file mode 100644 index 0000000..1bdee08 --- /dev/null +++ b/tests/topotests/bgp_as_override/r4/bgpd.conf @@ -0,0 +1,7 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.3.2 remote-as external + neighbor 192.168.3.2 timers 1 3 + neighbor 192.168.3.2 timers connect 1 +! diff --git a/tests/topotests/bgp_as_override/r4/zebra.conf b/tests/topotests/bgp_as_override/r4/zebra.conf new file mode 100644 index 0000000..71dc595 --- /dev/null +++ b/tests/topotests/bgp_as_override/r4/zebra.conf @@ -0,0 +1,6 @@ +! +interface r4-eth0 + ip address 192.168.3.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_as_override/test_bgp_as_override.py b/tests/topotests/bgp_as_override/test_bgp_as_override.py new file mode 100644 index 0000000..7cb4f81 --- /dev/null +++ b/tests/topotests/bgp_as_override/test_bgp_as_override.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 7): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_as_override(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r3 = tgen.gears["r3"] + r4 = tgen.gears["r4"] + + def _bgp_converge(): + output = json.loads(r3.vtysh_cmd("show ip bgp neighbor 192.168.2.1 json")) + expected = { + "192.168.2.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_as_override(): + output = json.loads(r4.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "172.16.255.1/32": [{"valid": True, "path": "65003 65002 65003"}] + } + } + return topotest.json_cmp(output, expected) + + step("Initial BGP converge") + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP convergence on R4" + + step("Check if BGP as-override from R3 works") + test_func = functools.partial(_bgp_as_override) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see overriden ASN (65001) from R3" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/__init__.py b/tests/topotests/bgp_as_wide_bgp_identifier/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r1/bgpd.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r1/bgpd.conf new file mode 100644 index 0000000..cb04749 --- /dev/null +++ b/tests/topotests/bgp_as_wide_bgp_identifier/r1/bgpd.conf @@ -0,0 +1,7 @@ +! exit1 +router bgp 65001 + bgp router-id 10.10.10.10 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65002 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r1/zebra.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r1/zebra.conf new file mode 100644 index 0000000..c060e14 --- /dev/null +++ b/tests/topotests/bgp_as_wide_bgp_identifier/r1/zebra.conf @@ -0,0 +1,6 @@ +! exit1 +interface r1-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r2/bgpd.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r2/bgpd.conf new file mode 100644 index 0000000..bed6885 --- /dev/null +++ b/tests/topotests/bgp_as_wide_bgp_identifier/r2/bgpd.conf @@ -0,0 +1,9 @@ +! spine +router bgp 65002 + bgp router-id 10.10.10.10 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.3 remote-as 65002 + neighbor 192.168.255.3 timers 3 10 +! diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r2/zebra.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r2/zebra.conf new file mode 100644 index 0000000..a45520f --- /dev/null +++ b/tests/topotests/bgp_as_wide_bgp_identifier/r2/zebra.conf @@ -0,0 +1,6 @@ +! spine +interface r2-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r3/bgpd.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r3/bgpd.conf new file mode 100644 index 0000000..384e617 --- /dev/null +++ b/tests/topotests/bgp_as_wide_bgp_identifier/r3/bgpd.conf @@ -0,0 +1,7 @@ +! exit2 +router bgp 65002 + bgp router-id 10.10.10.10 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65002 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r3/zebra.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r3/zebra.conf new file mode 100644 index 0000000..2f4dbc5 --- /dev/null +++ b/tests/topotests/bgp_as_wide_bgp_identifier/r3/zebra.conf @@ -0,0 +1,6 @@ +! exit2 +interface r3-eth0 + ip address 192.168.255.3/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py b/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py new file mode 100644 index 0000000..5c09a6b --- /dev/null +++ b/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_as_wide_bgp_identifier.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Donatas Abraitis +# + +""" +rfc6286: Autonomous-System-Wide Unique BGP Identifier for BGP-4 +Test if 'Bad BGP Identifier' notification is sent only to +internal peers (autonomous-system-wide). eBGP peers are not +affected and should work. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_as_wide_bgp_identifier(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = {"192.168.255.1": {"bgpState": "Established"}} + return topotest.json_cmp(output, expected) + + def _bgp_failed(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "lastNotificationReason": "OPEN Message Error/Bad BGP Identifier" + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, tgen.gears["r1"]) + success, result = topotest.run_and_expect(test_func, None, count=260, wait=0.5) + + assert result is None, 'Failed to converge: "{}"'.format(tgen.gears["r1"]) + + test_func = functools.partial(_bgp_failed, tgen.gears["r3"]) + success, result = topotest.run_and_expect(test_func, None, count=260, wait=0.5) + + assert result is None, 'Bad BGP Identifier notification not sent: "{}"'.format( + tgen.gears["r3"] + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_asdot_regex/__init__.py b/tests/topotests/bgp_asdot_regex/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_asdot_regex/r1/bgpd.conf b/tests/topotests/bgp_asdot_regex/r1/bgpd.conf new file mode 100644 index 0000000..4dd95dd --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r1/bgpd.conf @@ -0,0 +1,27 @@ +router bgp 1.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.255.2 remote-as 1.2 + address-family ipv4 unicast + network 172.31.1.0/24 route-map rmapout + network 172.31.2.0/24 route-map rmapout + neighbor 192.168.255.2 route-map rmapin in + neighbor 192.168.255.2 activate + exit-address-family +exit +bgp as-path access-list only1_4 permit _1.4_ +bgp as-path access-list only65540 permit _65540_ +access-list 172313 permit 172.31.3.0/24 +access-list 172314 permit 172.31.4.0/24 +route-map rmapout permit 1 + set as-path prepend 1.4 +exit +route-map rmapin permit 1 + match ip address 172313 + match as-path only1_4 +exit +route-map rmapin permit 2 + match ip address 172314 + match as-path only65540 +exit + diff --git a/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json b/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json new file mode 100644 index 0000000..e3703bf --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json @@ -0,0 +1,80 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 3, + "routerId": "192.168.255.1", + "defaultLocPrf": 100, + "localAS": "1.1", + "routes": { "172.31.1.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.1.0", + "prefixLen":24, + "network":"172.31.1.0/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"1.4", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +] +,"172.31.2.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.2.0", + "prefixLen":24, + "network":"172.31.2.0/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"1.4", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +] +,"172.31.3.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.3.0", + "prefixLen":24, + "network":"172.31.3.0/24", + "metric":0, + "weight":0, + "peerId":"192.168.255.2", + "path":"1.2 1.4", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.255.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +] + } } diff --git a/tests/topotests/bgp_asdot_regex/r1/zebra.conf b/tests/topotests/bgp_asdot_regex/r1/zebra.conf new file mode 100644 index 0000000..6e9b0b4 --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_asdot_regex/r2/bgpd.conf b/tests/topotests/bgp_asdot_regex/r2/bgpd.conf new file mode 100644 index 0000000..216dbd1 --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r2/bgpd.conf @@ -0,0 +1,26 @@ +router bgp 65538 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.255.1 remote-as 65537 + address-family ipv4 unicast + network 172.31.3.0/24 route-map rmapout + network 172.31.4.0/24 route-map rmapout + neighbor 192.168.255.1 route-map rmapin in + neighbor 192.168.255.1 activate + exit-address-family +exit +bgp as-path access-list only65540 permit _65540_ +bgp as-path access-list only1_4 permit _1.4_ +access-list 172311 permit 172.31.1.0/24 +access-list 172312 permit 172.31.2.0/24 +route-map rmapout permit 1 + set as-path prepend 65540 +exit +route-map rmapin permit 1 + match ip address 172311 + match as-path only65540 +exit +route-map rmapin permit 2 + match ip address 172312 + match as-path only1_4 +exit diff --git a/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json b/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json new file mode 100644 index 0000000..1af4ff7 --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json @@ -0,0 +1,80 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 3, + "routerId": "192.168.255.2", + "defaultLocPrf": 100, + "localAS": 65538, + "routes": { "172.31.1.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.1.0", + "prefixLen":24, + "network":"172.31.1.0/24", + "metric":0, + "weight":0, + "peerId":"192.168.255.1", + "path":"65537 65540", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.255.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +] +,"172.31.3.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.3.0", + "prefixLen":24, + "network":"172.31.3.0/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"65540", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +] +,"172.31.4.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.4.0", + "prefixLen":24, + "network":"172.31.4.0/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"65540", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +] + } } diff --git a/tests/topotests/bgp_asdot_regex/r2/zebra.conf b/tests/topotests/bgp_asdot_regex/r2/zebra.conf new file mode 100644 index 0000000..6c14de5 --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_asdot_regex/test_bgp_asdot_regex.py b/tests/topotests/bgp_asdot_regex/test_bgp_asdot_regex.py new file mode 100644 index 0000000..5d5f165 --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/test_bgp_asdot_regex.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python + +# +# test_bgp_asdot_regex.py +# Part of Topotests +# +# Copyright 2022 6WIND S.A. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_asdot_regex.py: + +Test how regex applies when asnotation to forge bgp config is based on dot or not. +""" + +import os +import sys +import json +import pytest +from functools import partial + +# add after imports, before defining classes or functions: +pytestmark = [pytest.mark.bgpd] + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_asdot_regex(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + logger.info("Check if neighbor sessions are up in {}".format(router1.name)) + test_func = partial(_bgp_converge, router1) + success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + assert result is None, 'Failed to see BGP convergence in "{}"'.format(router1.name) + + logger.info("BGP neighbor session is up in {}".format(router1.name)) + + logger.info("waiting for bgp peers exchanging UPDATES") + + for router in tgen.routers().values(): + ref_file = "{}/{}/show_bgp_ipv4.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show bgp ipv4 unicast json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=40, wait=2.5) + assertmsg = "{}: BGP UPDATE exchange failure".format(router.name) + assert res is None, assertmsg + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_aspath_zero/__init__.py b/tests/topotests/bgp_aspath_zero/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_aspath_zero/exabgp.env b/tests/topotests/bgp_aspath_zero/exabgp.env new file mode 100644 index 0000000..28e6423 --- /dev/null +++ b/tests/topotests/bgp_aspath_zero/exabgp.env @@ -0,0 +1,53 @@ +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' +##daemonize = false + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_aspath_zero/peer1/exabgp.cfg b/tests/topotests/bgp_aspath_zero/peer1/exabgp.cfg new file mode 100644 index 0000000..fe9ea01 --- /dev/null +++ b/tests/topotests/bgp_aspath_zero/peer1/exabgp.cfg @@ -0,0 +1,17 @@ +neighbor 10.0.0.1 { + router-id 10.0.0.2; + local-address 10.0.0.2; + local-as 65001; + peer-as 65534; + + static { + route 192.168.100.101/32 { + next-hop 10.0.0.2; + } + + route 192.168.100.102/32 { + as-path [65000 0 65001]; + next-hop 10.0.0.2; + } + } +} diff --git a/tests/topotests/bgp_aspath_zero/r1/bgpd.conf b/tests/topotests/bgp_aspath_zero/r1/bgpd.conf new file mode 100644 index 0000000..002a5c7 --- /dev/null +++ b/tests/topotests/bgp_aspath_zero/r1/bgpd.conf @@ -0,0 +1,6 @@ +! +router bgp 65534 + no bgp ebgp-requires-policy + neighbor 10.0.0.2 remote-as external + neighbor 10.0.0.2 timers 3 10 +! diff --git a/tests/topotests/bgp_aspath_zero/r1/zebra.conf b/tests/topotests/bgp_aspath_zero/r1/zebra.conf new file mode 100644 index 0000000..22a26ac --- /dev/null +++ b/tests/topotests/bgp_aspath_zero/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 10.0.0.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_aspath_zero/test_bgp_aspath_zero.py b/tests/topotests/bgp_aspath_zero/test_bgp_aspath_zero.py new file mode 100644 index 0000000..0f1a083 --- /dev/null +++ b/tests/topotests/bgp_aspath_zero/test_bgp_aspath_zero.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by +# Donatas Abraitis +# + +""" +Test if BGP UPDATE with AS-PATH attribute with value zero (0) +is threated as withdrawal. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + r1 = tgen.add_router("r1") + peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.2", defaultRoute="via 10.0.0.1") + + switch = tgen.add_switch("s1") + switch.add_link(r1) + switch.add_link(peer1) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router = tgen.gears["r1"] + router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf")) + router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf")) + router.start() + + peer = tgen.gears["peer1"] + peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env")) + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_aggregator_zero(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show ip bgp neighbor 10.0.0.2 json") + ) + expected = { + "10.0.0.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "More than one prefix seen at r1, SHOULD be only one." + + def _bgp_has_correct_routes_without_asn_0(): + output = json.loads(tgen.gears["r1"].vtysh_cmd("show ip bgp json")) + expected = {"routes": {"192.168.100.101/32": [{"valid": True}]}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_has_correct_routes_without_asn_0) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed listing 192.168.100.101/32, SHOULD be accepted." + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_auth/R1/bgpd.conf b/tests/topotests/bgp_auth/R1/bgpd.conf new file mode 100644 index 0000000..310841f --- /dev/null +++ b/tests/topotests/bgp_auth/R1/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65001 + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 password hello1 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 timers connect 5 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 password hello2 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 5 + address-family ipv4 unicast + neighbor 2.2.2.2 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp_auth/R1/bgpd_multi_vrf.conf b/tests/topotests/bgp_auth/R1/bgpd_multi_vrf.conf new file mode 100644 index 0000000..644e01c --- /dev/null +++ b/tests/topotests/bgp_auth/R1/bgpd_multi_vrf.conf @@ -0,0 +1,39 @@ +! debug bgp neighbor-events + +router bgp 65001 vrf blue + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 timers connect 5 + neighbor 2.2.2.2 password blue1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 5 + neighbor 3.3.3.3 password blue2 + address-family ipv4 unicast + neighbor 2.2.2.2 activate + neighbor 3.3.3.3 activate + +router bgp 65001 vrf red + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo2 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 timers connect 5 + neighbor 2.2.2.2 password red1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo2 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 5 + neighbor 3.3.3.3 password red2 + address-family ipv4 unicast + neighbor 2.2.2.2 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp_auth/R1/bgpd_multi_vrf_prefix.conf b/tests/topotests/bgp_auth/R1/bgpd_multi_vrf_prefix.conf new file mode 100644 index 0000000..7e15720 --- /dev/null +++ b/tests/topotests/bgp_auth/R1/bgpd_multi_vrf_prefix.conf @@ -0,0 +1,37 @@ +router bgp 65001 vrf blue + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor TWO_GROUP_blue peer-group + neighbor TWO_GROUP_blue remote-as 65002 + neighbor TWO_GROUP_blue update-source 1.1.1.1 + neighbor TWO_GROUP_blue ebgp-multihop 3 + neighbor TWO_GROUP_blue password blue1 + neighbor THREE_GROUP_blue peer-group + neighbor THREE_GROUP_blue remote-as 65003 + neighbor THREE_GROUP_blue update-source 1.1.1.1 + neighbor THREE_GROUP_blue ebgp-multihop 3 + neighbor THREE_GROUP_blue password blue2 + bgp listen range 2.2.2.0/24 peer-group TWO_GROUP_blue + bgp listen range 3.3.3.0/24 peer-group THREE_GROUP_blue + address-family ipv4 unicast + neighbor TWO_GROUP_blue maximum-prefix 4294967295 + neighbor THREE_GROUP_blue maximum-prefix 4294967295 + +router bgp 65001 vrf red + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor TWO_GROUP_red peer-group + neighbor TWO_GROUP_red remote-as 65002 + neighbor TWO_GROUP_red update-source 1.1.1.1 + neighbor TWO_GROUP_red ebgp-multihop 3 + neighbor TWO_GROUP_red password red1 + neighbor THREE_GROUP_red peer-group + neighbor THREE_GROUP_red remote-as 65003 + neighbor THREE_GROUP_red update-source 1.1.1.1 + neighbor THREE_GROUP_red ebgp-multihop 3 + neighbor THREE_GROUP_red password red2 + bgp listen range 2.2.2.0/24 peer-group TWO_GROUP_red + bgp listen range 3.3.3.0/24 peer-group THREE_GROUP_red + address-family ipv4 unicast + neighbor TWO_GROUP_red maximum-prefix 4294967295 + neighbor THREE_GROUP_red maximum-prefix 4294967295 diff --git a/tests/topotests/bgp_auth/R1/bgpd_prefix.conf b/tests/topotests/bgp_auth/R1/bgpd_prefix.conf new file mode 100644 index 0000000..9200b05 --- /dev/null +++ b/tests/topotests/bgp_auth/R1/bgpd_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65001 + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor TWO_GROUP peer-group + neighbor TWO_GROUP remote-as 65002 + neighbor TWO_GROUP update-source 1.1.1.1 + neighbor TWO_GROUP ebgp-multihop 3 + neighbor TWO_GROUP password hello1 + neighbor THREE_GROUP peer-group + neighbor THREE_GROUP remote-as 65003 + neighbor THREE_GROUP update-source 1.1.1.1 + neighbor THREE_GROUP ebgp-multihop 3 + neighbor THREE_GROUP password hello2 + bgp listen range 2.2.2.0/24 peer-group TWO_GROUP + bgp listen range 3.3.3.0/24 peer-group THREE_GROUP + address-family ipv4 unicast + neighbor TWO_GROUP maximum-prefix 4294967295 + neighbor THREE_GROUP maximum-prefix 4294967295 diff --git a/tests/topotests/bgp_auth/R1/bgpd_vrf.conf b/tests/topotests/bgp_auth/R1/bgpd_vrf.conf new file mode 100644 index 0000000..5799da1 --- /dev/null +++ b/tests/topotests/bgp_auth/R1/bgpd_vrf.conf @@ -0,0 +1,20 @@ +! debug bgp neighbor-events + +router bgp 65001 vrf blue + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 timers connect 5 + neighbor 2.2.2.2 password hello1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 5 + neighbor 3.3.3.3 password hello2 + address-family ipv4 unicast + neighbor 2.2.2.2 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp_auth/R1/bgpd_vrf_prefix.conf b/tests/topotests/bgp_auth/R1/bgpd_vrf_prefix.conf new file mode 100644 index 0000000..d68951b --- /dev/null +++ b/tests/topotests/bgp_auth/R1/bgpd_vrf_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65001 vrf blue + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor TWO_GROUP_blue peer-group + neighbor TWO_GROUP_blue remote-as 65002 + neighbor TWO_GROUP_blue update-source 1.1.1.1 + neighbor TWO_GROUP_blue ebgp-multihop 3 + neighbor TWO_GROUP_blue password hello1 + neighbor THREE_GROUP_blue peer-group + neighbor THREE_GROUP_blue remote-as 65003 + neighbor THREE_GROUP_blue update-source 1.1.1.1 + neighbor THREE_GROUP_blue ebgp-multihop 3 + neighbor THREE_GROUP_blue password hello2 + bgp listen range 2.2.2.0/24 peer-group TWO_GROUP_blue + bgp listen range 3.3.3.0/24 peer-group THREE_GROUP_blue + address-family ipv4 unicast + neighbor TWO_GROUP_blue maximum-prefix 4294967295 + neighbor THREE_GROUP_blue maximum-prefix 4294967295 diff --git a/tests/topotests/bgp_auth/R1/empty.conf b/tests/topotests/bgp_auth/R1/empty.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_auth/R1/ospfd.conf b/tests/topotests/bgp_auth/R1/ospfd.conf new file mode 100644 index 0000000..b28dd59 --- /dev/null +++ b/tests/topotests/bgp_auth/R1/ospfd.conf @@ -0,0 +1,22 @@ +interface R1-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R1-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R1-eth2 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R1-eth3 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R1-eth4 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R1-eth5 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +router ospf + network 10.10.0.0/16 area 0 + network 10.20.0.0/16 area 0 + network 1.1.1.1/32 area 0 \ No newline at end of file diff --git a/tests/topotests/bgp_auth/R1/ospfd_multi_vrf.conf b/tests/topotests/bgp_auth/R1/ospfd_multi_vrf.conf new file mode 100644 index 0000000..b64bec8 --- /dev/null +++ b/tests/topotests/bgp_auth/R1/ospfd_multi_vrf.conf @@ -0,0 +1,26 @@ +interface R1-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R1-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R1-eth2 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R1-eth3 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R1-eth4 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R1-eth5 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +router ospf vrf blue + network 10.10.0.0/16 area 0 + network 10.20.0.0/16 area 0 + network 1.1.1.1/32 area 0 +router ospf vrf red + network 10.10.0.0/16 area 0 + network 10.20.0.0/16 area 0 + network 1.1.1.1/32 area 0 diff --git a/tests/topotests/bgp_auth/R1/ospfd_vrf.conf b/tests/topotests/bgp_auth/R1/ospfd_vrf.conf new file mode 100644 index 0000000..deaf53d --- /dev/null +++ b/tests/topotests/bgp_auth/R1/ospfd_vrf.conf @@ -0,0 +1,22 @@ +interface R1-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R1-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R1-eth2 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R1-eth3 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R1-eth4 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R1-eth5 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +router ospf vrf blue + network 10.10.0.0/16 area 0 + network 10.20.0.0/16 area 0 + network 1.1.1.1/32 area 0 diff --git a/tests/topotests/bgp_auth/R1/zebra.conf b/tests/topotests/bgp_auth/R1/zebra.conf new file mode 100644 index 0000000..a0b062c --- /dev/null +++ b/tests/topotests/bgp_auth/R1/zebra.conf @@ -0,0 +1,20 @@ +! +interface lo + ip address 1.1.1.1/32 +interface lo1 vrf blue + ip address 1.1.1.1/32 +interface lo2 vrf red + ip address 1.1.1.1/32 +interface R1-eth0 + ip address 10.10.0.1/24 +interface R1-eth1 + ip address 10.20.0.1/24 +interface R1-eth2 vrf blue + ip address 10.10.0.1/24 +interface R1-eth3 vrf blue + ip address 10.20.0.1/24 +interface R1-eth4 vrf red + ip address 10.10.0.1/24 +interface R1-eth5 vrf red + ip address 10.20.0.1/24 +! diff --git a/tests/topotests/bgp_auth/R2/bgpd.conf b/tests/topotests/bgp_auth/R2/bgpd.conf new file mode 100644 index 0000000..2149c05 --- /dev/null +++ b/tests/topotests/bgp_auth/R2/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65002 + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password hello1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 5 + neighbor 3.3.3.3 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp_auth/R2/bgpd_multi_vrf.conf b/tests/topotests/bgp_auth/R2/bgpd_multi_vrf.conf new file mode 100644 index 0000000..af88fe1 --- /dev/null +++ b/tests/topotests/bgp_auth/R2/bgpd_multi_vrf.conf @@ -0,0 +1,37 @@ +router bgp 65002 vrf blue + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password blue1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 5 + neighbor 3.3.3.3 password blue3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate + +router bgp 65002 vrf red + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo2 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password red1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo2 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 5 + neighbor 3.3.3.3 password red3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp_auth/R2/bgpd_multi_vrf_prefix.conf b/tests/topotests/bgp_auth/R2/bgpd_multi_vrf_prefix.conf new file mode 100644 index 0000000..af88fe1 --- /dev/null +++ b/tests/topotests/bgp_auth/R2/bgpd_multi_vrf_prefix.conf @@ -0,0 +1,37 @@ +router bgp 65002 vrf blue + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password blue1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 5 + neighbor 3.3.3.3 password blue3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate + +router bgp 65002 vrf red + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo2 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password red1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo2 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 5 + neighbor 3.3.3.3 password red3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp_auth/R2/bgpd_prefix.conf b/tests/topotests/bgp_auth/R2/bgpd_prefix.conf new file mode 100644 index 0000000..2149c05 --- /dev/null +++ b/tests/topotests/bgp_auth/R2/bgpd_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65002 + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password hello1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 5 + neighbor 3.3.3.3 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp_auth/R2/bgpd_vrf.conf b/tests/topotests/bgp_auth/R2/bgpd_vrf.conf new file mode 100644 index 0000000..03cadb3 --- /dev/null +++ b/tests/topotests/bgp_auth/R2/bgpd_vrf.conf @@ -0,0 +1,18 @@ +router bgp 65002 vrf blue + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password hello1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 5 + neighbor 3.3.3.3 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp_auth/R2/bgpd_vrf_prefix.conf b/tests/topotests/bgp_auth/R2/bgpd_vrf_prefix.conf new file mode 100644 index 0000000..03cadb3 --- /dev/null +++ b/tests/topotests/bgp_auth/R2/bgpd_vrf_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65002 vrf blue + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password hello1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 5 + neighbor 3.3.3.3 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp_auth/R2/empty.conf b/tests/topotests/bgp_auth/R2/empty.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_auth/R2/ospfd.conf b/tests/topotests/bgp_auth/R2/ospfd.conf new file mode 100644 index 0000000..78e78d6 --- /dev/null +++ b/tests/topotests/bgp_auth/R2/ospfd.conf @@ -0,0 +1,22 @@ +interface R2-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R2-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R2-eth2 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R2-eth3 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R2-eth4 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R2-eth5 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +router ospf + network 10.10.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 2.2.2.2/32 area 0 diff --git a/tests/topotests/bgp_auth/R2/ospfd_multi_vrf.conf b/tests/topotests/bgp_auth/R2/ospfd_multi_vrf.conf new file mode 100644 index 0000000..81eb5d6 --- /dev/null +++ b/tests/topotests/bgp_auth/R2/ospfd_multi_vrf.conf @@ -0,0 +1,26 @@ +interface R2-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R2-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R2-eth2 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R2-eth3 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R2-eth4 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R2-eth5 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +router ospf vrf blue + network 10.10.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 2.2.2.2/32 area 0 +router ospf vrf red + network 10.10.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 2.2.2.2/32 area 0 diff --git a/tests/topotests/bgp_auth/R2/ospfd_vrf.conf b/tests/topotests/bgp_auth/R2/ospfd_vrf.conf new file mode 100644 index 0000000..673d103 --- /dev/null +++ b/tests/topotests/bgp_auth/R2/ospfd_vrf.conf @@ -0,0 +1,22 @@ +interface R2-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R2-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R2-eth2 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R2-eth3 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R2-eth4 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R2-eth5 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +router ospf vrf blue + network 10.10.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 2.2.2.2/32 area 0 diff --git a/tests/topotests/bgp_auth/R2/zebra.conf b/tests/topotests/bgp_auth/R2/zebra.conf new file mode 100644 index 0000000..fed4c27 --- /dev/null +++ b/tests/topotests/bgp_auth/R2/zebra.conf @@ -0,0 +1,20 @@ +! +interface lo + ip address 2.2.2.2/32 +interface lo1 vrf blue + ip address 2.2.2.2/32 +interface lo2 vrf red + ip address 2.2.2.2/32 +interface R2-eth0 + ip address 10.10.0.2/24 +interface R2-eth1 + ip address 10.30.0.2/24 +interface R2-eth2 vrf blue + ip address 10.10.0.2/24 +interface R2-eth3 vrf blue + ip address 10.30.0.2/24 +interface R2-eth4 vrf red + ip address 10.10.0.2/24 +interface R2-eth5 vrf red + ip address 10.30.0.2/24 +! diff --git a/tests/topotests/bgp_auth/R3/bgpd.conf b/tests/topotests/bgp_auth/R3/bgpd.conf new file mode 100644 index 0000000..ca9b838 --- /dev/null +++ b/tests/topotests/bgp_auth/R3/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65003 + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password hello2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 5 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp_auth/R3/bgpd_multi_vrf.conf b/tests/topotests/bgp_auth/R3/bgpd_multi_vrf.conf new file mode 100644 index 0000000..81d0299 --- /dev/null +++ b/tests/topotests/bgp_auth/R3/bgpd_multi_vrf.conf @@ -0,0 +1,37 @@ +router bgp 65003 vrf blue + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password blue2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 5 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password blue3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate + +router bgp 65003 vrf red + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo2 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password red2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo2 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 5 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password red3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp_auth/R3/bgpd_multi_vrf_prefix.conf b/tests/topotests/bgp_auth/R3/bgpd_multi_vrf_prefix.conf new file mode 100644 index 0000000..81d0299 --- /dev/null +++ b/tests/topotests/bgp_auth/R3/bgpd_multi_vrf_prefix.conf @@ -0,0 +1,37 @@ +router bgp 65003 vrf blue + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password blue2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 5 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password blue3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate + +router bgp 65003 vrf red + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo2 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password red2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo2 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 5 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password red3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp_auth/R3/bgpd_prefix.conf b/tests/topotests/bgp_auth/R3/bgpd_prefix.conf new file mode 100644 index 0000000..ca9b838 --- /dev/null +++ b/tests/topotests/bgp_auth/R3/bgpd_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65003 + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password hello2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 5 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp_auth/R3/bgpd_vrf.conf b/tests/topotests/bgp_auth/R3/bgpd_vrf.conf new file mode 100644 index 0000000..f8323e0 --- /dev/null +++ b/tests/topotests/bgp_auth/R3/bgpd_vrf.conf @@ -0,0 +1,18 @@ +router bgp 65003 vrf blue + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password hello2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 5 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp_auth/R3/bgpd_vrf_prefix.conf b/tests/topotests/bgp_auth/R3/bgpd_vrf_prefix.conf new file mode 100644 index 0000000..f8323e0 --- /dev/null +++ b/tests/topotests/bgp_auth/R3/bgpd_vrf_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65003 vrf blue + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 5 + neighbor 1.1.1.1 password hello2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 5 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp_auth/R3/empty.conf b/tests/topotests/bgp_auth/R3/empty.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_auth/R3/ospfd.conf b/tests/topotests/bgp_auth/R3/ospfd.conf new file mode 100644 index 0000000..befeadb --- /dev/null +++ b/tests/topotests/bgp_auth/R3/ospfd.conf @@ -0,0 +1,22 @@ +interface R3-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R3-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R3-eth2 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R3-eth3 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R3-eth4 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R3-eth5 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +router ospf + network 10.20.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 3.3.3.3/32 area 0 diff --git a/tests/topotests/bgp_auth/R3/ospfd_multi_vrf.conf b/tests/topotests/bgp_auth/R3/ospfd_multi_vrf.conf new file mode 100644 index 0000000..2b2abc6 --- /dev/null +++ b/tests/topotests/bgp_auth/R3/ospfd_multi_vrf.conf @@ -0,0 +1,26 @@ +interface R3-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R3-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R3-eth2 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R3-eth3 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R3-eth4 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R3-eth5 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +router ospf vrf blue + network 10.20.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 3.3.3.3/32 area 0 +router ospf vrf red + network 10.20.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 3.3.3.3/32 area 0 diff --git a/tests/topotests/bgp_auth/R3/ospfd_vrf.conf b/tests/topotests/bgp_auth/R3/ospfd_vrf.conf new file mode 100644 index 0000000..392d17a --- /dev/null +++ b/tests/topotests/bgp_auth/R3/ospfd_vrf.conf @@ -0,0 +1,22 @@ +interface R3-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R3-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R3-eth2 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R3-eth3 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R3-eth4 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +interface R3-eth5 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +router ospf vrf blue + network 10.20.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 3.3.3.3/32 area 0 diff --git a/tests/topotests/bgp_auth/R3/zebra.conf b/tests/topotests/bgp_auth/R3/zebra.conf new file mode 100644 index 0000000..d49c98b --- /dev/null +++ b/tests/topotests/bgp_auth/R3/zebra.conf @@ -0,0 +1,20 @@ +! +interface lo + ip address 3.3.3.3/32 +interface lo1 vrf blue + ip address 3.3.3.3/32 +interface lo2 vrf red + ip address 3.3.3.3/32 +interface R3-eth0 + ip address 10.20.0.3/24 +interface R3-eth1 + ip address 10.30.0.3/24 +interface R3-eth2 vrf blue + ip address 10.20.0.3/24 +interface R3-eth3 vrf blue + ip address 10.30.0.3/24 +interface R3-eth4 vrf red + ip address 10.20.0.3/24 +interface R3-eth5 vrf red + ip address 10.30.0.3/24 +! diff --git a/tests/topotests/bgp_auth/bgp_auth_common.py b/tests/topotests/bgp_auth/bgp_auth_common.py new file mode 100644 index 0000000..824498e --- /dev/null +++ b/tests/topotests/bgp_auth/bgp_auth_common.py @@ -0,0 +1,266 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_auth.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# + +""" +test_bgp_auth.py: Test BGP Md5 Authentication + + +------+ + +--------| |--------+ + | +------| R1 |------+ | + | | -----| |----+ | | + | | | +------+ | | | + | | | | | | + +------+ +------+ + | |------------| | + | R2 |------------| R3 | + | |------------| | + +------+ +------+ + + +setup is 3 routers with 3 links between each each link in a different vrf +Default, blue and red respectively +Tests check various fiddling with passwords and checking that the peer +establishment is as expected and passwords are not leaked across sockets +for bgp instances +""" +# pylint: disable=C0413 + +import json +import os +import platform +import sys +from time import sleep + +from lib import common_config, topotest +from lib.common_config import ( + save_initial_config_on_routers, + reset_with_new_configs, +) +from lib.topogen import Topogen, TopoRouter, get_topogen + +CWD = os.path.dirname(os.path.realpath(__file__)) + + +def vrf_str(vrf): + if vrf == "": + vrf_str = "" + else: + vrf_str = "vrf {}".format(vrf) + + return vrf_str + + +def peer_name(rtr, prefix, vrf): + "generate VRF string for CLI" + if vrf == "": + vrf_str = "" + else: + vrf_str = "_" + vrf + + if prefix == "yes": + if rtr == "R2": + return "TWO_GROUP" + vrf_str + else: + return "THREE_GROUP" + vrf_str + else: + if rtr == "R2": + return "2.2.2.2" + else: + return "3.3.3.3" + + +def print_diag(vrf): + "print failure disagnostics" + + tgen = get_topogen() + router_list = tgen.routers() + for rname, router in router_list.items(): + print(rname + ":") + print(router.vtysh_cmd("show run")) + print(router.vtysh_cmd("show ip route {}".format(vrf_str(vrf)))) + print(router.vtysh_cmd("show bgp {} neighbor".format(vrf_str(vrf)))) + + +@common_config.retry(retry_timeout=190) +def _check_neigh_state(router, peer, state, vrf=""): + "check BGP neighbor state on a router" + + neigh_output = router.vtysh_cmd( + "show bgp {} neighbors {} json".format(vrf_str(vrf), peer) + ) + + peer_state = "Unknown" + neigh_output_json = json.loads(neigh_output) + if peer in neigh_output_json: + peer_state = neigh_output_json[peer]["bgpState"] + if peer_state == state: + return True + return "{} peer with {} expected state {} got {} ".format( + router.name, peer, state, peer_state + ) + + +def check_neigh_state(router, peer, state, vrf=""): + "check BGP neighbor state on a router" + + assertmsg = _check_neigh_state(router, peer, state, vrf) + assert assertmsg is True, assertmsg + + +def check_all_peers_established(vrf=""): + "standard check for extablished peers per vrf" + + tgen = get_topogen() + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + # do r1 last as he might be the dynamic one + check_neigh_state(r2, "1.1.1.1", "Established", vrf) + check_neigh_state(r2, "3.3.3.3", "Established", vrf) + check_neigh_state(r3, "1.1.1.1", "Established", vrf) + check_neigh_state(r3, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "3.3.3.3", "Established", vrf) + + +def check_vrf_peer_remove_passwords(vrf="", prefix="no"): + "selectively remove passwords checking state" + + tgen = get_topogen() + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + + check_all_peers_established(vrf) + + r1.vtysh_cmd( + "conf t\nrouter bgp 65001 {}\nno neighbor {} password".format( + vrf_str(vrf), peer_name("R2", prefix, vrf) + ) + ) + + check_neigh_state(r2, "1.1.1.1", "Connect", vrf) + check_neigh_state(r2, "3.3.3.3", "Established", vrf) + check_neigh_state(r3, "1.1.1.1", "Established", vrf) + check_neigh_state(r3, "2.2.2.2", "Established", vrf) + # don't check dynamic downed peers - they are removed + if prefix == "no": + check_neigh_state(r1, "2.2.2.2", "Connect", vrf) + check_neigh_state(r1, "3.3.3.3", "Established", vrf) + + r2.vtysh_cmd( + "conf t\nrouter bgp 65002 {}\nno neighbor 1.1.1.1 password".format(vrf_str(vrf)) + ) + check_all_peers_established(vrf) + + r1.vtysh_cmd( + "conf t\nrouter bgp 65001 {}\nno neighbor {} password".format( + vrf_str(vrf), peer_name("R3", prefix, vrf) + ) + ) + check_neigh_state(r2, "1.1.1.1", "Established", vrf) + check_neigh_state(r2, "3.3.3.3", "Established", vrf) + check_neigh_state(r3, "1.1.1.1", "Connect", vrf) + check_neigh_state(r3, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "2.2.2.2", "Established", vrf) + # don't check dynamic downed peers - they are removed + if prefix == "no": + check_neigh_state(r1, "3.3.3.3", "Connect", vrf) + + r3.vtysh_cmd( + "conf t\nrouter bgp 65003 {}\nno neighbor 1.1.1.1 password".format(vrf_str(vrf)) + ) + check_all_peers_established(vrf) + + r2.vtysh_cmd( + "conf t\nrouter bgp 65002 {}\nno neighbor 3.3.3.3 password".format(vrf_str(vrf)) + ) + check_neigh_state(r2, "1.1.1.1", "Established", vrf) + check_neigh_state(r2, "3.3.3.3", "Connect", vrf) + check_neigh_state(r3, "1.1.1.1", "Established", vrf) + check_neigh_state(r3, "2.2.2.2", "Connect", vrf) + check_neigh_state(r1, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "3.3.3.3", "Established", vrf) + + r3.vtysh_cmd( + "conf t\nrouter bgp 65003 {}\nno neighbor 2.2.2.2 password".format(vrf_str(vrf)) + ) + check_all_peers_established(vrf) + + +def check_vrf_peer_change_passwords(vrf="", prefix="no"): + "selectively change passwords checking state" + + tgen = get_topogen() + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + check_all_peers_established(vrf) + + r1.vtysh_cmd( + "conf t\nrouter bgp 65001 {}\nneighbor {} password change1".format( + vrf_str(vrf), peer_name("R2", prefix, vrf) + ) + ) + check_neigh_state(r2, "1.1.1.1", "Connect", vrf) + check_neigh_state(r2, "3.3.3.3", "Established", vrf) + check_neigh_state(r3, "1.1.1.1", "Established", vrf) + check_neigh_state(r3, "2.2.2.2", "Established", vrf) + # don't check dynamic downed peers - they are removed + if prefix == "no": + check_neigh_state(r1, "2.2.2.2", "Connect", vrf) + check_neigh_state(r1, "3.3.3.3", "Established", vrf) + + r2.vtysh_cmd( + "conf t\nrouter bgp 65002 {}\nneighbor 1.1.1.1 password change1".format( + vrf_str(vrf) + ) + ) + check_all_peers_established(vrf) + + r1.vtysh_cmd( + "conf t\nrouter bgp 65001 {}\nneighbor {} password change2".format( + vrf_str(vrf), peer_name("R3", prefix, vrf) + ) + ) + check_neigh_state(r2, "1.1.1.1", "Established", vrf) + check_neigh_state(r2, "3.3.3.3", "Established", vrf) + check_neigh_state(r3, "1.1.1.1", "Connect", vrf) + check_neigh_state(r3, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "2.2.2.2", "Established", vrf) + # don't check dynamic downed peers - they are removed + if prefix == "no": + check_neigh_state(r1, "3.3.3.3", "Connect", vrf) + + r3.vtysh_cmd( + "conf t\nrouter bgp 65003 {}\nneighbor 1.1.1.1 password change2".format( + vrf_str(vrf) + ) + ) + check_all_peers_established(vrf) + + r2.vtysh_cmd( + "conf t\nrouter bgp 65002 {}\nneighbor 3.3.3.3 password change3".format( + vrf_str(vrf) + ) + ) + check_neigh_state(r2, "1.1.1.1", "Established", vrf) + check_neigh_state(r2, "3.3.3.3", "Connect", vrf) + check_neigh_state(r3, "1.1.1.1", "Established", vrf) + check_neigh_state(r3, "2.2.2.2", "Connect", vrf) + check_neigh_state(r1, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "3.3.3.3", "Established", vrf) + + r3.vtysh_cmd( + "conf t\nrouter bgp 65003 {}\nneighbor 2.2.2.2 password change3".format( + vrf_str(vrf) + ) + ) + check_all_peers_established(vrf) diff --git a/tests/topotests/bgp_auth/test_bgp_auth1.py b/tests/topotests/bgp_auth/test_bgp_auth1.py new file mode 100644 index 0000000..9d47106 --- /dev/null +++ b/tests/topotests/bgp_auth/test_bgp_auth1.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_auth.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# + +""" +test_bgp_auth.py: Test BGP Md5 Authentication + + +------+ + +--------| |--------+ + | +------| R1 |------+ | + | | -----| |----+ | | + | | | +------+ | | | + | | | | | | + +------+ +------+ + | |------------| | + | R2 |------------| R3 | + | |------------| | + +------+ +------+ + + +setup is 3 routers with 3 links between each each link in a different vrf +Default, blue and red respectively +Tests check various fiddling with passwords and checking that the peer +establishment is as expected and passwords are not leaked across sockets +for bgp instances +""" +# pylint: disable=C0413 + +import json +import os +import platform +import sys +from time import sleep + +import pytest +from lib import common_config, topotest +from lib.common_config import ( + save_initial_config_on_routers, + reset_with_new_configs, +) + +from bgp_auth_common import ( + check_all_peers_established, + check_vrf_peer_remove_passwords, + check_vrf_peer_change_passwords, +) +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd] + +CWD = os.path.dirname(os.path.realpath(__file__)) + + +def build_topo(tgen): + tgen.add_router("R1") + tgen.add_router("R2") + tgen.add_router("R3") + + tgen.add_link(tgen.gears["R1"], tgen.gears["R2"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R2"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R2"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R2"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R2"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R2"], tgen.gears["R3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + + # blue vrf + r1.cmd_raises("ip link add blue type vrf table 1001") + r1.cmd_raises("ip link set up dev blue") + r2.cmd_raises("ip link add blue type vrf table 1001") + r2.cmd_raises("ip link set up dev blue") + r3.cmd_raises("ip link add blue type vrf table 1001") + r3.cmd_raises("ip link set up dev blue") + + r1.cmd_raises("ip link add lo1 type dummy") + r1.cmd_raises("ip link set lo1 master blue") + r1.cmd_raises("ip link set up dev lo1") + r2.cmd_raises("ip link add lo1 type dummy") + r2.cmd_raises("ip link set up dev lo1") + r2.cmd_raises("ip link set lo1 master blue") + r3.cmd_raises("ip link add lo1 type dummy") + r3.cmd_raises("ip link set up dev lo1") + r3.cmd_raises("ip link set lo1 master blue") + + r1.cmd_raises("ip link set R1-eth2 master blue") + r1.cmd_raises("ip link set R1-eth3 master blue") + r2.cmd_raises("ip link set R2-eth2 master blue") + r2.cmd_raises("ip link set R2-eth3 master blue") + r3.cmd_raises("ip link set R3-eth2 master blue") + r3.cmd_raises("ip link set R3-eth3 master blue") + + r1.cmd_raises("ip link set up dev R1-eth2") + r1.cmd_raises("ip link set up dev R1-eth3") + r2.cmd_raises("ip link set up dev R2-eth2") + r2.cmd_raises("ip link set up dev R2-eth3") + r3.cmd_raises("ip link set up dev R3-eth2") + r3.cmd_raises("ip link set up dev R3-eth3") + + # red vrf + r1.cmd_raises("ip link add red type vrf table 1002") + r1.cmd_raises("ip link set up dev red") + r2.cmd_raises("ip link add red type vrf table 1002") + r2.cmd_raises("ip link set up dev red") + r3.cmd_raises("ip link add red type vrf table 1002") + r3.cmd_raises("ip link set up dev red") + + r1.cmd_raises("ip link add lo2 type dummy") + r1.cmd_raises("ip link set lo2 master red") + r1.cmd_raises("ip link set up dev lo2") + r2.cmd_raises("ip link add lo2 type dummy") + r2.cmd_raises("ip link set up dev lo2") + r2.cmd_raises("ip link set lo2 master red") + r3.cmd_raises("ip link add lo2 type dummy") + r3.cmd_raises("ip link set up dev lo2") + r3.cmd_raises("ip link set lo2 master red") + + r1.cmd_raises("ip link set R1-eth4 master red") + r1.cmd_raises("ip link set R1-eth5 master red") + r2.cmd_raises("ip link set R2-eth4 master red") + r2.cmd_raises("ip link set R2-eth5 master red") + r3.cmd_raises("ip link set R3-eth4 master red") + r3.cmd_raises("ip link set R3-eth5 master red") + + r1.cmd_raises("ip link set up dev R1-eth4") + r1.cmd_raises("ip link set up dev R1-eth5") + r2.cmd_raises("ip link set up dev R2-eth4") + r2.cmd_raises("ip link set up dev R2-eth5") + r3.cmd_raises("ip link set up dev R3-eth4") + r3.cmd_raises("ip link set up dev R3-eth5") + + r1.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1") + r2.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1") + r3.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1") + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + router.load_config(TopoRouter.RD_OSPF, "") + router.load_config(TopoRouter.RD_BGP, "") + + # After copying the configurations, this function loads configured daemons. + tgen.start_router() + + # Save the initial router config. reset_config_on_routers will return to this config. + save_initial_config_on_routers(tgen) + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_default_peer_established(tgen): + "default vrf 3 peers same password" + + reset_with_new_configs(tgen, "bgpd.conf", "ospfd.conf") + check_all_peers_established() + + +def test_default_peer_remove_passwords(tgen): + "selectively remove passwords checking state" + + reset_with_new_configs(tgen, "bgpd.conf", "ospfd.conf") + check_vrf_peer_remove_passwords() + + +def test_default_peer_change_passwords(tgen): + "selectively change passwords checking state" + + reset_with_new_configs(tgen, "bgpd.conf", "ospfd.conf") + check_vrf_peer_change_passwords() + + +def test_default_prefix_peer_established(tgen): + "default vrf 3 peers same password with prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + reset_with_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf") + check_all_peers_established() + + +def test_prefix_peer_remove_passwords(tgen): + "selectively remove passwords checking state with prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + reset_with_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf") + check_vrf_peer_remove_passwords(prefix="yes") + + +def test_memory_leak(tgen): + "Run the memory leak test and report results." + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_auth/test_bgp_auth2.py b/tests/topotests/bgp_auth/test_bgp_auth2.py new file mode 100644 index 0000000..6b92036 --- /dev/null +++ b/tests/topotests/bgp_auth/test_bgp_auth2.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_auth.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# + +""" +test_bgp_auth.py: Test BGP Md5 Authentication + + +------+ + +--------| |--------+ + | +------| R1 |------+ | + | | -----| |----+ | | + | | | +------+ | | | + | | | | | | + +------+ +------+ + | |------------| | + | R2 |------------| R3 | + | |------------| | + +------+ +------+ + + +setup is 3 routers with 3 links between each each link in a different vrf +Default, blue and red respectively +Tests check various fiddling with passwords and checking that the peer +establishment is as expected and passwords are not leaked across sockets +for bgp instances +""" +# pylint: disable=C0413 + +import json +import os +import platform +import sys +from time import sleep + +import pytest +from lib import common_config, topotest +from lib.common_config import ( + save_initial_config_on_routers, + reset_with_new_configs, +) +from bgp_auth_common import ( + check_all_peers_established, + check_vrf_peer_remove_passwords, + check_vrf_peer_change_passwords, + check_all_peers_established, +) +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd] + +CWD = os.path.dirname(os.path.realpath(__file__)) + + +def build_topo(tgen): + tgen.add_router("R1") + tgen.add_router("R2") + tgen.add_router("R3") + + tgen.add_link(tgen.gears["R1"], tgen.gears["R2"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R2"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R2"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R2"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R2"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R2"], tgen.gears["R3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + + # blue vrf + r1.cmd_raises("ip link add blue type vrf table 1001") + r1.cmd_raises("ip link set up dev blue") + r2.cmd_raises("ip link add blue type vrf table 1001") + r2.cmd_raises("ip link set up dev blue") + r3.cmd_raises("ip link add blue type vrf table 1001") + r3.cmd_raises("ip link set up dev blue") + + r1.cmd_raises("ip link add lo1 type dummy") + r1.cmd_raises("ip link set lo1 master blue") + r1.cmd_raises("ip link set up dev lo1") + r2.cmd_raises("ip link add lo1 type dummy") + r2.cmd_raises("ip link set up dev lo1") + r2.cmd_raises("ip link set lo1 master blue") + r3.cmd_raises("ip link add lo1 type dummy") + r3.cmd_raises("ip link set up dev lo1") + r3.cmd_raises("ip link set lo1 master blue") + + r1.cmd_raises("ip link set R1-eth2 master blue") + r1.cmd_raises("ip link set R1-eth3 master blue") + r2.cmd_raises("ip link set R2-eth2 master blue") + r2.cmd_raises("ip link set R2-eth3 master blue") + r3.cmd_raises("ip link set R3-eth2 master blue") + r3.cmd_raises("ip link set R3-eth3 master blue") + + r1.cmd_raises("ip link set up dev R1-eth2") + r1.cmd_raises("ip link set up dev R1-eth3") + r2.cmd_raises("ip link set up dev R2-eth2") + r2.cmd_raises("ip link set up dev R2-eth3") + r3.cmd_raises("ip link set up dev R3-eth2") + r3.cmd_raises("ip link set up dev R3-eth3") + + # red vrf + r1.cmd_raises("ip link add red type vrf table 1002") + r1.cmd_raises("ip link set up dev red") + r2.cmd_raises("ip link add red type vrf table 1002") + r2.cmd_raises("ip link set up dev red") + r3.cmd_raises("ip link add red type vrf table 1002") + r3.cmd_raises("ip link set up dev red") + + r1.cmd_raises("ip link add lo2 type dummy") + r1.cmd_raises("ip link set lo2 master red") + r1.cmd_raises("ip link set up dev lo2") + r2.cmd_raises("ip link add lo2 type dummy") + r2.cmd_raises("ip link set up dev lo2") + r2.cmd_raises("ip link set lo2 master red") + r3.cmd_raises("ip link add lo2 type dummy") + r3.cmd_raises("ip link set up dev lo2") + r3.cmd_raises("ip link set lo2 master red") + + r1.cmd_raises("ip link set R1-eth4 master red") + r1.cmd_raises("ip link set R1-eth5 master red") + r2.cmd_raises("ip link set R2-eth4 master red") + r2.cmd_raises("ip link set R2-eth5 master red") + r3.cmd_raises("ip link set R3-eth4 master red") + r3.cmd_raises("ip link set R3-eth5 master red") + + r1.cmd_raises("ip link set up dev R1-eth4") + r1.cmd_raises("ip link set up dev R1-eth5") + r2.cmd_raises("ip link set up dev R2-eth4") + r2.cmd_raises("ip link set up dev R2-eth5") + r3.cmd_raises("ip link set up dev R3-eth4") + r3.cmd_raises("ip link set up dev R3-eth5") + + r1.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1") + r2.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1") + r3.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1") + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + router.load_config(TopoRouter.RD_OSPF, "") + router.load_config(TopoRouter.RD_BGP, "") + + # After copying the configurations, this function loads configured daemons. + tgen.start_router() + + # Save the initial router config. reset_config_on_routers will return to this config. + save_initial_config_on_routers(tgen) + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_vrf_prefix_peer_established(tgen): + "default vrf 3 peers same password with VRF prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + reset_with_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf") + check_all_peers_established("blue") + + +def test_vrf_prefix_peer_remove_passwords(tgen): + "selectively remove passwords checking state with VRF prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + reset_with_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf") + check_vrf_peer_remove_passwords(vrf="blue", prefix="yes") + + +def test_vrf_prefix_peer_change_passwords(tgen): + "selectively change passwords checking state with VRF prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + reset_with_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf") + check_vrf_peer_change_passwords(vrf="blue", prefix="yes") + + +def test_multiple_vrf_peer_established(tgen): + "default vrf 3 peers same password with multiple VRFs" + + reset_with_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf") + check_all_peers_established("blue") + check_all_peers_established("red") + + +def test_multiple_vrf_peer_remove_passwords(tgen): + "selectively remove passwords checking state with multiple VRFs" + + reset_with_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf") + check_vrf_peer_remove_passwords("blue") + check_all_peers_established("red") + check_vrf_peer_remove_passwords("red") + check_all_peers_established("blue") + + +def test_memory_leak(tgen): + "Run the memory leak test and report results." + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_auth/test_bgp_auth3.py b/tests/topotests/bgp_auth/test_bgp_auth3.py new file mode 100644 index 0000000..2237c6b --- /dev/null +++ b/tests/topotests/bgp_auth/test_bgp_auth3.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_auth.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# + +""" +test_bgp_auth.py: Test BGP Md5 Authentication + + +------+ + +--------| |--------+ + | +------| R1 |------+ | + | | -----| |----+ | | + | | | +------+ | | | + | | | | | | + +------+ +------+ + | |------------| | + | R2 |------------| R3 | + | |------------| | + +------+ +------+ + + +setup is 3 routers with 3 links between each each link in a different vrf +Default, blue and red respectively +Tests check various fiddling with passwords and checking that the peer +establishment is as expected and passwords are not leaked across sockets +for bgp instances +""" +# pylint: disable=C0413 + +import json +import os +import platform +import sys +from time import sleep + +import pytest +from lib import common_config, topotest +from lib.common_config import ( + save_initial_config_on_routers, + reset_with_new_configs, +) +from bgp_auth_common import ( + check_vrf_peer_change_passwords, + check_all_peers_established, + check_vrf_peer_remove_passwords, +) +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd] + +CWD = os.path.dirname(os.path.realpath(__file__)) + + +def build_topo(tgen): + tgen.add_router("R1") + tgen.add_router("R2") + tgen.add_router("R3") + + tgen.add_link(tgen.gears["R1"], tgen.gears["R2"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R2"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R2"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R2"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R2"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R2"], tgen.gears["R3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + + # blue vrf + r1.cmd_raises("ip link add blue type vrf table 1001") + r1.cmd_raises("ip link set up dev blue") + r2.cmd_raises("ip link add blue type vrf table 1001") + r2.cmd_raises("ip link set up dev blue") + r3.cmd_raises("ip link add blue type vrf table 1001") + r3.cmd_raises("ip link set up dev blue") + + r1.cmd_raises("ip link add lo1 type dummy") + r1.cmd_raises("ip link set lo1 master blue") + r1.cmd_raises("ip link set up dev lo1") + r2.cmd_raises("ip link add lo1 type dummy") + r2.cmd_raises("ip link set up dev lo1") + r2.cmd_raises("ip link set lo1 master blue") + r3.cmd_raises("ip link add lo1 type dummy") + r3.cmd_raises("ip link set up dev lo1") + r3.cmd_raises("ip link set lo1 master blue") + + r1.cmd_raises("ip link set R1-eth2 master blue") + r1.cmd_raises("ip link set R1-eth3 master blue") + r2.cmd_raises("ip link set R2-eth2 master blue") + r2.cmd_raises("ip link set R2-eth3 master blue") + r3.cmd_raises("ip link set R3-eth2 master blue") + r3.cmd_raises("ip link set R3-eth3 master blue") + + r1.cmd_raises("ip link set up dev R1-eth2") + r1.cmd_raises("ip link set up dev R1-eth3") + r2.cmd_raises("ip link set up dev R2-eth2") + r2.cmd_raises("ip link set up dev R2-eth3") + r3.cmd_raises("ip link set up dev R3-eth2") + r3.cmd_raises("ip link set up dev R3-eth3") + + # red vrf + r1.cmd_raises("ip link add red type vrf table 1002") + r1.cmd_raises("ip link set up dev red") + r2.cmd_raises("ip link add red type vrf table 1002") + r2.cmd_raises("ip link set up dev red") + r3.cmd_raises("ip link add red type vrf table 1002") + r3.cmd_raises("ip link set up dev red") + + r1.cmd_raises("ip link add lo2 type dummy") + r1.cmd_raises("ip link set lo2 master red") + r1.cmd_raises("ip link set up dev lo2") + r2.cmd_raises("ip link add lo2 type dummy") + r2.cmd_raises("ip link set up dev lo2") + r2.cmd_raises("ip link set lo2 master red") + r3.cmd_raises("ip link add lo2 type dummy") + r3.cmd_raises("ip link set up dev lo2") + r3.cmd_raises("ip link set lo2 master red") + + r1.cmd_raises("ip link set R1-eth4 master red") + r1.cmd_raises("ip link set R1-eth5 master red") + r2.cmd_raises("ip link set R2-eth4 master red") + r2.cmd_raises("ip link set R2-eth5 master red") + r3.cmd_raises("ip link set R3-eth4 master red") + r3.cmd_raises("ip link set R3-eth5 master red") + + r1.cmd_raises("ip link set up dev R1-eth4") + r1.cmd_raises("ip link set up dev R1-eth5") + r2.cmd_raises("ip link set up dev R2-eth4") + r2.cmd_raises("ip link set up dev R2-eth5") + r3.cmd_raises("ip link set up dev R3-eth4") + r3.cmd_raises("ip link set up dev R3-eth5") + + r1.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1") + r2.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1") + r3.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1") + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + router.load_config(TopoRouter.RD_OSPF, "") + router.load_config(TopoRouter.RD_BGP, "") + + # After copying the configurations, this function loads configured daemons. + tgen.start_router() + + # Save the initial router config. reset_config_on_routers will return to this config. + save_initial_config_on_routers(tgen) + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_prefix_peer_change_passwords(tgen): + "selecively change passwords checkig state with prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + reset_with_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf") + check_vrf_peer_change_passwords(prefix="yes") + + +def test_vrf_peer_established(tgen): + "default vrf 3 peers same password with VRF config" + + # clean routers and load vrf config + reset_with_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf") + check_all_peers_established("blue") + + +def test_vrf_peer_remove_passwords(tgen): + "selectively remove passwords checking state with VRF config" + + reset_with_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf") + check_vrf_peer_remove_passwords(vrf="blue") + + +def test_vrf_peer_change_passwords(tgen): + "selectively change passwords checking state with VRF config" + + reset_with_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf") + check_vrf_peer_change_passwords(vrf="blue") + + +def test_memory_leak(tgen): + "Run the memory leak test and report results." + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_auth/test_bgp_auth4.py b/tests/topotests/bgp_auth/test_bgp_auth4.py new file mode 100644 index 0000000..d6fe425 --- /dev/null +++ b/tests/topotests/bgp_auth/test_bgp_auth4.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_auth.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# + +""" +test_bgp_auth.py: Test BGP Md5 Authentication + + +------+ + +--------| |--------+ + | +------| R1 |------+ | + | | -----| |----+ | | + | | | +------+ | | | + | | | | | | + +------+ +------+ + | |------------| | + | R2 |------------| R3 | + | |------------| | + +------+ +------+ + + +setup is 3 routers with 3 links between each each link in a different vrf +Default, blue and red respectively +Tests check various fiddling with passwords and checking that the peer +establishment is as expected and passwords are not leaked across sockets +for bgp instances +""" +# pylint: disable=C0413 + +import json +import os +import platform +import sys +from time import sleep + +import pytest +from lib import common_config, topotest +from lib.common_config import ( + save_initial_config_on_routers, + reset_with_new_configs, +) +from bgp_auth_common import ( + check_vrf_peer_change_passwords, + check_all_peers_established, + check_vrf_peer_remove_passwords, +) +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd] + +CWD = os.path.dirname(os.path.realpath(__file__)) + + +def build_topo(tgen): + tgen.add_router("R1") + tgen.add_router("R2") + tgen.add_router("R3") + + tgen.add_link(tgen.gears["R1"], tgen.gears["R2"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R2"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R2"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R2"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R2"]) + tgen.add_link(tgen.gears["R1"], tgen.gears["R3"]) + tgen.add_link(tgen.gears["R2"], tgen.gears["R3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + + # blue vrf + r1.cmd_raises("ip link add blue type vrf table 1001") + r1.cmd_raises("ip link set up dev blue") + r2.cmd_raises("ip link add blue type vrf table 1001") + r2.cmd_raises("ip link set up dev blue") + r3.cmd_raises("ip link add blue type vrf table 1001") + r3.cmd_raises("ip link set up dev blue") + + r1.cmd_raises("ip link add lo1 type dummy") + r1.cmd_raises("ip link set lo1 master blue") + r1.cmd_raises("ip link set up dev lo1") + r2.cmd_raises("ip link add lo1 type dummy") + r2.cmd_raises("ip link set up dev lo1") + r2.cmd_raises("ip link set lo1 master blue") + r3.cmd_raises("ip link add lo1 type dummy") + r3.cmd_raises("ip link set up dev lo1") + r3.cmd_raises("ip link set lo1 master blue") + + r1.cmd_raises("ip link set R1-eth2 master blue") + r1.cmd_raises("ip link set R1-eth3 master blue") + r2.cmd_raises("ip link set R2-eth2 master blue") + r2.cmd_raises("ip link set R2-eth3 master blue") + r3.cmd_raises("ip link set R3-eth2 master blue") + r3.cmd_raises("ip link set R3-eth3 master blue") + + r1.cmd_raises("ip link set up dev R1-eth2") + r1.cmd_raises("ip link set up dev R1-eth3") + r2.cmd_raises("ip link set up dev R2-eth2") + r2.cmd_raises("ip link set up dev R2-eth3") + r3.cmd_raises("ip link set up dev R3-eth2") + r3.cmd_raises("ip link set up dev R3-eth3") + + # red vrf + r1.cmd_raises("ip link add red type vrf table 1002") + r1.cmd_raises("ip link set up dev red") + r2.cmd_raises("ip link add red type vrf table 1002") + r2.cmd_raises("ip link set up dev red") + r3.cmd_raises("ip link add red type vrf table 1002") + r3.cmd_raises("ip link set up dev red") + + r1.cmd_raises("ip link add lo2 type dummy") + r1.cmd_raises("ip link set lo2 master red") + r1.cmd_raises("ip link set up dev lo2") + r2.cmd_raises("ip link add lo2 type dummy") + r2.cmd_raises("ip link set up dev lo2") + r2.cmd_raises("ip link set lo2 master red") + r3.cmd_raises("ip link add lo2 type dummy") + r3.cmd_raises("ip link set up dev lo2") + r3.cmd_raises("ip link set lo2 master red") + + r1.cmd_raises("ip link set R1-eth4 master red") + r1.cmd_raises("ip link set R1-eth5 master red") + r2.cmd_raises("ip link set R2-eth4 master red") + r2.cmd_raises("ip link set R2-eth5 master red") + r3.cmd_raises("ip link set R3-eth4 master red") + r3.cmd_raises("ip link set R3-eth5 master red") + + r1.cmd_raises("ip link set up dev R1-eth4") + r1.cmd_raises("ip link set up dev R1-eth5") + r2.cmd_raises("ip link set up dev R2-eth4") + r2.cmd_raises("ip link set up dev R2-eth5") + r3.cmd_raises("ip link set up dev R3-eth4") + r3.cmd_raises("ip link set up dev R3-eth5") + + r1.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1") + r2.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1") + r3.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1") + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + router.load_config(TopoRouter.RD_OSPF, "") + router.load_config(TopoRouter.RD_BGP, "") + + # After copying the configurations, this function loads configured daemons. + tgen.start_router() + + # Save the initial router config. reset_config_on_routers will return to this config. + save_initial_config_on_routers(tgen) + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_multiple_vrf_peer_change_passwords(tgen): + "selectively change passwords checking state with multiple VRFs" + + reset_with_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf") + check_vrf_peer_change_passwords("blue") + check_all_peers_established("red") + check_vrf_peer_change_passwords("red") + check_all_peers_established("blue") + + +def test_multiple_vrf_prefix_peer_established(tgen): + "default vrf 3 peers same password with multilpe VRFs and prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + reset_with_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf") + check_all_peers_established("blue") + check_all_peers_established("red") + + +def test_multiple_vrf_prefix_peer_remove_passwords(tgen): + "selectively remove passwords checking state with multiple vrfs and prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + reset_with_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf") + check_vrf_peer_remove_passwords(vrf="blue", prefix="yes") + check_all_peers_established("red") + check_vrf_peer_remove_passwords(vrf="red", prefix="yes") + check_all_peers_established("blue") + + +def test_multiple_vrf_prefix_peer_change_passwords(tgen): + "selectively change passwords checking state with multiple vrfs and prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + reset_with_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf") + check_vrf_peer_change_passwords(vrf="blue", prefix="yes") + check_all_peers_established("red") + check_vrf_peer_change_passwords(vrf="red", prefix="yes") + check_all_peers_established("blue") + + +def test_memory_leak(tgen): + "Run the memory leak test and report results." + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_basic_functionality_topo1/__init__.py b/tests/topotests/bgp_basic_functionality_topo1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_basic_functionality_topo1/bgp_basic_functionality.json b/tests/topotests/bgp_basic_functionality_topo1/bgp_basic_functionality.json new file mode 100644 index 0000000..ee1f1b7 --- /dev/null +++ b/tests/topotests/bgp_basic_functionality_topo1/bgp_basic_functionality.json @@ -0,0 +1,115 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {}}}, + "r3": {"dest_link": {"r1": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {}}}, + "r3": {"dest_link": {"r1": {}}} + } + } + } + } + } + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + } + } + } + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {}}}, + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {}}}, + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": {"neighbor": {"r3": {"dest_link": {"r4": {}}}}} + }, + "ipv6": { + "unicast": {"neighbor": {"r3": {"dest_link": {"r4": {}}}}} + } + } + } + } + } +} diff --git a/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py b/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py new file mode 100644 index 0000000..c97fc5f --- /dev/null +++ b/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py @@ -0,0 +1,1169 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test BGP basic functionality: + +Test steps +- Create topology (setup module) + Creating 4 routers topology, r1, r2, r3 are in IBGP and + r3, r4 are in EBGP +- Bring up topology +- Verify for bgp to converge +- Modify/Delete and verify router-id +- Modify and verify bgp timers +- Create and verify static routes +- Modify and verify admin distance for existing static routes +- Test advertise network using network command +- Verify clear bgp +- Test bgp convergence with loopback interface +- Test advertise network using network command +- Verify routes not installed in zebra when /32 routes received + with loopback BGP session subnet +""" +# XXX clean up in later commit to avoid conflict on rebase +# pylint: disable=C0413 + +import os +import sys +import time +import pytest +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +from lib.bgp import ( + clear_bgp_and_verify, + create_router_bgp, + modify_as_number, + verify_as_numbers, + verify_bgp_convergence, + verify_bgp_rib, + verify_bgp_timers_and_functionality, + verify_router_id, +) +from lib.common_config import ( + addKernelRoute, + apply_raw_config, + check_address_types, + create_prefix_lists, + create_route_maps, + create_static_routes, + required_linux_kernel_version, + reset_config_on_routers, + start_topology, + step, + verify_admin_distance_for_static_routes, + verify_bgp_community, + verify_fib_routes, + verify_rib, + write_test_footer, + write_test_header, +) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +# Global Variable +KEEPALIVETIMER = 2 +HOLDDOWNTIMER = 6 +r1_ipv4_loopback = "1.0.1.0/24" +r2_ipv4_loopback = "1.0.2.0/24" +r3_ipv4_loopback = "1.0.3.0/24" +r4_ipv4_loopback = "1.0.4.0/24" +r1_ipv6_loopback = "2001:db8:f::1:0/120" +r2_ipv6_loopback = "2001:db8:f::2:0/120" +r3_ipv6_loopback = "2001:db8:f::3:0/120" +r4_ipv6_loopback = "2001:db8:f::4:0/120" +NETWORK = { + "ipv4": ["100.1.1.1/32", "100.1.1.2/32"], + "ipv6": ["100::1/128", "100::2/128"], +} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >=4.15") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_basic_functionality.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global ADDR_TYPES + global BGP_CONVERGENCE + ADDR_TYPES = check_address_types() + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_modify_and_delete_router_id(request): + """Test to modify, delete and verify router-id.""" + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Modify router id + input_dict = { + "r1": {"bgp": {"router_id": "12.12.12.12"}}, + "r2": {"bgp": {"router_id": "22.22.22.22"}}, + "r3": {"bgp": {"router_id": "33.33.33.33"}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Verifying router id once modified + result = verify_router_id(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Delete router id + input_dict = { + "r1": {"bgp": {"del_router_id": True}}, + "r2": {"bgp": {"del_router_id": True}}, + "r3": {"bgp": {"del_router_id": True}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Verifying router id once deleted + # Once router-id is deleted, highest interface ip should become + # router-id + result = verify_router_id(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_bgp_config_with_4byte_as_number(request): + """ + Configure BGP with 4 byte ASN and verify it works fine + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + input_dict = { + "r1": {"bgp": {"local_as": 131079}}, + "r2": {"bgp": {"local_as": 131079}}, + "r3": {"bgp": {"local_as": 131079}}, + "r4": {"bgp": {"local_as": 131080}}, + } + result = modify_as_number(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + result = verify_as_numbers(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_BGP_config_with_invalid_ASN_p2(request): + """ + Configure BGP with invalid ASN(ex - 0, reserved ASN) and verify test case + ended up with error + """ + + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to modify AS number + input_dict = { + "r1": { + "bgp": { + "local_as": 0, + } + }, + "r2": { + "bgp": { + "local_as": 0, + } + }, + "r3": { + "bgp": { + "local_as": 0, + } + }, + "r4": { + "bgp": { + "local_as": 64000, + } + }, + } + result = modify_as_number(tgen, topo, input_dict) + assert ( + result is not True + ), "Expected BGP config is not created because of invalid ASNs: {}".format(result) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + result = verify_bgp_convergence(tgen, topo) + if result != True: + assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result) + + write_test_footer(tc_name) + + +def test_BGP_config_with_2byteAS_and_4byteAS_number_p1(request): + """ + Configure BGP with 4 byte and 2 byte ASN and verify BGP is converged + """ + + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + result = verify_bgp_convergence(tgen, topo) + if result != True: + assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result) + + # Api call to modify AS number + input_dict = { + "r1": {"bgp": {"local_as": 131079}}, + "r2": {"bgp": {"local_as": 131079}}, + "r3": {"bgp": {"local_as": 131079}}, + "r4": {"bgp": {"local_as": 111}}, + } + result = modify_as_number(tgen, topo, input_dict) + if result != True: + assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result) + + result = verify_as_numbers(tgen, topo, input_dict) + if result != True: + assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result) + + # Api call verify whether BGP is converged + result = verify_bgp_convergence(tgen, topo) + if result != True: + assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result) + + write_test_footer(tc_name) + + +def test_bgp_timers_functionality(request): + """ + Test to modify bgp timers and verify timers functionality. + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to modify BGP timerse + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, deepcopy(input_dict)) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Api call to clear bgp, so timer modification would take place + clear_bgp_and_verify(tgen, topo, "r1") + + # Verifying bgp timers functionality + result = verify_bgp_timers_and_functionality(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_static_routes(request): + """Test to create and verify static routes.""" + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to create static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": "10.0.20.1/32", + "no_of_ip": 9, + "admin_distance": 100, + "next_hop": "10.0.0.2", + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + next_hop = ["10.0.0.2", "10.0.0.5"] + result = verify_rib( + tgen, "ipv4", dut, input_dict, next_hop=next_hop, protocol=protocol + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_admin_distance_for_existing_static_routes(request): + """Test to modify and verify admin distance for existing static routes.""" + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + input_dict = { + "r1": { + "static_routes": [ + { + "network": "10.0.20.1/32", + "admin_distance": 10, + "next_hop": "10.0.0.2", + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Verifying admin distance once modified + result = verify_admin_distance_for_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_advertise_network_using_network_command(request): + """Test advertise networks using network command.""" + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "20.0.0.0/32", "no_of_network": 10}, + {"network": "30.0.0.0/32", "no_of_network": 10}, + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r2" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_clear_bgp_and_verify(request): + """ + Created few static routes and verified all routes are learned via BGP + cleared BGP and verified all routes are intact + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # clear ip bgp + result = clear_bgp_and_verify(tgen, topo, "r1") + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_BGP_attributes_with_vrf_default_keyword_p0(request): + """ + TC_9: + Verify BGP functionality for default vrf with + "vrf default" keyword. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Configure static routes and redistribute in BGP on R3") + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type][0], + "no_of_ip": 4, + "next_hop": "Null0", + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Create a route-map to match a specific prefix and modify" + "BGP attributes for matched prefix" + ) + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "ABC": [ + { + "seqid": 10, + "action": "permit", + "network": NETWORK["ipv4"][0], + } + ] + }, + "ipv6": { + "XYZ": [ + { + "seqid": 100, + "action": "permit", + "network": NETWORK["ipv6"][0], + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + if addr_type == "ipv4": + pf_list = "ABC" + else: + pf_list = "XYZ" + + input_dict_6 = { + "r3": { + "route_maps": { + "BGP_ATTR_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 10, + "match": {addr_type: {"prefix_lists": pf_list}}, + "set": { + "aspath": {"as_num": 500, "as_action": "prepend"}, + "localpref": 500, + "origin": "egp", + "community": {"num": "500:500", "action": "additive"}, + "large_community": { + "num": "500:500:500", + "action": "additive", + }, + }, + }, + {"action": "permit", "seq_id": 20}, + ] + }, + "BGP_ATTR_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 100, + "match": {addr_type: {"prefix_lists": pf_list}}, + "set": { + "aspath": {"as_num": 500, "as_action": "prepend"}, + "localpref": 500, + "origin": "egp", + "community": {"num": "500:500", "action": "additive"}, + "large_community": { + "num": "500:500:500", + "action": "additive", + }, + }, + }, + {"action": "permit", "seq_id": 200}, + ], + } + } + + result = create_route_maps(tgen, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Apply the route-map on R3 in outbound direction for peer R4") + + input_dict_7 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "BGP_ATTR_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "BGP_ATTR_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "verify modified attributes for specific prefix with 'vrf default'" + "keyword on R4" + ) + for addr_type in ADDR_TYPES: + dut = "r4" + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type][0], + "vrf": "default", + "largeCommunity": "500:500:500", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + dut = "r4" + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type][0], + "vrf": "default", + "community": "500:500", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_4 = {"largeCommunity": "500:500:500", "community": "500:500"} + + result = verify_bgp_community( + tgen, addr_type, dut, [NETWORK[addr_type][0]], input_dict_4 + ) + assert result is True, "Test case {} : Should fail \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_bgp_with_loopback_interface(request): + """ + Test BGP with loopback interface + + Adding keys:value pair "dest_link": "lo" and "source_link": "lo" + peer dict of input json file for all router's creating config using + loopback interface. Once BGP neighboship is up then verifying BGP + convergence + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + for routerN in sorted(topo["routers"].keys()): + for bgp_neighbor in topo["routers"][routerN]["bgp"]["address_family"]["ipv4"][ + "unicast" + ]["neighbor"].keys(): + + # Adding ['source_link'] = 'lo' key:value pair + topo["routers"][routerN]["bgp"]["address_family"]["ipv4"]["unicast"][ + "neighbor" + ][bgp_neighbor]["dest_link"] = { + "lo": { + "source_link": "lo", + } + } + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + input_dict = { + "r1": { + "static_routes": [ + {"network": "1.0.2.17/32", "next_hop": "10.0.0.2"}, + {"network": "1.0.3.17/32", "next_hop": "10.0.0.6"}, + ] + }, + "r2": { + "static_routes": [ + {"network": "1.0.1.17/32", "next_hop": "10.0.0.1"}, + {"network": "1.0.3.17/32", "next_hop": "10.0.0.10"}, + ] + }, + "r3": { + "static_routes": [ + {"network": "1.0.1.17/32", "next_hop": "10.0.0.5"}, + {"network": "1.0.2.17/32", "next_hop": "10.0.0.9"}, + {"network": "1.0.4.17/32", "next_hop": "10.0.0.14"}, + ] + }, + "r4": {"static_routes": [{"network": "1.0.3.17/32", "next_hop": "10.0.0.13"}]}, + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Api call verify whether BGP is converged + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_bgp_with_loopback_with_same_subnet_p1(request): + """ + Verify routes not installed in zebra when /32 routes received + with loopback BGP session subnet + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + step("Delete BGP seesion created initially") + input_dict_r1 = { + "r1": {"bgp": {"delete": True}}, + "r2": {"bgp": {"delete": True}}, + "r3": {"bgp": {"delete": True}}, + "r4": {"bgp": {"delete": True}}, + } + result = create_router_bgp(tgen, topo, input_dict_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create BGP session over loop address") + topo_modify = deepcopy(topo) + + for routerN in sorted(topo["routers"].keys()): + for addr_type in ADDR_TYPES: + for bgp_neighbor in topo_modify["routers"][routerN]["bgp"][ + "address_family" + ][addr_type]["unicast"]["neighbor"].keys(): + + # Adding ['source_link'] = 'lo' key:value pair + topo_modify["routers"][routerN]["bgp"]["address_family"][addr_type][ + "unicast" + ]["neighbor"][bgp_neighbor]["dest_link"] = { + "lo": {"source_link": "lo", "ebgp_multihop": 2} + } + + result = create_router_bgp(tgen, topo_modify["routers"]) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Disable IPv6 BGP nbr from ipv4 address family") + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r1"]["bgp"]["local_as"]), + "address-family ipv4 unicast", + "no neighbor {} activate".format( + topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0] + ), + "no neighbor {} activate".format( + topo["routers"]["r3"]["links"]["lo"]["ipv6"].split("/")[0] + ), + ] + }, + "r2": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r2"]["bgp"]["local_as"]), + "address-family ipv4 unicast", + "no neighbor {} activate".format( + topo["routers"]["r1"]["links"]["lo"]["ipv6"].split("/")[0] + ), + "no neighbor {} activate".format( + topo["routers"]["r3"]["links"]["lo"]["ipv6"].split("/")[0] + ), + ] + }, + "r3": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r3"]["bgp"]["local_as"]), + "address-family ipv4 unicast", + "no neighbor {} activate".format( + topo["routers"]["r1"]["links"]["lo"]["ipv6"].split("/")[0] + ), + "no neighbor {} activate".format( + topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0] + ), + "no neighbor {} activate".format( + topo["routers"]["r4"]["links"]["lo"]["ipv6"].split("/")[0] + ), + ] + }, + "r4": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r4"]["bgp"]["local_as"]), + "address-family ipv4 unicast", + "no neighbor {} activate".format( + topo["routers"]["r3"]["links"]["lo"]["ipv6"].split("/")[0] + ), + ] + }, + } + + step("Configure kernel routes") + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + r1_ipv4_lo = topo["routers"]["r1"]["links"]["lo"]["ipv4"] + r1_ipv6_lo = topo["routers"]["r1"]["links"]["lo"]["ipv6"] + r2_ipv4_lo = topo["routers"]["r2"]["links"]["lo"]["ipv4"] + r2_ipv6_lo = topo["routers"]["r2"]["links"]["lo"]["ipv6"] + r3_ipv4_lo = topo["routers"]["r3"]["links"]["lo"]["ipv4"] + r3_ipv6_lo = topo["routers"]["r3"]["links"]["lo"]["ipv6"] + r4_ipv4_lo = topo["routers"]["r4"]["links"]["lo"]["ipv4"] + r4_ipv6_lo = topo["routers"]["r4"]["links"]["lo"]["ipv6"] + + r1_r2 = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0] + r2_r1 = topo["routers"]["r2"]["links"]["r1"]["ipv6"].split("/")[0] + r1_r3 = topo["routers"]["r1"]["links"]["r3"]["ipv6"].split("/")[0] + r3_r1 = topo["routers"]["r3"]["links"]["r1"]["ipv6"].split("/")[0] + r2_r3 = topo["routers"]["r2"]["links"]["r3"]["ipv6"].split("/")[0] + r3_r2 = topo["routers"]["r3"]["links"]["r2"]["ipv6"].split("/")[0] + r3_r4 = topo["routers"]["r3"]["links"]["r4"]["ipv6"].split("/")[0] + r4_r3 = topo["routers"]["r4"]["links"]["r3"]["ipv6"].split("/")[0] + + r1_r2_ipv4 = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0] + r2_r1_ipv4 = topo["routers"]["r2"]["links"]["r1"]["ipv4"].split("/")[0] + r1_r3_ipv4 = topo["routers"]["r1"]["links"]["r3"]["ipv4"].split("/")[0] + r3_r1_ipv4 = topo["routers"]["r3"]["links"]["r1"]["ipv4"].split("/")[0] + r2_r3_ipv4 = topo["routers"]["r2"]["links"]["r3"]["ipv4"].split("/")[0] + r3_r2_ipv4 = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0] + r3_r4_ipv4 = topo["routers"]["r3"]["links"]["r4"]["ipv4"].split("/")[0] + r4_r3_ipv4 = topo["routers"]["r4"]["links"]["r3"]["ipv4"].split("/")[0] + + r1_r2_intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + r2_r1_intf = topo["routers"]["r2"]["links"]["r1"]["interface"] + r1_r3_intf = topo["routers"]["r1"]["links"]["r3"]["interface"] + r3_r1_intf = topo["routers"]["r3"]["links"]["r1"]["interface"] + r2_r3_intf = topo["routers"]["r2"]["links"]["r3"]["interface"] + r3_r2_intf = topo["routers"]["r3"]["links"]["r2"]["interface"] + r3_r4_intf = topo["routers"]["r3"]["links"]["r4"]["interface"] + r4_r3_intf = topo["routers"]["r4"]["links"]["r3"]["interface"] + + ipv4_list = [ + ("r1", r1_r2_intf, r2_ipv4_loopback), + ("r1", r1_r3_intf, r3_ipv4_loopback), + ("r2", r2_r1_intf, r1_ipv4_loopback), + ("r2", r2_r3_intf, r3_ipv4_loopback), + ("r3", r3_r1_intf, r1_ipv4_loopback), + ("r3", r3_r2_intf, r2_ipv4_loopback), + ("r3", r3_r4_intf, r4_ipv4_loopback), + ("r4", r4_r3_intf, r3_ipv4_loopback), + ] + + ipv6_list = [ + ("r1", r1_r2_intf, r2_ipv6_loopback, r2_r1), + ("r1", r1_r3_intf, r3_ipv6_loopback, r3_r1), + ("r2", r2_r1_intf, r1_ipv6_loopback, r1_r2), + ("r2", r2_r3_intf, r3_ipv6_loopback, r3_r2), + ("r3", r3_r1_intf, r1_ipv6_loopback, r1_r3), + ("r3", r3_r2_intf, r2_ipv6_loopback, r2_r3), + ("r3", r3_r4_intf, r4_ipv6_loopback, r4_r3), + ("r4", r4_r3_intf, r3_ipv6_loopback, r3_r4), + ] + + for dut, intf, loop_addr in ipv4_list: + result = addKernelRoute(tgen, dut, intf, loop_addr) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + for dut, intf, loop_addr, next_hop in ipv6_list: + result = addKernelRoute(tgen, dut, intf, loop_addr, next_hop) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("Configure static routes") + + input_dict = { + "r1": { + "static_routes": [ + {"network": r2_ipv4_loopback, "next_hop": r2_r1_ipv4}, + {"network": r3_ipv4_loopback, "next_hop": r3_r1_ipv4}, + {"network": r2_ipv6_loopback, "next_hop": r2_r1}, + {"network": r3_ipv6_loopback, "next_hop": r3_r1}, + ] + }, + "r2": { + "static_routes": [ + {"network": r1_ipv4_loopback, "next_hop": r1_r2_ipv4}, + {"network": r3_ipv4_loopback, "next_hop": r3_r2_ipv4}, + {"network": r1_ipv6_loopback, "next_hop": r1_r2}, + {"network": r3_ipv6_loopback, "next_hop": r3_r2}, + ] + }, + "r3": { + "static_routes": [ + {"network": r1_ipv4_loopback, "next_hop": r1_r3_ipv4}, + {"network": r2_ipv4_loopback, "next_hop": r2_r3_ipv4}, + {"network": r4_ipv4_loopback, "next_hop": r4_r3_ipv4}, + {"network": r1_ipv6_loopback, "next_hop": r1_r3}, + {"network": r2_ipv6_loopback, "next_hop": r2_r3}, + {"network": r4_ipv6_loopback, "next_hop": r4_r3}, + ] + }, + "r4": { + "static_routes": [ + {"network": r3_ipv4_loopback, "next_hop": r3_r4_ipv4}, + {"network": r3_ipv6_loopback, "next_hop": r3_r4}, + ] + }, + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP session convergence") + + result = verify_bgp_convergence(tgen, topo_modify) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure redistribute connected on R2 and R4") + input_dict_1 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify Ipv4 and Ipv6 network installed in R1 RIB but not in FIB") + input_dict_r1 = { + "r1": { + "static_routes": [ + {"network": "1.0.2.17/32"}, + {"network": "2001:db8:f::2:17/128"}, + ] + } + } + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_fib_routes( + tgen, addr_type, dut, input_dict_r1, expected=False + ) # pylint: disable=E1123 + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + step("Verify Ipv4 and Ipv6 network installed in r3 RIB but not in FIB") + input_dict_r3 = { + "r3": { + "static_routes": [ + {"network": "1.0.4.17/32"}, + {"network": "2001:db8:f::4:17/128"}, + ] + } + } + dut = "r3" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_fib_routes( + tgen, addr_type, dut, input_dict_r1, expected=False + ) # pylint: disable=E1123 + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_bfd_down_cease_notification/__init__.py b/tests/topotests/bgp_bfd_down_cease_notification/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r1/bfdd.conf b/tests/topotests/bgp_bfd_down_cease_notification/r1/bfdd.conf new file mode 100644 index 0000000..0ae384e --- /dev/null +++ b/tests/topotests/bgp_bfd_down_cease_notification/r1/bfdd.conf @@ -0,0 +1,6 @@ +bfd + peer 192.168.255.2 interface r1-eth0 + exit + ! +exit +! diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf b/tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf new file mode 100644 index 0000000..e855f75 --- /dev/null +++ b/tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as external + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 timers connect 1 + neighbor 192.168.255.2 bfd + neighbor 192.168.255.2 passive + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r1/zebra.conf b/tests/topotests/bgp_bfd_down_cease_notification/r1/zebra.conf new file mode 100644 index 0000000..091794f --- /dev/null +++ b/tests/topotests/bgp_bfd_down_cease_notification/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.1/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r2/bfdd.conf b/tests/topotests/bgp_bfd_down_cease_notification/r2/bfdd.conf new file mode 100644 index 0000000..dcd5033 --- /dev/null +++ b/tests/topotests/bgp_bfd_down_cease_notification/r2/bfdd.conf @@ -0,0 +1,6 @@ +bfd + peer 192.168.255.1 interface r2-eth0 + exit + ! +exit +! diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf b/tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf new file mode 100644 index 0000000..faf2c6b --- /dev/null +++ b/tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as external + neighbor 192.168.255.1 timers 3 10 + neighbor 192.168.255.1 timers connect 1 + neighbor 192.168.255.1 bfd + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r2/zebra.conf b/tests/topotests/bgp_bfd_down_cease_notification/r2/zebra.conf new file mode 100644 index 0000000..1fcccbe --- /dev/null +++ b/tests/topotests/bgp_bfd_down_cease_notification/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.2/32 +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py b/tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py new file mode 100644 index 0000000..0014298 --- /dev/null +++ b/tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# bgp_bfd_down_cease_notification.py +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Check if Cease/BFD Down notification message is sent/received +when the BFD is down. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import kill_router_daemons, step + +pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_bfd_down_notification(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + "peerBfdInfo": {"status": "Up"}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_bfd_down_notification(): + output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "lastNotificationReason": "Cease/BFD Down", + "lastNotificationHardReset": True, + } + } + return topotest.json_cmp(output, expected) + + step("Initial BGP converge") + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Failed to see BGP convergence on R2" + + step("Kill bfdd on R2") + kill_router_daemons(tgen, "r2", ["bfdd"]) + + step("Check if we received Cease/BFD Down notification message") + test_func = functools.partial(_bgp_bfd_down_notification) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Failed to see BGP Cease/BFD Down notification message on R2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_blackhole_community/__init__.py b/tests/topotests/bgp_blackhole_community/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_blackhole_community/r1/bgpd.conf b/tests/topotests/bgp_blackhole_community/r1/bgpd.conf new file mode 100644 index 0000000..574d199 --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r1/bgpd.conf @@ -0,0 +1,14 @@ +! +router bgp 65001 + timers bgp 3 9 + no bgp ebgp-requires-policy + neighbor r1-eth0 interface remote-as external + address-family ipv4 unicast + redistribute connected + neighbor r1-eth0 route-map r2 out + exit-address-family + ! +! +route-map r2 permit 10 + set community blackhole +! diff --git a/tests/topotests/bgp_blackhole_community/r1/zebra.conf b/tests/topotests/bgp_blackhole_community/r1/zebra.conf new file mode 100644 index 0000000..70dc5e5 --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r1/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.0.1/24 +! +ip forwarding +! + diff --git a/tests/topotests/bgp_blackhole_community/r2/bgpd.conf b/tests/topotests/bgp_blackhole_community/r2/bgpd.conf new file mode 100644 index 0000000..2260613 --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r2/bgpd.conf @@ -0,0 +1,8 @@ +! +router bgp 65002 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor r2-eth0 interface remote-as external + neighbor r2-eth1 interface remote-as external + neighbor r2-eth2 interface remote-as internal +! diff --git a/tests/topotests/bgp_blackhole_community/r2/zebra.conf b/tests/topotests/bgp_blackhole_community/r2/zebra.conf new file mode 100644 index 0000000..cf6fb6d --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r2/zebra.conf @@ -0,0 +1,12 @@ +! +interface r2-eth0 + ip address 192.168.0.2/24 +! +interface r2-eth1 + ip address 192.168.1.1/24 +! +interface r2-eth2 + ip address 192.168.2.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_blackhole_community/r3/bgpd.conf b/tests/topotests/bgp_blackhole_community/r3/bgpd.conf new file mode 100644 index 0000000..d0c4b40 --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r3/bgpd.conf @@ -0,0 +1,6 @@ +! +router bgp 65003 + timers bgp 3 9 + no bgp ebgp-requires-policy + neighbor r3-eth0 interface remote-as external +! diff --git a/tests/topotests/bgp_blackhole_community/r3/zebra.conf b/tests/topotests/bgp_blackhole_community/r3/zebra.conf new file mode 100644 index 0000000..05ab56d --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r3/zebra.conf @@ -0,0 +1,6 @@ +! +interface r3-eth0 + ip address 192.168.1.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_blackhole_community/r4/bgpd.conf b/tests/topotests/bgp_blackhole_community/r4/bgpd.conf new file mode 100644 index 0000000..eca12bd --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r4/bgpd.conf @@ -0,0 +1,13 @@ +! +router bgp 65002 + timers bgp 3 9 + no bgp ebgp-requires-policy + neighbor r4-eth0 interface remote-as internal +! +address-family ipv4 unicast + neighbor r4-eth0 route-map FOO in +exit-address-family +! +route-map FOO permit 10 + set ipv6 next-hop local fe80::202:ff:fe00:99 +exit diff --git a/tests/topotests/bgp_blackhole_community/r4/zebra.conf b/tests/topotests/bgp_blackhole_community/r4/zebra.conf new file mode 100644 index 0000000..e2ccaed --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r4/zebra.conf @@ -0,0 +1,6 @@ +! +interface r4-eth0 + ip address 192.168.2.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py b/tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py new file mode 100644 index 0000000..9f5c0ef --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by +# Donatas Abraitis +# + +""" +Test if 172.16.255.254/32 tagged with BLACKHOLE community is not +re-advertised downstream outside local AS. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_blackhole_community(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(): + output = json.loads( + tgen.gears["r2"].vtysh_cmd("show ip bgp 172.16.255.254/32 json") + ) + expected = {"paths": [{"community": {"list": ["blackhole", "noExport"]}}]} + return topotest.json_cmp(output, expected) + + def _bgp_no_advertise_ebgp(): + output = json.loads( + tgen.gears["r2"].vtysh_cmd( + "show ip bgp neighbor r2-eth1 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": {}, + "totalPrefixCounter": 0, + "filteredPrefixCounter": 0, + } + + return topotest.json_cmp(output, expected) + + def _bgp_no_advertise_ibgp(): + output = json.loads( + tgen.gears["r2"].vtysh_cmd( + "show ip bgp neighbor r2-eth2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": {"172.16.255.254/32": {}}, + "totalPrefixCounter": 2, + } + + return topotest.json_cmp(output, expected) + + def _bgp_verify_nexthop_validity(): + output = json.loads(tgen.gears["r4"].vtysh_cmd("show bgp nexthop json")) + + expected = { + "ipv6": { + "fe80::202:ff:fe00:99": { + "valid": True, + "complete": True, + "igpMetric": 0, + "pathCount": 2, + "nexthops": [{"interfaceName": "r4-eth0"}], + }, + } + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r2"]) + + step("Check if 172.16.255.254/32 is not advertised to eBGP peers") + + test_func = functools.partial(_bgp_no_advertise_ebgp) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert ( + result is None + ), 'Advertised blackhole tagged prefix to eBGP peers in "{}"'.format( + tgen.gears["r2"] + ) + + step("Check if 172.16.255.254/32 is advertised to iBGP peers") + test_func = functools.partial(_bgp_no_advertise_ibgp) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert ( + result is None + ), 'Withdrawn blackhole tagged prefix to iBGP peers in "{}"'.format( + tgen.gears["r2"] + ) + + step("Verify if the nexthop set via route-map on r4 is marked valid") + test_func = functools.partial(_bgp_verify_nexthop_validity) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, 'Nexthops are not valid "{}"'.format(tgen.gears["r4"]) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_bmp/__init__.py b/tests/topotests/bgp_bmp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_bmp/r1/bgpd.conf b/tests/topotests/bgp_bmp/r1/bgpd.conf new file mode 100644 index 0000000..69acf6e --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 65501 + bgp router-id 192.168.0.1 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 192.168.0.2 remote-as 65502 + neighbor 192:168::2 remote-as 65502 +! + bmp targets bmp1 + bmp connect 192.0.178.10 port 1789 min-retry 100 max-retry 10000 + exit +! + address-family ipv4 unicast + neighbor 192.168.0.2 activate + neighbor 192.168.0.2 soft-reconfiguration inbound + no neighbor 192:168::2 activate + exit-address-family +! + address-family ipv6 unicast + neighbor 192:168::2 activate + neighbor 192:168::2 soft-reconfiguration inbound + exit-address-family +! diff --git a/tests/topotests/bgp_bmp/r1/zebra.conf b/tests/topotests/bgp_bmp/r1/zebra.conf new file mode 100644 index 0000000..6a25a6f --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/zebra.conf @@ -0,0 +1,7 @@ +interface r1-eth0 + ip address 192.0.178.1/24 +! +interface r1-eth1 + ip address 192.168.0.1/24 + ipv6 address 192:168::1/64 +! diff --git a/tests/topotests/bgp_bmp/r2/bgpd.conf b/tests/topotests/bgp_bmp/r2/bgpd.conf new file mode 100644 index 0000000..7c8255a --- /dev/null +++ b/tests/topotests/bgp_bmp/r2/bgpd.conf @@ -0,0 +1,19 @@ +router bgp 65502 + bgp router-id 192.168.0.2 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.0.1 remote-as 65501 + neighbor 192:168::1 remote-as 65501 +! + address-family ipv4 unicast + neighbor 192.168.0.1 activate + no neighbor 192:168::1 activate + redistribute connected + exit-address-family +! + address-family ipv6 unicast + neighbor 192:168::1 activate + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_bmp/r2/zebra.conf b/tests/topotests/bgp_bmp/r2/zebra.conf new file mode 100644 index 0000000..9d82bfe --- /dev/null +++ b/tests/topotests/bgp_bmp/r2/zebra.conf @@ -0,0 +1,8 @@ +interface r2-eth0 + ip address 192.168.0.2/24 + ipv6 address 192:168::2/64 +! +interface r2-eth1 + ip address 172.31.0.2/24 + ipv6 address 172:31::2/64 +! diff --git a/tests/topotests/bgp_bmp/test_bgp_bmp.py b/tests/topotests/bgp_bmp/test_bgp_bmp.py new file mode 100644 index 0000000..65f191b --- /dev/null +++ b/tests/topotests/bgp_bmp/test_bgp_bmp.py @@ -0,0 +1,246 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright 2023 6WIND S.A. +# Authored by Farid Mihoub +# + +""" +test_bgp_bmp.py: Test BGP BMP functionalities + + +------+ +------+ +------+ + | | | | | | + | BMP1 |------------| R1 |---------------| R2 | + | | | | | | + +------+ +------+ +------+ + +Setup two routers R1 and R2 with one link configured with IPv4 and +IPv6 addresses. +Configure BGP in R1 and R2 to exchange prefixes from +the latter to the first router. +Setup a link between R1 and the BMP server, activate the BMP feature in R1 +and ensure the monitored BGP sessions logs are well present on the BMP server. +""" + +from functools import partial +from ipaddress import ip_network +import json +import os +import platform +import pytest +import sys + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join("../")) +sys.path.append(os.path.join("../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.bgp import verify_bgp_convergence_from_running_config +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + +# remember the last sequence number of the logging messages +SEQ = 0 + +PRE_POLICY = "pre-policy" +POST_POLICY = "post-policy" + + +def build_topo(tgen): + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_bmp_server("bmp1", ip="192.0.178.10", defaultRoute="via 192.0.178.1") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["bmp1"]) + + tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "r1-eth1", "r2-eth0") + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + for rname, router in tgen.routers().items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, "{}/bgpd.conf".format(rname)), + "-M bmp", + ) + + tgen.start_router() + + logger.info("starting BMP servers") + for _, server in tgen.get_bmp_servers().items(): + server.start() + + +def teardown_module(_mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_convergence(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + result = verify_bgp_convergence_from_running_config(tgen, dut="r1") + assert result is True, "BGP is not converging" + + +def get_bmp_messages(): + """ + Read the BMP logging messages. + """ + messages = [] + tgen = get_topogen() + text_output = tgen.gears["bmp1"].run("cat /var/log/bmp.log") + + for m in text_output.splitlines(): + # some output in the bash can break the message decoding + try: + messages.append(json.loads(m)) + except Exception as e: + logger.warning(str(e) + " message: {}".format(str(m))) + continue + + if not messages: + logger.error("Bad BMP log format, check your BMP server") + + return messages + + +def check_for_prefixes(expected_prefixes, bmp_log_type, post_policy): + """ + Check for the presence of the given prefixes in the BMP server logs with + the given message type and the set policy. + """ + global SEQ + # we care only about the new messages + messages = [ + m for m in sorted(get_bmp_messages(), key=lambda d: d["seq"]) if m["seq"] > SEQ + ] + + # get the list of pairs (prefix, policy, seq) for the given message type + prefixes = [ + m["ip_prefix"] + for m in messages + if "ip_prefix" in m.keys() + and "bmp_log_type" in m.keys() + and m["bmp_log_type"] == bmp_log_type + and m["post_policy"] == post_policy + ] + + # check for prefixes + for ep in expected_prefixes: + if ep not in prefixes: + msg = "The prefix {} is not present in the {} log messages." + logger.debug(msg.format(ep, bmp_log_type)) + return False + + SEQ = messages[-1]["seq"] + return True + + +def set_bmp_policy(tgen, node, asn, target, safi, policy, vrf=None): + """ + Configure the bmp policy. + """ + vrf = " vrf {}" if vrf else "" + cmd = [ + "con t\n", + "router bgp {}{}\n".format(asn, vrf), + "bmp targets {}\n".format(target), + "bmp monitor ipv4 {} {}\n".format(safi, policy), + "bmp monitor ipv6 {} {}\n".format(safi, policy), + "end\n", + ] + tgen.gears[node].vtysh_cmd("".join(cmd)) + + +def configure_prefixes(tgen, node, asn, safi, prefixes, vrf=None, update=True): + """ + Configure the bgp prefixes. + """ + withdraw = "no " if not update else "" + vrf = " vrf {}" if vrf else "" + for p in prefixes: + ip = ip_network(p) + cmd = [ + "conf t\n", + "router bgp {}{}\n".format(asn, vrf), + "address-family ipv{} {}\n".format(ip.version, safi), + "{}network {}\n".format(withdraw, ip), + "exit-address-family\n", + ] + logger.debug("setting prefix: ipv{} {} {}".format(ip.version, safi, ip)) + tgen.gears[node].vtysh_cmd("".join(cmd)) + + +def unicast_prefixes(policy): + """ + Setup the BMP monitor policy, Add and withdraw ipv4/v6 prefixes. + Check if the previous actions are logged in the BMP server with the right + message type and the right policy. + """ + tgen = get_topogen() + set_bmp_policy(tgen, "r1", 65501, "bmp1", "unicast", policy) + + prefixes = ["172.31.0.15/32", "2111::1111/128"] + # add prefixes + configure_prefixes(tgen, "r2", 65502, "unicast", prefixes) + + logger.info("checking for updated prefixes") + # check + test_func = partial(check_for_prefixes, prefixes, "update", policy == POST_POLICY) + success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + assert success, "Checking the updated prefixes has been failed !." + + # withdraw prefixes + configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, update=False) + logger.info("checking for withdrawed prefxies") + # check + test_func = partial(check_for_prefixes, prefixes, "withdraw", policy == POST_POLICY) + success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + assert success, "Checking the withdrawed prefixes has been failed !." + + +def test_bmp_server_logging(): + """ + Assert the logging of the bmp server. + """ + + def check_for_log_file(): + tgen = get_topogen() + output = tgen.gears["bmp1"].run("ls /var/log/") + if "bmp.log" not in output: + return False + return True + + success, _ = topotest.run_and_expect(check_for_log_file, True, wait=0.5) + assert success, "The BMP server is not logging" + + +def test_bmp_bgp_unicast(): + """ + Add/withdraw bgp unicast prefixes and check the bmp logs. + """ + logger.info("*** Unicast prefixes pre-policy logging ***") + unicast_prefixes(PRE_POLICY) + logger.info("*** Unicast prefixes post-policy logging ***") + unicast_prefixes(POST_POLICY) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_color_extcommunities/__init__.py b/tests/topotests/bgp_color_extcommunities/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf b/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf new file mode 100644 index 0000000..d4ca392 --- /dev/null +++ b/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf @@ -0,0 +1,17 @@ +! +router bgp 65001 + bgp router-id 192.168.1.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as external + address-family ipv4 unicast + network 10.10.10.10/24 route-map rmap + neighbor 192.168.1.2 route-map rmap out + neighbor 192.168.1.2 activate + exit-address-family +! +route-map rmap permit 10 + set extcommunity color 1 + set extcommunity rt 80:987 + set extcommunity color 100 55555 200 +exit diff --git a/tests/topotests/bgp_color_extcommunities/r1/zebra.conf b/tests/topotests/bgp_color_extcommunities/r1/zebra.conf new file mode 100644 index 0000000..42a8303 --- /dev/null +++ b/tests/topotests/bgp_color_extcommunities/r1/zebra.conf @@ -0,0 +1,3 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 diff --git a/tests/topotests/bgp_color_extcommunities/r2/bgpd.conf b/tests/topotests/bgp_color_extcommunities/r2/bgpd.conf new file mode 100644 index 0000000..2f83ada --- /dev/null +++ b/tests/topotests/bgp_color_extcommunities/r2/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 65002 + bgp router-id 192.168.1.2 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external diff --git a/tests/topotests/bgp_color_extcommunities/r2/zebra.conf b/tests/topotests/bgp_color_extcommunities/r2/zebra.conf new file mode 100644 index 0000000..cffe827 --- /dev/null +++ b/tests/topotests/bgp_color_extcommunities/r2/zebra.conf @@ -0,0 +1,4 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py b/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py new file mode 100644 index 0000000..6d17cdb --- /dev/null +++ b/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright 2022 6WIND S.A. +# Copyright 2023 6WIND S.A. +# François Dumontet +# + + +""" +test_bgp_color_extcommunity.py: Test the FRR BGP color extented +community feature +""" + +import os +import sys +import json +import functools +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + logger.info("setup_module") + + router_list = tgen.routers() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_color_extended_communities(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp summary json")) + expected = { + "ipv4Unicast": { + "peers": { + "192.168.1.2": { + "pfxSnt": 1, + "state": "Established", + }, + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed announcing 10.10.10.10/32 to r2" + + def _bgp_check_route(router, exists): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast 10.10.10.10 json")) + if exists: + expected = { + "prefix": "10.10.10.0/24", + "paths": [ + { + "valid": True, + "extendedCommunity": { + "string": "RT:80:987 Color:100 Color:200 Color:55555" + }, + } + ], + } + else: + expected = {} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_route, r2, True) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.0/24 ext community is correctly not installed, but SHOULD be" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_comm_list_delete/__init__.py b/tests/topotests/bgp_comm_list_delete/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_comm_list_delete/r1/bgpd.conf b/tests/topotests/bgp_comm_list_delete/r1/bgpd.conf new file mode 100644 index 0000000..12161d2 --- /dev/null +++ b/tests/topotests/bgp_comm_list_delete/r1/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + redistribute connected route-map r2-out + exit-address-family +! +route-map r2-out permit 10 + set community 111:111 222:222 333:333 444:444 +! diff --git a/tests/topotests/bgp_comm_list_delete/r1/zebra.conf b/tests/topotests/bgp_comm_list_delete/r1/zebra.conf new file mode 100644 index 0000000..0a283c0 --- /dev/null +++ b/tests/topotests/bgp_comm_list_delete/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_comm_list_delete/r2/bgpd.conf b/tests/topotests/bgp_comm_list_delete/r2/bgpd.conf new file mode 100644 index 0000000..33231b5 --- /dev/null +++ b/tests/topotests/bgp_comm_list_delete/r2/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 + neighbor 192.168.255.1 route-map r1-in in + exit-address-family +! +bgp community-list standard r1 permit 333:333 +! +route-map r1-in permit 10 + set comm-list r1 delete +! diff --git a/tests/topotests/bgp_comm_list_delete/r2/zebra.conf b/tests/topotests/bgp_comm_list_delete/r2/zebra.conf new file mode 100644 index 0000000..606c17b --- /dev/null +++ b/tests/topotests/bgp_comm_list_delete/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_comm_list_delete/test_bgp_comm-list_delete.py b/tests/topotests/bgp_comm_list_delete/test_bgp_comm-list_delete.py new file mode 100644 index 0000000..efc8f20 --- /dev/null +++ b/tests/topotests/bgp_comm_list_delete/test_bgp_comm-list_delete.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# bgp_comm-list_delete.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +bgp_comm-list_delete.py: + +Test if works the following commands: +route-map test permit 10 + set comm-list delete +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_maximum_prefix_invalid(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 2, + } + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge initially" + + def _bgp_comm_list_delete(): + output = json.loads(r2.vtysh_cmd("show ip bgp 172.16.255.254/32 json")) + expected = {"paths": [{"community": {"list": ["333:333"]}}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_comm_list_delete) + _, result = topotest.run_and_expect(test_func, not None, count=60, wait=0.5) + assert result is not None, "333:333 community SHOULD be stripped from r1" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_comm_list_match/__init__.py b/tests/topotests/bgp_comm_list_match/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_comm_list_match/r1/bgpd.conf b/tests/topotests/bgp_comm_list_match/r1/bgpd.conf new file mode 100644 index 0000000..bac8412 --- /dev/null +++ b/tests/topotests/bgp_comm_list_match/r1/bgpd.conf @@ -0,0 +1,28 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.0.2 remote-as external + neighbor 192.168.0.2 timers 1 3 + neighbor 192.168.0.2 timers connect 1 + address-family ipv4 + redistribute connected + neighbor 192.168.0.2 route-map r2 out + exit-address-family +! +ip prefix-list p1 seq 5 permit 172.16.255.1/32 +ip prefix-list p3 seq 5 permit 172.16.255.3/32 +ip prefix-list p4 seq 5 permit 172.16.255.4/32 +! +route-map r2 permit 10 + match ip address prefix-list p1 + set community 65001:1 65001:2 +route-map r2 permit 20 + match ip address prefix-list p3 + set community 65001:3 +route-map r2 permit 30 + match ip address prefix-list p4 + set community 65001:10 65001:12 65001:13 +exit +route-map r2 permit 40 +exit +! diff --git a/tests/topotests/bgp_comm_list_match/r1/zebra.conf b/tests/topotests/bgp_comm_list_match/r1/zebra.conf new file mode 100644 index 0000000..4219a7c --- /dev/null +++ b/tests/topotests/bgp_comm_list_match/r1/zebra.conf @@ -0,0 +1,12 @@ +! +interface lo + ip address 172.16.255.1/32 + ip address 172.16.255.2/32 + ip address 172.16.255.3/32 + ip address 172.16.255.4/32 +! +interface r1-eth0 + ip address 192.168.0.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_comm_list_match/r2/bgpd.conf b/tests/topotests/bgp_comm_list_match/r2/bgpd.conf new file mode 100644 index 0000000..cb2f89e --- /dev/null +++ b/tests/topotests/bgp_comm_list_match/r2/bgpd.conf @@ -0,0 +1,24 @@ +! +!debug bgp updates +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.0.1 remote-as external + neighbor 192.168.0.1 timers 1 3 + neighbor 192.168.0.1 timers connect 1 + neighbor 192.168.1.3 remote-as external + neighbor 192.168.1.3 timers 1 3 + neighbor 192.168.1.3 timers connect 1 + address-family ipv4 + neighbor 192.168.0.1 route-map r1 in + neighbor 192.168.0.1 soft-reconfiguration inbound + exit-address-family +! +bgp community-list 1 seq 5 permit 65001:1 65001:2 +bgp community-list 1 seq 10 permit 65001:3 +! +route-map r1 deny 10 + match community 1 +route-map r1 permit 20 +exit +! diff --git a/tests/topotests/bgp_comm_list_match/r2/zebra.conf b/tests/topotests/bgp_comm_list_match/r2/zebra.conf new file mode 100644 index 0000000..7fe82ba --- /dev/null +++ b/tests/topotests/bgp_comm_list_match/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface r2-eth0 + ip address 192.168.0.2/24 +! +interface r2-eth1 + ip address 192.168.1.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_comm_list_match/r3/bgpd.conf b/tests/topotests/bgp_comm_list_match/r3/bgpd.conf new file mode 100644 index 0000000..e68a3e4 --- /dev/null +++ b/tests/topotests/bgp_comm_list_match/r3/bgpd.conf @@ -0,0 +1,21 @@ +! +!debug bgp updates +! +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + address-family ipv4 + neighbor 192.168.1.2 route-map r1 in + neighbor 192.168.1.2 soft-reconfiguration inbound + exit-address-family +! +bgp community-list 2 seq 10 permit 65001:12 +! +route-map r1 deny 10 + match community 2 any +exit +route-map r1 permit 20 +exit +! diff --git a/tests/topotests/bgp_comm_list_match/r3/zebra.conf b/tests/topotests/bgp_comm_list_match/r3/zebra.conf new file mode 100644 index 0000000..755dd18 --- /dev/null +++ b/tests/topotests/bgp_comm_list_match/r3/zebra.conf @@ -0,0 +1,6 @@ +! +interface r3-eth0 + ip address 192.168.1.3/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py b/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py new file mode 100644 index 0000000..de69ea9 --- /dev/null +++ b/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Check if BGP community-list works as OR if multiple community entries specified, +like: + +bgp community-list 1 seq 5 permit 65001:1 65002:2 +bgp community-list 1 seq 10 permit 65001:3 +! +route-map test deny 10 + match community 1 +route-map test permit 20 + +Here, we should deny routes in/out if the path has: +(65001:1 AND 65001:2) OR 65001:3. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_comm_list_match(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads( + router.vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.0.1 filtered-routes json" + ) + ) + expected = { + "receivedRoutes": { + "172.16.255.1/32": { + "path": "65001", + }, + "172.16.255.3/32": { + "path": "65001", + }, + } + } + return topotest.json_cmp(output, expected) + + step("Initial BGP converge between R1 and R2") + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to filter BGP UPDATES with community-list on R2" + + +def test_bgp_comm_list_match_any(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r3"] + + def _bgp_converge(): + output = json.loads( + router.vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.1.2 filtered-routes json" + ) + ) + expected = { + "receivedRoutes": { + "172.16.255.4/32": { + "path": "65002 65001", + }, + } + } + return topotest.json_cmp(output, expected) + + step("Initial BGP converge between R3 and R2") + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to filter BGP UPDATES with community-list on R3" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_communities_topo1/bgp_communities.json b/tests/topotests/bgp_communities_topo1/bgp_communities.json new file mode 100644 index 0000000..da6aec2 --- /dev/null +++ b/tests/topotests/bgp_communities_topo1/bgp_communities.json @@ -0,0 +1,175 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r0": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_communities_topo1/bgp_communities_topo2.json b/tests/topotests/bgp_communities_topo1/bgp_communities_topo2.json new file mode 100644 index 0000000..fa89f6b --- /dev/null +++ b/tests/topotests/bgp_communities_topo1/bgp_communities_topo2.json @@ -0,0 +1,191 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": { + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + } + } + }, + "r3": { + "dest_link": { + "r1": { + } + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + } + } +} diff --git a/tests/topotests/bgp_communities_topo1/test_bgp_communities.py b/tests/topotests/bgp_communities_topo1/test_bgp_communities.py new file mode 100644 index 0000000..f3e03a0 --- /dev/null +++ b/tests/topotests/bgp_communities_topo1/test_bgp_communities.py @@ -0,0 +1,631 @@ +#!/usr/bin/python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test bgp community functionality: +- Verify routes are not advertised when NO-ADVERTISE Community is applied + +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_route_maps, + create_prefix_lists, + create_route_maps, + required_linux_kernel_version, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, +) +from lib.topojson import build_config_from_json +from copy import deepcopy + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = {"ipv4": "2.2.2.2/32", "ipv6": "22:22::2/128"} +NEXT_HOP_IP = {} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >= 4.15") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_communities.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global BGP_CONVERGENCE + global ADDR_TYPES + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_bgp_no_advertise_community_p0(request): + """ + Verify routes are not advertised when NO-ADVERTISE Community is applied + + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + NEXT_HOP_IP = { + "ipv4": topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0], + } + + # configure static routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static and connected in Router BGP " "in R1") + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "BGP neighbors are up, static and connected route advertised from" + " R1 are present on R2 BGP table and RIB using show ip bgp and " + " show ip route" + ) + step( + "Static and connected route advertised from R1 are present on R3" + " BGP table and RIB using show ip bgp and show ip route" + ) + + dut = "r3" + protocol = "bgp" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure prefix list P1 on R2 to permit route coming from R1") + # Create ip prefix list + input_dict_2 = { + "r2": { + "prefix_lists": { + addr_type: { + "pf_list_1_{}".format(addr_type): [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + input_dict_3 = { + "r2": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: {"prefix_lists": "pf_list_1_" + addr_type} + }, + "set": {"community": {"num": "no-advertise"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Apply route-map RM1 on R2, R2 to R3 BGP neighbor with no" + " advertise community" + ) + # Configure neighbor for route map + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "rmap_match_pf_1_" + + addr_type, + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After advertising no advertise community to BGP neighbor " + "static and connected router got removed from R3 verify using " + "show ip bgp & show ip route" + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Expected: " + "Routes still present in {} router. Found: {}".format(tc_name, dut, result) + ) + + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected: Routes still present in {} router. Found: {}".format( + tc_name, dut, result + ) + + step("Remove and Add no advertise community") + # Configure neighbor for route map + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "rmap_match_pf_1_" + + addr_type, + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing no advertise community from BGP neighbor " + "static and connected router got advertised to R3 and " + "removing route-map, verify route using show ip bgp" + " and show ip route" + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed \n Routes still present in R3 router. Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes still present in R3 router. Error: {}".format( + tc_name, result + ) + + step("Repeat above steps when IBGP nbr configured between R1, R2 & R2, R3") + topo1 = deepcopy(topo) + + topo1["routers"]["r1"]["bgp"]["local_as"] = "100" + topo1["routers"]["r2"]["bgp"]["local_as"] = "100" + topo1["routers"]["r3"]["bgp"]["local_as"] = "100" + + for rtr in ["r1", "r2", "r3"]: + if "bgp" in topo1["routers"][rtr].keys(): + delete_bgp = {rtr: {"bgp": {"delete": True}}} + result = create_router_bgp(tgen, topo1, delete_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + config_bgp = { + rtr: {"bgp": {"local_as": topo1["routers"][rtr]["bgp"]["local_as"]}} + } + result = create_router_bgp(tgen, topo1, config_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + build_config_from_json(tgen, topo1, save_bkup=False) + + step("verify bgp convergence before starting test case") + + bgp_convergence = verify_bgp_convergence(tgen, topo1) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + # configure static routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static and connected in Router " "BGP in R1") + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "BGP neighbors are up, static and connected route advertised from" + " R1 are present on R2 BGP table and RIB using show ip bgp and " + " show ip route" + ) + step( + "Static and connected route advertised from R1 are present on R3" + " BGP table and RIB using show ip bgp and show ip route" + ) + + dut = "r2" + protocol = "bgp" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure prefix list P1 on R2 to permit route coming from R1") + # Create ip prefix list + input_dict_2 = { + "r2": { + "prefix_lists": { + addr_type: { + "pf_list_1_{}".format(addr_type): [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + input_dict_3 = { + "r2": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: {"prefix_lists": "pf_list_1_" + addr_type} + }, + "set": {"community": {"num": "no-advertise"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Apply route-map RM1 on R2, R2 to R3 BGP neighbor with no" + " advertise community" + ) + + # Configure neighbor for route map + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "rmap_match_pf_1_" + + addr_type, + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After advertising no advertise community to BGP neighbor " + "static and connected router got removed from R3 verify using " + "show ip bgp & show ip route" + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed \n Routes still present in R3 router. Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes still present in R3 router. Error: {}".format( + tc_name, result + ) + + step("Remove and Add no advertise community") + # Configure neighbor for route map + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "rmap_match_pf_1_" + + addr_type, + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing no advertise community from BGP neighbor " + "static and connected router got advertised to R3 and " + "removing route verify using show ip bgp and " + " show ip route" + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed \n Routes still present in R3 router. Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes still present in R3 router. Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py b/tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py new file mode 100644 index 0000000..324f53f --- /dev/null +++ b/tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py @@ -0,0 +1,371 @@ +#!/usr/bin/python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test bgp community functionality: +1. Verify that BGP well known communities work fine for + eBGP and iBGP peers. + Well known communities tested: no-export, local-AS + +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_route_maps, + create_route_maps, + required_linux_kernel_version, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, + verify_bgp_community, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = { + "ipv4": ["192.0.2.1/32", "192.0.2.2/32"], + "ipv6": ["2001:DB8::1:1/128", "2001:DB8::1:2/128"], +} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.14") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >= 4.14") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_communities_topo2.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global BGP_CONVERGENCE + global ADDR_TYPES + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_bgp_no_export_local_as_communities_p0(request): + """ + Verify that BGP well known communities work fine for + eBGP and iBGP peers. + Well known communities tested: no-export, local-AS + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Initial config: Configure BGP neighborship between R1 and R3.") + reset_config_on_routers(tgen) + + step("Configure static routes on R1 with next-hop as null0") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [{"network": NETWORK[addr_type], "next_hop": "null0"}] + } + } + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for comm_type in ["no-export", "local-AS"]: + + step("Create a route-map on R1 to set community as {}".format(comm_type)) + + seq_id = 10 + input_rmap = { + "r1": { + "route_maps": { + "rmap_wkc": [ + { + "action": "permit", + "seq_id": seq_id, + "set": {"community": {"num": "{}".format(comm_type)}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_rmap) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Apply route-map while redistributing static routes into BGP") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"route-map": "rmap_wkc"}, + } + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"route-map": "rmap_wkc"}, + } + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + + step("Verify that BGP prefixes on R1 have community: {}".format(comm_type)) + input_dict_4 = {"community": "{}".format(comm_type)} + for addr_type in ADDR_TYPES: + result = verify_bgp_community( + tgen, addr_type, "r1", NETWORK[addr_type], input_dict_4 + ) + assert result is True, "Test case {} : Should fail \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r2"]["links"]["r1"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + input_dict_4, + next_hop=topo["routers"]["r1"]["links"]["r2"][addr_type].split("/")[0], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that these prefixes, originated on R1, are not" + "received on R3 but received on R2" + ) + result = verify_rib( + tgen, + addr_type, + "r3", + input_dict_4, + next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes are still present in rib of r3 \n " + "Found: {}".format(tc_name, result) + ) + + step("Remove route-map from redistribute static on R1") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static", "delete": True} + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static", "delete": True} + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure redistribute static") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that these prefixes, originated on R1, are now" + "received on both routers R2 and R3" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r2"]["links"]["r1"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + input_dict_4, + next_hop=topo["routers"]["r1"]["links"]["r2"][addr_type].split("/")[0], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r3", + input_dict_4, + next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_community_alias/__init__.py b/tests/topotests/bgp_community_alias/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_community_alias/r1/bgpd.conf b/tests/topotests/bgp_community_alias/r1/bgpd.conf new file mode 100644 index 0000000..844ab28 --- /dev/null +++ b/tests/topotests/bgp_community_alias/r1/bgpd.conf @@ -0,0 +1,26 @@ +! +bgp send-extra-data zebra +! +bgp community alias 65001:1 community-r2-1 +bgp community alias 65002:2 community-r2-2 +bgp community alias 65001:1:1 large-community-r2-1 +! +bgp large-community-list expanded r2 seq 5 permit _65001:1:1_ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + address-family ipv4 unicast + redistribute connected + neighbor 192.168.1.2 route-map r2 in + exit-address-family +! +route-map r2 permit 10 + match alias community-r2-1 + set tag 10 +route-map r2 permit 20 + match alias community-r2-2 + set tag 20 +route-map r2 permit 30 + set tag 100 +! diff --git a/tests/topotests/bgp_community_alias/r1/zebra.conf b/tests/topotests/bgp_community_alias/r1/zebra.conf new file mode 100644 index 0000000..b29940f --- /dev/null +++ b/tests/topotests/bgp_community_alias/r1/zebra.conf @@ -0,0 +1,4 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_community_alias/r2/bgpd.conf b/tests/topotests/bgp_community_alias/r2/bgpd.conf new file mode 100644 index 0000000..7806077 --- /dev/null +++ b/tests/topotests/bgp_community_alias/r2/bgpd.conf @@ -0,0 +1,25 @@ +! +bgp send-extra-data zebra +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + address-family ipv4 unicast + redistribute connected + neighbor 192.168.1.1 route-map r1 out + exit-address-family +! +ip prefix-list p1 permit 172.16.16.1/32 +ip prefix-list p2 permit 172.16.16.2/32 +ip prefix-list p3 permit 172.16.16.3/32 +! +route-map r1 permit 10 + match ip address prefix-list p1 + set community 65001:1 65001:2 + set large-community 65001:1:1 65001:1:2 +route-map r1 permit 20 + match ip address prefix-list p2 + set community 65002:1 65002:2 +route-map r1 permit 30 + match ip address prefix-list p3 +! diff --git a/tests/topotests/bgp_community_alias/r2/zebra.conf b/tests/topotests/bgp_community_alias/r2/zebra.conf new file mode 100644 index 0000000..b8cb9ba --- /dev/null +++ b/tests/topotests/bgp_community_alias/r2/zebra.conf @@ -0,0 +1,9 @@ +! +int lo + ip address 172.16.16.1/32 + ip address 172.16.16.2/32 + ip address 172.16.16.3/32 +! +int r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_community_alias/test_bgp-community-alias.py b/tests/topotests/bgp_community_alias/test_bgp-community-alias.py new file mode 100644 index 0000000..000ea60 --- /dev/null +++ b/tests/topotests/bgp_community_alias/test_bgp-community-alias.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2021 by +# Donatas Abraitis +# + +""" +Test if BGP community alias is visible in CLI outputs +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_community_alias(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "172.16.16.1/32": [ + { + "tag": 10, + "communities": "community-r2-1 65001:2", + "largeCommunities": "large-community-r2-1 65001:1:2", + } + ], + "172.16.16.2/32": [ + { + "tag": 20, + "communities": "65002:1 community-r2-2", + "largeCommunities": "", + } + ], + "172.16.16.3/32": [ + { + "tag": 100, + "communities": "", + "largeCommunities": "", + } + ], + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see BGP community aliases at r1" + + def _bgp_show_prefixes_by_alias(router): + output = json.loads( + router.vtysh_cmd( + "show bgp ipv4 unicast alias large-community-r2-1 json detail" + ) + ) + expected = { + "routes": { + "172.16.16.1/32": { + "paths": [ + { + "community": {"string": "community-r2-1 65001:2"}, + "largeCommunity": { + "string": "large-community-r2-1 65001:1:2" + }, + } + ] + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_show_prefixes_by_alias, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see BGP prefixes by community alias at r1" + + def _bgp_show_prefixes_by_large_community_list(router): + output = json.loads( + router.vtysh_cmd("show bgp ipv4 unicast large-community-list r2 json") + ) + expected = {"routes": {"172.16.16.1/32": [{"valid": True}]}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_show_prefixes_by_large_community_list, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see BGP prefixes by large community list at r1" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_community_change_update/__init__.py b/tests/topotests/bgp_community_change_update/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_community_change_update/c1/bgpd.conf b/tests/topotests/bgp_community_change_update/c1/bgpd.conf new file mode 100644 index 0000000..24cf9df --- /dev/null +++ b/tests/topotests/bgp_community_change_update/c1/bgpd.conf @@ -0,0 +1,11 @@ +! +debug bgp updates +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 10.0.1.2 remote-as external + neighbor 10.0.1.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_community_change_update/c1/zebra.conf b/tests/topotests/bgp_community_change_update/c1/zebra.conf new file mode 100644 index 0000000..e3dbbc0 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/c1/zebra.conf @@ -0,0 +1,6 @@ +! +interface c1-eth0 + ip address 10.0.1.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py b/tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py new file mode 100644 index 0000000..5ad15e0 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2020 by +# Donatas Abraitis +# + +r""" +Reference: https://www.cmand.org/communityexploration + + --y2-- + / | \ + c1 ---- x1 ---- y1 | z1 + \ | / + --y3-- + +1. z1 announces 192.168.255.254/32 to y2, y3. +2. y2 and y3 tags this prefix at ingress with appropriate +communities 65004:2 (y2) and 65004:3 (y3). +3. x1 filters all communities at the egress to c1. +4. Shutdown the link between y1 and y2. +5. y1 will generate a BGP UPDATE message regarding the next-hop change. +6. x1 will generate a BGP UPDATE message regarding community change. + +To avoid sending duplicate BGP UPDATE messages we should make sure +we send only actual route updates. In this example, x1 will skip +BGP UPDATE to c1 because the actual route is the same +(filtered communities - nothing changes). +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +from lib.common_config import step +from time import sleep + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("z1") + tgen.add_router("y1") + tgen.add_router("y2") + tgen.add_router("y3") + tgen.add_router("x1") + tgen.add_router("c1") + + # 10.0.1.0/30 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["c1"]) + switch.add_link(tgen.gears["x1"]) + + # 10.0.2.0/30 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["x1"]) + switch.add_link(tgen.gears["y1"]) + + # 10.0.3.0/30 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["y1"]) + switch.add_link(tgen.gears["y2"]) + + # 10.0.4.0/30 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["y1"]) + switch.add_link(tgen.gears["y3"]) + + # 10.0.5.0/30 + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["y2"]) + switch.add_link(tgen.gears["y3"]) + + # 10.0.6.0/30 + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["y2"]) + switch.add_link(tgen.gears["z1"]) + + # 10.0.7.0/30 + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["y3"]) + switch.add_link(tgen.gears["z1"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_community_update_path_change(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge_initial(): + output = json.loads( + tgen.gears["c1"].vtysh_cmd("show ip bgp neighbor 10.0.1.2 json") + ) + expected = { + "10.0.1.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 8}}, + } + } + return topotest.json_cmp(output, expected) + + step("Check if an initial topology is converged") + test_func = functools.partial(_bgp_converge_initial) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see bgp convergence in c1" + + step("Disable link between y1 and y2") + tgen.gears["y1"].run("ip link set dev y1-eth1 down") + + def _bgp_converge_link_disabled(): + output = json.loads(tgen.gears["y1"].vtysh_cmd("show ip bgp nei 10.0.3.2 json")) + expected = {"10.0.3.2": {"bgpState": "Active"}} + return topotest.json_cmp(output, expected) + + step("Check if a topology is converged after a link down between y1 and y2") + test_func = functools.partial(_bgp_converge_link_disabled) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see bgp convergence in y1" + + def _bgp_check_for_duplicate_updates(): + duplicate = False + i = 0 + while i < 5: + if ( + len( + tgen.gears["c1"].run( + 'grep "10.0.1.2(x1) rcvd 192.168.255.254/32 IPv4 unicast...duplicate ignored" bgpd.log' + ) + ) + > 0 + ): + duplicate = True + i += 1 + sleep(0.5) + return duplicate + + step("Check if we see duplicate BGP UPDATE message in c1 (suppress-duplicates)") + assert ( + _bgp_check_for_duplicate_updates() == False + ), "Seen duplicate BGP UPDATE message in c1 from x1" + + step("Disable bgp suppress-duplicates at x1") + tgen.gears["x1"].run( + "vtysh -c 'conf' -c 'router bgp' -c 'no bgp suppress-duplicates'" + ) + + step("Enable link between y1 and y2") + tgen.gears["y1"].run("ip link set dev y1-eth1 up") + + def _bgp_converge_link_enabled(): + output = json.loads(tgen.gears["y1"].vtysh_cmd("show ip bgp nei 10.0.3.2 json")) + expected = { + "10.0.3.2": { + "bgpState": "Established", + "addressFamilyInfo": { + "ipv4Unicast": {"acceptedPrefixCounter": 5, "sentPrefixCounter": 4} + }, + } + } + return topotest.json_cmp(output, expected) + + step("Check if a topology is converged after a link up between y1 and y2") + test_func = functools.partial(_bgp_converge_link_enabled) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see bgp convergence in y1" + + step( + "Check if we see duplicate BGP UPDATE message in c1 (no bgp suppress-duplicates)" + ) + assert ( + _bgp_check_for_duplicate_updates() == True + ), "Didn't see duplicate BGP UPDATE message in c1 from x1" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_community_change_update/x1/bgpd.conf b/tests/topotests/bgp_community_change_update/x1/bgpd.conf new file mode 100644 index 0000000..8d7bcb9 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/x1/bgpd.conf @@ -0,0 +1,20 @@ +! +debug bgp updates +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 10.0.1.1 remote-as external + neighbor 10.0.1.1 timers 3 10 + neighbor 10.0.2.2 remote-as external + neighbor 10.0.2.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + neighbor 10.0.1.1 route-map c1 out + exit-address-family +! +bgp community-list standard c1 seq 1 permit 65004:2 +bgp community-list standard c1 seq 2 permit 65004:3 +! +route-map c1 permit 10 + set comm-list c1 delete +! diff --git a/tests/topotests/bgp_community_change_update/x1/zebra.conf b/tests/topotests/bgp_community_change_update/x1/zebra.conf new file mode 100644 index 0000000..8b7c03f --- /dev/null +++ b/tests/topotests/bgp_community_change_update/x1/zebra.conf @@ -0,0 +1,9 @@ +! +interface x1-eth0 + ip address 10.0.1.2/30 +! +interface x1-eth1 + ip address 10.0.2.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_community_change_update/y1/bgpd.conf b/tests/topotests/bgp_community_change_update/y1/bgpd.conf new file mode 100644 index 0000000..a010085 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/y1/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 10.0.2.1 remote-as external + neighbor 10.0.2.1 timers 3 10 + neighbor 10.0.3.2 remote-as internal + neighbor 10.0.3.2 timers 3 10 + neighbor 10.0.4.2 remote-as internal + neighbor 10.0.4.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_community_change_update/y1/zebra.conf b/tests/topotests/bgp_community_change_update/y1/zebra.conf new file mode 100644 index 0000000..4096f2a --- /dev/null +++ b/tests/topotests/bgp_community_change_update/y1/zebra.conf @@ -0,0 +1,12 @@ +! +interface y1-eth0 + ip address 10.0.2.2/30 +! +interface y1-eth1 + ip address 10.0.3.1/30 +! +interface y1-eth2 + ip address 10.0.4.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_community_change_update/y2/bgpd.conf b/tests/topotests/bgp_community_change_update/y2/bgpd.conf new file mode 100644 index 0000000..27f1e81 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/y2/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 10.0.3.1 remote-as internal + neighbor 10.0.3.1 timers 3 10 + neighbor 10.0.5.2 remote-as internal + neighbor 10.0.5.2 timers 3 10 + neighbor 10.0.6.2 remote-as external + neighbor 10.0.6.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + neighbor 10.0.3.1 route-reflector-client + neighbor 10.0.5.2 route-reflector-client + neighbor 10.0.6.2 route-map z1 in + exit-address-family +! +route-map z1 permit 10 + set community 65004:2 +! diff --git a/tests/topotests/bgp_community_change_update/y2/zebra.conf b/tests/topotests/bgp_community_change_update/y2/zebra.conf new file mode 100644 index 0000000..7e9458a --- /dev/null +++ b/tests/topotests/bgp_community_change_update/y2/zebra.conf @@ -0,0 +1,12 @@ +! +interface y2-eth0 + ip address 10.0.3.2/30 +! +interface y2-eth1 + ip address 10.0.5.1/30 +! +interface y2-eth2 + ip address 10.0.6.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_community_change_update/y3/bgpd.conf b/tests/topotests/bgp_community_change_update/y3/bgpd.conf new file mode 100644 index 0000000..8e57284 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/y3/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 10.0.4.1 remote-as internal + neighbor 10.0.4.1 timers 3 10 + neighbor 10.0.5.1 remote-as internal + neighbor 10.0.5.1 timers 3 10 + neighbor 10.0.7.2 remote-as external + neighbor 10.0.7.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + neighbor 10.0.4.1 route-reflector-client + neighbor 10.0.5.1 route-reflector-client + neighbor 10.0.7.2 route-map z1 in + exit-address-family +! +route-map z1 permit 10 + set community 65004:3 +! diff --git a/tests/topotests/bgp_community_change_update/y3/zebra.conf b/tests/topotests/bgp_community_change_update/y3/zebra.conf new file mode 100644 index 0000000..93dd87d --- /dev/null +++ b/tests/topotests/bgp_community_change_update/y3/zebra.conf @@ -0,0 +1,12 @@ +! +interface y3-eth0 + ip address 10.0.4.2/30 +! +interface y3-eth1 + ip address 10.0.5.2/30 +! +interface y3-eth2 + ip address 10.0.7.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_community_change_update/z1/bgpd.conf b/tests/topotests/bgp_community_change_update/z1/bgpd.conf new file mode 100644 index 0000000..2f470f1 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/z1/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65004 + no bgp ebgp-requires-policy + neighbor 10.0.6.1 remote-as external + neighbor 10.0.6.1 timers 3 10 + neighbor 10.0.7.1 remote-as external + neighbor 10.0.7.1 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_community_change_update/z1/zebra.conf b/tests/topotests/bgp_community_change_update/z1/zebra.conf new file mode 100644 index 0000000..7f6bbb6 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/z1/zebra.conf @@ -0,0 +1,12 @@ +! +interface lo + ip address 192.168.255.254/32 +! +interface z1-eth0 + ip address 10.0.6.2/30 +! +interface z1-eth1 + ip address 10.0.7.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_conditional_advertisement/__init__.py b/tests/topotests/bgp_conditional_advertisement/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf b/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf new file mode 100644 index 0000000..293b38c --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf @@ -0,0 +1,31 @@ +! +ip prefix-list CUST seq 5 permit 10.139.224.0/20 +ip prefix-list DEFAULT seq 5 permit 0.0.0.0/0 +ip prefix-list PL1 seq 5 permit 192.0.2.1/32 +! +route-map CUST permit 10 + match ip address prefix-list CUST + set community 64671:501 +! +route-map RM1 permit 10 + match ip address prefix-list PL1 + set community 64952:3008 +! +route-map DEF permit 10 + match ip address prefix-list DEFAULT + set community 64848:3011 65011:200 65013:200 +! +router bgp 1 + bgp log-neighbor-changes + bgp conditional-advertisement timer 5 + no bgp ebgp-requires-policy + neighbor 10.10.10.2 remote-as 2 + ! + address-family ipv4 unicast + network 0.0.0.0/0 route-map DEF + network 192.0.2.1/32 route-map RM1 + network 192.0.2.5/32 + redistribute connected route-map CUST + neighbor 10.10.10.2 soft-reconfiguration inbound + exit-address-family +! diff --git a/tests/topotests/bgp_conditional_advertisement/r1/zebra.conf b/tests/topotests/bgp_conditional_advertisement/r1/zebra.conf new file mode 100644 index 0000000..bb887e4 --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement/r1/zebra.conf @@ -0,0 +1,19 @@ +! +hostname Router1 +! +ip route 0.0.0.0/0 blackhole +ip route 192.0.2.1/32 blackhole +ip route 192.0.2.2/32 blackhole +ip route 192.0.2.3/32 blackhole +ip route 192.0.2.4/32 blackhole +ip route 192.0.2.5/32 blackhole +! +interface r1-eth0 + ip address 10.10.10.1/24 +! +interface lo + ip address 10.139.224.1/20 +! +ip forwarding +ipv6 forwarding +! diff --git a/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf b/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf new file mode 100644 index 0000000..73f837c --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf @@ -0,0 +1,46 @@ +! +ip prefix-list DEFAULT seq 5 permit 192.0.2.5/32 +ip prefix-list DEFAULT seq 10 permit 192.0.2.1/32 +ip prefix-list EXIST seq 5 permit 10.10.10.10/32 +ip prefix-list DEFAULT-ROUTE seq 5 permit 0.0.0.0/0 +ip prefix-list IP1 seq 5 permit 10.139.224.0/20 +ip prefix-list IP2 seq 5 permit 203.0.113.1/32 +! +bgp community-list standard DC-ROUTES seq 5 permit 64952:3008 +bgp community-list standard DC-ROUTES seq 10 permit 64671:501 +bgp community-list standard DC-ROUTES seq 15 permit 64950:3009 +bgp community-list standard DEFAULT-ROUTE seq 5 permit 65013:200 +! +route-map ADV-MAP-1 permit 10 + match ip address prefix-list IP1 +! +route-map ADV-MAP-1 permit 20 + match community DC-ROUTES +! +route-map ADV-MAP-2 permit 10 + match ip address prefix-list IP2 + set metric 911 +! +route-map EXIST-MAP permit 10 + match community DEFAULT-ROUTE + match ip address prefix-list DEFAULT-ROUTE +! +route-map RMAP-1 deny 10 + match ip address prefix-list IP1 +! +route-map RMAP-2 deny 10 + match ip address prefix-list IP2 +! +router bgp 2 + bgp log-neighbor-changes + bgp conditional-advertisement timer 5 + no bgp ebgp-requires-policy + neighbor 10.10.10.1 remote-as 1 + neighbor 10.10.20.3 remote-as 3 + ! + address-family ipv4 unicast + network 203.0.113.1/32 + neighbor 10.10.10.1 soft-reconfiguration inbound + neighbor 10.10.20.3 soft-reconfiguration inbound + exit-address-family +! diff --git a/tests/topotests/bgp_conditional_advertisement/r2/zebra.conf b/tests/topotests/bgp_conditional_advertisement/r2/zebra.conf new file mode 100644 index 0000000..434ab68 --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement/r2/zebra.conf @@ -0,0 +1,15 @@ +! +hostname Router2 +! +interface r2-eth0 + ip address 10.10.10.2/24 +! +interface r2-eth1 + ip address 10.10.20.2/24 +! +interface lo + ip address 203.0.113.1/32 +! +ip forwarding +ipv6 forwarding +! diff --git a/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf b/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf new file mode 100644 index 0000000..f389f30 --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf @@ -0,0 +1,12 @@ +! +router bgp 3 + bgp log-neighbor-changes + bgp conditional-advertisement timer 5 + no bgp ebgp-requires-policy + neighbor 10.10.20.2 remote-as 2 + ! + address-family ipv4 unicast + neighbor 10.10.20.2 soft-reconfiguration inbound + exit-address-family +! + diff --git a/tests/topotests/bgp_conditional_advertisement/r3/zebra.conf b/tests/topotests/bgp_conditional_advertisement/r3/zebra.conf new file mode 100644 index 0000000..0dadfdb --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement/r3/zebra.conf @@ -0,0 +1,12 @@ +! +hostname Router3 +! +interface r3-eth0 + ip address 10.10.20.3/24 +! +interface lo + ip address 198.51.100.1/32 +! +ip forwarding +ipv6 forwarding +! diff --git a/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py b/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py new file mode 100644 index 0000000..0128c88 --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py @@ -0,0 +1,1297 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_conditional_advertisement.py +# +# Copyright (c) 2020 by +# Samsung R&D Institute India - Bangalore. +# Madhurilatha Kuruganti +# + +""" +Test BGP conditional advertisement functionality. + + +--------+ +--------+ +--------+ + | | | | | | + | R1 |------------| R2 |------------| R3 | + | | | | | | + +--------+ +--------+ +--------+ + +R2 is DUT and peers with R1 and R3 in default bgp instance. + +Following tests are covered under BGP conditional advertisement functionality. +Conditional advertisement +------------------------- +TC11: R3 BGP convergence, without advertise-map configuration. + All routes are advertised to R3. + +TC21: exist-map routes present in R2's BGP table. + advertise-map routes present in R2's BGP table are advertised to R3. +TC22: exist-map routes not present in R2's BGP table + advertise-map routes present in R2's BGP table are withdrawn from R3. +TC23: advertise-map with exist-map configuration is removed from a peer + send normal BGP update to advertise previously withdrawn routes if any. + +TC31: non-exist-map routes not present in R2's BGP table + advertise-map routes present in R2's BGP table are advertised to R3. +TC32: non-exist-map routes present in R2's BGP table + advertise-map routes present in R2's BGP table are withdrawn from R3. +TC33: advertise-map with non-exist-map configuration is removed from a peer + send normal BGP update to advertisepreviously withdrawn routes if any. + +TC41: non-exist-map route-map configuration removed in R2. + advertise-map routes present in R2's BGP table are advertised to R3. +TC42: exist-map route-map configuration removed in R2 + advertise-map routes present in R2's BGP table are withdrawn from R3. + +Conditional advertisement(received routes) along with Route-map Filter +---------------------------------------------------------------------- +TC51: exist-map routes present in R2's BGP table, with route-map filter. + All routes are withdrawn from R3 except advertise-map routes. +TC52: exist-map routes present in R2's BGP table, without route-map filter. + All routes are advertised to R3 including advertise-map routes. +TC53: non-exist-map routes present in R2's BGP table, with route-map filter. + All routes are withdrawn from R3 including advertise-map routes. +TC54: non-exist-map routes present in R2's BGP table, without route-map filter. + All routes are advertised to R3 except advertise-map routes. + +TC61: exist-map routes not present in R2's BGP table, with route-map filter. + All routes are withdrawn from R3 including advertise-map routes. +TC62: exist-map routes not present in R2's BGP table, without route-map filter. + All routes are advertised to R3 except advertise-map routes. +TC63: non-exist-map routes not present in R2's BGP table, with route-map filter. + All routes are withdrawn from R3 except advertise-map routes. +TC64: non-exist-map routes not present in R2's BGP table, without route-map filter. + All routes are advertised to R3 including advertise-map routes. + +Conditional advertisement(attached routes) along with Route-map Filter +----------------------------------------------------------------- +TC71: exist-map routes present in R2's BGP table, with route-map filter. + All routes are withdrawn from R3 except advertise-map routes. +TC72: exist-map routes present in R2's BGP table, without route-map filter. + All routes are advertised to R3 including advertise-map routes. +TC73: non-exist-map routes present in R2's BGP table, with route-map filter. + All routes are withdrawn from R3 including advertise-map routes. +TC74: non-exist-map routes present in R2's BGP table, without route-map filter. + All routes are advertised to R3 except advertise-map routes. + +TC81: exist-map routes not present in R2's BGP table, with route-map filter. + All routes are withdrawn from R3 including advertise-map routes. +TC82: exist-map routes not present in R2's BGP table, without route-map filter. + All routes are advertised to R3 except advertise-map routes. +TC83: non-exist-map routes not present in R2's BGP table, with route-map filter. + All routes are withdrawn from R3 except advertise-map routes. +TC84: non-exist-map routes not present in R2's BGP table, without route-map filter. + All routes are advertised to R3 including advertise-map routes. + +TC91: exist-map routes present in R2's BGP table, with route-map filter and network. + All routes are advertised to R3 including advertise-map routes. +TC92: exist-map routes present in R2's BGP table, with route-map filter and no network. + All routes are advertised to R3 except advertise-map routes. +TC93: non-exist-map routes not present in R2's BGP table, with route-map filter and network. + All routes are advertised to R3 including advertise-map routes. +TC94: non-exist-map routes not present in R2's BGP table, with route-map filter and no network. + All routes are advertised to R3 except advertise-map routes. + +i.e. ++----------------+-------------------------+------------------------+ +| Routes in | exist-map status | advertise-map status | +| BGP table | | | ++----------------+-------------------------+------------------------+ +| Present | Condition matched | Advertise | ++----------------+-------------------------+------------------------+ +| Not Present | Condition not matched | Withdrawn | ++----------------+-------------------------+------------------------+ +| | non-exist-map status | advertise-map status | +| | | | ++----------------+-------------------------+------------------------+ +| Present | Condition matched | Withdrawn | ++----------------+-------------------------+------------------------+ +| Not Present | Condition not matched | Advertise | ++----------------+-------------------------+------------------------+ +Here in this topology, based on the default route presence in R2 and +the configured condition-map (exist-map/non-exist-map) 10.139.224.0/20 +will be either advertised/withdrawn to/from R3. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + r1 = tgen.add_router("r1") + r2 = tgen.add_router("r2") + r3 = tgen.add_router("r3") + + switch = tgen.add_switch("s1") + switch.add_link(r1) + switch.add_link(r2) + + switch = tgen.add_switch("s2") + switch.add_link(r2) + switch.add_link(r3) + + +def setup_module(mod): + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def all_routes_advertised(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": [{"protocol": "bgp"}], + "192.0.2.1/32": [{"protocol": "bgp"}], + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": [{"protocol": "bgp"}], + "203.0.113.1/32": [{"protocol": "bgp"}], + } + return topotest.json_cmp(output, expected) + + +def all_routes_withdrawn(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": None, + "192.0.2.5/32": None, + "10.139.224.0/20": None, + "203.0.113.1/32": None, + } + return topotest.json_cmp(output, expected) + + +def default_route_withdrawn(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": [{"protocol": "bgp"}], + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": [{"protocol": "bgp"}], + "203.0.113.1/32": [{"protocol": "bgp"}], + } + return topotest.json_cmp(output, expected) + + +# BGP conditional advertisement with route-maps +# EXIST-MAP, ADV-MAP-1 and RMAP-1 +def exist_map_routes_present(router): + return all_routes_advertised(router) + + +def exist_map_routes_not_present(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": None, + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": None, + "203.0.113.1/32": [{"protocol": "bgp"}], + } + return topotest.json_cmp(output, expected) + + +def non_exist_map_routes_present(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": [{"protocol": "bgp"}], + "192.0.2.1/32": None, + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": None, + "203.0.113.1/32": [{"protocol": "bgp"}], + } + return topotest.json_cmp(output, expected) + + +def non_exist_map_routes_not_present(router): + return default_route_withdrawn(router) + + +def exist_map_no_condition_route_map(router): + return non_exist_map_routes_present(router) + + +def non_exist_map_no_condition_route_map(router): + return all_routes_advertised(router) + + +def exist_map_routes_present_rmap_filter(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": [{"protocol": "bgp"}], + "192.0.2.5/32": None, + "10.139.224.0/20": [{"protocol": "bgp"}], + "203.0.113.1/32": None, + } + return topotest.json_cmp(output, expected) + + +def exist_map_routes_present_no_rmap_filter(router): + return all_routes_advertised(router) + + +def non_exist_map_routes_present_rmap_filter(router): + return all_routes_withdrawn(router) + + +def non_exist_map_routes_present_no_rmap_filter(router): + return non_exist_map_routes_present(router) + + +def exist_map_routes_not_present_rmap_filter(router): + return all_routes_withdrawn(router) + + +def exist_map_routes_not_present_no_rmap_filter(router): + return exist_map_routes_not_present(router) + + +def non_exist_map_routes_not_present_rmap_filter(router): + return exist_map_routes_present_rmap_filter(router) + + +def non_exist_map_routes_not_present_no_rmap_filter(router): + return non_exist_map_routes_not_present(router) + + +# BGP conditional advertisement with route-maps +# EXIST-MAP, ADV-MAP-2 and RMAP-2 +def exist_map_routes_not_present_rmap2_filter(router): + return all_routes_withdrawn(router) + + +def exist_map_routes_not_present_no_rmap2_filter(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": [{"protocol": "bgp"}], + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": [{"protocol": "bgp"}], + "203.0.113.1/32": None, + } + return topotest.json_cmp(output, expected) + + +def non_exist_map_routes_not_present_rmap2_filter(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": None, + "192.0.2.5/32": None, + "10.139.224.0/20": None, + "203.0.113.1/32": [{"protocol": "bgp", "metric": 911}], + } + return topotest.json_cmp(output, expected) + + +def non_exist_map_routes_not_present_no_rmap2_filter(router): + return non_exist_map_routes_not_present(router) + + +def exist_map_routes_present_rmap2_filter(router): + return non_exist_map_routes_not_present_rmap2_filter(router) + + +def exist_map_routes_present_no_rmap2_filter(router): + return all_routes_advertised(router) + + +def non_exist_map_routes_present_rmap2_filter(router): + return all_routes_withdrawn(router) + + +def non_exist_map_routes_present_no_rmap2_filter(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": [{"protocol": "bgp"}], + "192.0.2.1/32": [{"protocol": "bgp"}], + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": [{"protocol": "bgp"}], + "203.0.113.1/32": None, + } + return topotest.json_cmp(output, expected) + + +def exist_map_routes_present_rmap2_network(router): + return non_exist_map_routes_not_present_rmap2_filter(router) + + +def exist_map_routes_present_rmap2_no_network(router): + return all_routes_withdrawn(router) + + +def non_exist_map_routes_not_present_rmap2_network(router): + return non_exist_map_routes_not_present_rmap2_filter(router) + + +def non_exist_map_routes_not_present_rmap2_no_network(router): + return all_routes_withdrawn(router) + + +passed = "PASSED!!!" +failed = "FAILED!!!" + + +def test_bgp_conditional_advertisement_tc_1_1(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC11: R3 BGP convergence, without advertise-map configuration. + # All routes are advertised to R3. + test_func = functools.partial(all_routes_advertised, router3) + success, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + + msg = 'TC11: "router3" BGP convergence - ' + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_2_1(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC21: exist-map routes present in R2's BGP table. + # advertise-map routes present in R2's BGP table are advertised to R3. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 advertise-map ADV-MAP-1 exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(exist_map_routes_present, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = 'TC21: exist-map routes present in "router2" BGP table - ' + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_2_2(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC22: exist-map routes not present in R2's BGP table + # advertise-map routes present in R2's BGP table are withdrawn from R3. + router1.vtysh_cmd( + """ + configure terminal + router bgp 1 + address-family ipv4 unicast + no network 0.0.0.0/0 route-map DEF + """ + ) + + test_func = functools.partial(exist_map_routes_not_present, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = 'TC22: exist-map routes not present in "router2" BGP table - ' + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_2_3(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC23: advertise-map with exist-map configuration is removed from a peer + # send normal BGP update to advertise previously withdrawn routes if any. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no neighbor 10.10.20.3 advertise-map ADV-MAP-1 exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(default_route_withdrawn, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC23: advertise-map with exist-map configuration is removed from peer - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_3_1(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC31: non-exist-map routes not present in R2's BGP table + # advertise-map routes present in R2's BGP table are advertised to R3. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 advertise-map ADV-MAP-1 non-exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(non_exist_map_routes_not_present, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = 'TC31: non-exist-map routes not present in "router2" BGP table - ' + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_3_2(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC32: non-exist-map routes present in R2's BGP table + # advertise-map routes present in R2's BGP table are withdrawn from R3. + router1.vtysh_cmd( + """ + configure terminal + router bgp 1 + address-family ipv4 unicast + network 0.0.0.0/0 route-map DEF + """ + ) + + test_func = functools.partial(non_exist_map_routes_present, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = 'TC32: non-exist-map routes present in "router2" BGP table - ' + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_3_3(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC33: advertise-map with non-exist-map configuration is removed from a peer + # send normal BGP update to advertisepreviously withdrawn routes if any. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no neighbor 10.10.20.3 advertise-map ADV-MAP-1 non-exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(all_routes_advertised, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = ( + "TC33: advertise-map with non-exist-map configuration is removed from a peer - " + ) + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_4_1(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC41: non-exist-map route-map configuration removed in R2. + # advertise-map routes present in R2's BGP table are advertised to R3. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 advertise-map ADV-MAP-1 non-exist-map EXIST-MAP + no route-map EXIST-MAP permit 10 + """ + ) + + test_func = functools.partial(non_exist_map_no_condition_route_map, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = 'TC41: non-exist-map route-map removed in "router2" - ' + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_4_2(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC42: exist-map route-map configuration removed in R2 + # advertise-map routes present in R2's BGP table are withdrawn from R3. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 advertise-map ADV-MAP-1 exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(exist_map_no_condition_route_map, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = 'TC42: exist-map route-map removed in "router2" - ' + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_5_1(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC51: exist-map routes present in R2's BGP table, with route-map filter. + # All routes are withdrawn from R3 except advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + route-map EXIST-MAP permit 10 + match community DEFAULT-ROUTE + match ip address prefix-list DEFAULT-ROUTE + ! + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 route-map RMAP-1 out + """ + ) + + test_func = functools.partial(exist_map_routes_present_rmap_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC51: exist-map routes present with route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_5_2(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC52: exist-map routes present in R2's BGP table, no route-map filter. + # All routes are advertised to R3 including advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no neighbor 10.10.20.3 route-map RMAP-1 out + """ + ) + + test_func = functools.partial(exist_map_routes_present_no_rmap_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC52: exist-map routes present, no route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_5_3(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC53: non-exist-map routes present in R2's BGP table, with route-map filter. + # All routes are withdrawn from R3 including advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 route-map RMAP-1 out + neighbor 10.10.20.3 advertise-map ADV-MAP-1 non-exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(non_exist_map_routes_present_rmap_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC53: non-exist-map routes present, with route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_5_4(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC54: non-exist-map routes present in R2's BGP table, no route-map filter. + # All routes are advertised to R3 except advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no neighbor 10.10.20.3 route-map RMAP-1 out + """ + ) + + test_func = functools.partial(non_exist_map_routes_present_no_rmap_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC54: non-exist-map routes present, no route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_6_1(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC61: exist-map routes not present in R2's BGP table, with route-map filter. + # All routes are withdrawn from R3 including advertise-map routes. + router1.vtysh_cmd( + """ + configure terminal + router bgp 1 + address-family ipv4 unicast + no network 0.0.0.0/0 route-map DEF + """ + ) + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 route-map RMAP-1 out + neighbor 10.10.20.3 advertise-map ADV-MAP-1 exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(exist_map_routes_not_present_rmap_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC61: exist-map routes not present, route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_6_2(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC62: exist-map routes not present in R2's BGP table, without route-map filter. + # All routes are advertised to R3 except advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no neighbor 10.10.20.3 route-map RMAP-1 out + """ + ) + + test_func = functools.partial(exist_map_routes_not_present_no_rmap_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC62: exist-map routes not present, no route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_6_3(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC63: non-exist-map routes not present in R2's BGP table, with route-map filter. + # All routes are withdrawn from R3 except advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 route-map RMAP-1 out + neighbor 10.10.20.3 advertise-map ADV-MAP-1 non-exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(non_exist_map_routes_not_present_rmap_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC63: non-exist-map routes not present, route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_6_4(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC64: non-exist-map routes not present in R2's BGP table, without route-map filter. + # All routes are advertised to R3 including advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no neighbor 10.10.20.3 route-map RMAP-1 out + """ + ) + + test_func = functools.partial( + non_exist_map_routes_not_present_no_rmap_filter, router3 + ) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC64: non-exist-map routes not present, no route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_7_1(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC71: exist-map routes present in R2's BGP table, with route-map filter. + # All routes are withdrawn from R3 except advertise-map routes. + router1.vtysh_cmd( + """ + configure terminal + router bgp 1 + address-family ipv4 unicast + network 0.0.0.0/0 route-map DEF + """ + ) + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 route-map RMAP-2 out + neighbor 10.10.20.3 advertise-map ADV-MAP-2 exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(exist_map_routes_present_rmap2_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC71: exist-map routes present, route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_7_2(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC72: exist-map routes present in R2's BGP table, without route-map filter. + # All routes are advertised to R3 including advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no neighbor 10.10.20.3 route-map RMAP-2 out + """ + ) + + test_func = functools.partial(exist_map_routes_present_no_rmap2_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC72: exist-map routes present, no route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_7_3(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC73: non-exist-map routes present in R2's BGP table, with route-map filter. + # All routes are advertised to R3 including advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 route-map RMAP-2 out + neighbor 10.10.20.3 advertise-map ADV-MAP-2 non-exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(non_exist_map_routes_present_rmap2_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC73: non-exist-map routes present, route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_7_4(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC74: non-exist-map routes present in R2's BGP table, without route-map filter. + # All routes are advertised to R3 including advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no neighbor 10.10.20.3 route-map RMAP-2 out + """ + ) + + test_func = functools.partial(non_exist_map_routes_present_no_rmap2_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC74: non-exist-map routes present, no route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_8_1(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC81: exist-map routes not present in R2's BGP table, with route-map filter. + # All routes are withdrawn from R3 including advertise-map routes. + router1.vtysh_cmd( + """ + configure terminal + router bgp 1 + address-family ipv4 unicast + no network 0.0.0.0/0 route-map DEF + """ + ) + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 route-map RMAP-2 out + neighbor 10.10.20.3 advertise-map ADV-MAP-2 exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(exist_map_routes_not_present_rmap2_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC81: exist-map routes not present, route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_8_2(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC82: exist-map routes not present in R2's BGP table, without route-map filter. + # All routes are advertised to R3 except advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no neighbor 10.10.20.3 route-map RMAP-2 out + """ + ) + + test_func = functools.partial(exist_map_routes_not_present_no_rmap2_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC82: exist-map routes not present, no route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_8_3(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC83: non-exist-map routes not present in R2's BGP table, with route-map filter. + # All routes are advertised to R3 including advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 route-map RMAP-2 out + neighbor 10.10.20.3 advertise-map ADV-MAP-2 non-exist-map EXIST-MAP + """ + ) + + test_func = functools.partial( + non_exist_map_routes_not_present_rmap2_filter, router3 + ) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC83: non-exist-map routes not present, route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_8_4(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC84: non-exist-map routes not present in R2's BGP table, without route-map filter. + # All routes are advertised to R3 including advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no neighbor 10.10.20.3 route-map RMAP-2 out + """ + ) + + test_func = functools.partial( + non_exist_map_routes_not_present_no_rmap2_filter, router3 + ) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC84: non-exist-map routes not present, no route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_9_1(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC91: exist-map routes present in R2's BGP table, with route-map filter and network. + # All routes are advertised to R3 including advertise-map routes. + router1.vtysh_cmd( + """ + configure terminal + router bgp 1 + address-family ipv4 unicast + network 0.0.0.0/0 route-map DEF + """ + ) + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 route-map RMAP-2 out + neighbor 10.10.20.3 advertise-map ADV-MAP-2 exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(exist_map_routes_present_rmap2_network, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC91: exist-map routes present, route-map filter and network - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_9_2(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC92: exist-map routes present in R2's BGP table, with route-map filter and no network. + # All routes are advertised to R3 except advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no network 203.0.113.1/32 + """ + ) + + test_func = functools.partial(exist_map_routes_present_rmap2_no_network, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC92: exist-map routes present, route-map filter and no network - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_9_3(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC93: non-exist-map routes not present in R2's BGP table, with route-map filter and network. + # All routes are advertised to R3 including advertise-map routes. + router1.vtysh_cmd( + """ + configure terminal + router bgp 1 + address-family ipv4 unicast + no network 0.0.0.0/0 route-map DEF + """ + ) + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + network 203.0.113.1/32 + neighbor 10.10.20.3 advertise-map ADV-MAP-2 non-exist-map EXIST-MAP + """ + ) + + test_func = functools.partial( + non_exist_map_routes_not_present_rmap2_network, router3 + ) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC93: non-exist-map routes not present, route-map filter and network - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_bgp_conditional_advertisement_tc_9_4(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # TC94: non-exist-map routes not present in R2's BGP table, with route-map filter and no network. + # All routes are advertised to R3 except advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no network 203.0.113.1/32 + """ + ) + + test_func = functools.partial( + non_exist_map_routes_not_present_rmap2_no_network, router3 + ) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC94: non-exist-map routes not present, route-map filter and no network - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_conditional_advertisement_static_route/__init__.py b/tests/topotests/bgp_conditional_advertisement_static_route/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_conditional_advertisement_static_route/r1/frr.conf b/tests/topotests/bgp_conditional_advertisement_static_route/r1/frr.conf new file mode 100644 index 0000000..3e51337 --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement_static_route/r1/frr.conf @@ -0,0 +1,10 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as internal + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 +! diff --git a/tests/topotests/bgp_conditional_advertisement_static_route/r2/frr.conf b/tests/topotests/bgp_conditional_advertisement_static_route/r2/frr.conf new file mode 100644 index 0000000..3ced934 --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement_static_route/r2/frr.conf @@ -0,0 +1,40 @@ +! +!debug bgp conditional-advertisement +!debug bgp updates +! +int r2-eth0 + ip address 192.168.1.2/24 +! +int r2-eth1 + ip address 192.168.2.2/24 +! +router bgp 65000 + no bgp ebgp-requires-policy + bgp conditional-advertisement timer 5 + neighbor 192.168.1.1 remote-as internal + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + neighbor 192.168.2.1 remote-as internal + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 + address-family ipv4 unicast + redistribute static + neighbor 192.168.1.1 advertise-map advertise-map exist-map exist-map + neighbor 192.168.1.1 route-map deny-all out + exit-address-family +! +ip route 10.10.10.1/32 r2-eth0 +ip route 10.10.10.2/32 r2-eth0 +! +ip prefix-list default seq 5 permit 0.0.0.0/0 +ip prefix-list advertise seq 5 permit 10.10.10.1/32 +! +route-map deny-all deny 10 +! +route-map exist-map permit 10 + match ip address prefix-list default +! +route-map advertise-map permit 10 + match ip address prefix-list advertise + set community 65000:1 +! diff --git a/tests/topotests/bgp_conditional_advertisement_static_route/r3/frr.conf b/tests/topotests/bgp_conditional_advertisement_static_route/r3/frr.conf new file mode 100644 index 0000000..a24a2cb --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement_static_route/r3/frr.conf @@ -0,0 +1,19 @@ +! +int lo + ip address 10.10.10.1/32 + ip address 10.10.10.2/32 +! +int r3-eth0 + ip address 192.168.2.1/24 +! +router bgp 65000 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.2.2 remote-as internal + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + ! + address-family ipv4 unicast + neighbor 192.168.2.2 default-originate + exit-address-family +! diff --git a/tests/topotests/bgp_conditional_advertisement_static_route/test_bgp_conditional_advertisement_static_route.py b/tests/topotests/bgp_conditional_advertisement_static_route/test_bgp_conditional_advertisement_static_route.py new file mode 100644 index 0000000..4180bfc --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement_static_route/test_bgp_conditional_advertisement_static_route.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if static route with BGP conditional advertisement works correctly +if we modify the prefix-lists. +""" + +import os +import re +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2"), "s2": ("r2", "r3")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_conditional_advertisements_static_route(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads( + r2.vtysh_cmd( + "show bgp ipv4 unicast neighbor 192.168.1.1 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "10.10.10.1/32": { + "valid": True, + } + }, + "totalPrefixCounter": 1, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't converge" + + step("Append prefix-list to advertise 10.10.10.2/32") + + r2.vtysh_cmd( + """ + configure terminal + ip prefix-list advertise seq 10 permit 10.10.10.2/32 + """ + ) + + def _bgp_check_advertised_after_update(): + output = json.loads( + r2.vtysh_cmd( + "show bgp ipv4 unicast neighbor 192.168.1.1 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "10.10.10.1/32": { + "valid": True, + }, + "10.10.10.2/32": { + "valid": True, + }, + }, + "totalPrefixCounter": 2, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_advertised_after_update, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.2/32 is not advertised after prefix-list update" + + def _bgp_check_received_routes(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 10.10.10.1/32 json")) + expected = { + "paths": [ + { + "community": { + "string": "65000:1", + } + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_received_routes, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.1/32 does not have 65000:1 community attached" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/__init__.py b/tests/topotests/bgp_conditional_advertisement_track_peer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/r1/bgpd.conf b/tests/topotests/bgp_conditional_advertisement_track_peer/r1/bgpd.conf new file mode 100644 index 0000000..1e98f4e --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement_track_peer/r1/bgpd.conf @@ -0,0 +1,17 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 3 10 + address-family ipv4 unicast + neighbor 192.168.1.2 route-map r2 in + exit-address-family +! +ip prefix-list p1 seq 5 permit 172.16.255.31/32 +! +route-map r2 permit 10 + match ip address prefix-list p1 + set as-path replace 65003 +route-map r2 permit 20 + set as-path replace any +! diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/r1/zebra.conf b/tests/topotests/bgp_conditional_advertisement_track_peer/r1/zebra.conf new file mode 100644 index 0000000..acf120b --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement_track_peer/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/r2/bgpd.conf b/tests/topotests/bgp_conditional_advertisement_track_peer/r2/bgpd.conf new file mode 100644 index 0000000..66c3dc5 --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement_track_peer/r2/bgpd.conf @@ -0,0 +1,28 @@ +! +router bgp 65002 + no bgp ebgp-requires-policy + bgp conditional-advertisement timer 5 + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 3 10 + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 3 10 + address-family ipv4 unicast + redistribute static + neighbor 192.168.1.1 advertise-map advertise exist-map exist + neighbor 192.168.1.1 route-map deny-all out + neighbor 192.168.2.1 route-map exist in + exit-address-family +! +ip prefix-list exist seq 5 permit 172.16.255.3/32 +ip prefix-list advertise seq 5 permit 172.16.255.2/32 +! +route-map advertise permit 10 + match ip address prefix-list advertise +exit +! +route-map exist permit 10 + match ip address prefix-list exist +exit +! +route-map deny-all deny 10 +exit diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/r2/staticd.conf b/tests/topotests/bgp_conditional_advertisement_track_peer/r2/staticd.conf new file mode 100644 index 0000000..6c013e2 --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement_track_peer/r2/staticd.conf @@ -0,0 +1,3 @@ +! +ip route 172.16.255.2/32 r2-eth1 +! diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/r2/zebra.conf b/tests/topotests/bgp_conditional_advertisement_track_peer/r2/zebra.conf new file mode 100644 index 0000000..f229954 --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement_track_peer/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface r2-eth0 + ip address 192.168.1.2/24 +! +interface r2-eth1 + ip address 192.168.2.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/r3/bgpd.conf b/tests/topotests/bgp_conditional_advertisement_track_peer/r3/bgpd.conf new file mode 100644 index 0000000..5058d40 --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement_track_peer/r3/bgpd.conf @@ -0,0 +1,10 @@ +! +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 3 10 + neighbor 192.168.2.2 shutdown + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/r3/zebra.conf b/tests/topotests/bgp_conditional_advertisement_track_peer/r3/zebra.conf new file mode 100644 index 0000000..268163e --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement_track_peer/r3/zebra.conf @@ -0,0 +1,9 @@ +! +int lo + ip address 172.16.255.3/32 +! +interface r3-eth0 + ip address 192.168.2.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/test_bgp_conditional_advertisement_track_peer.py b/tests/topotests/bgp_conditional_advertisement_track_peer/test_bgp_conditional_advertisement_track_peer.py new file mode 100644 index 0000000..e763072 --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement_track_peer/test_bgp_conditional_advertisement_track_peer.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Conditionally advertise 172.16.255.2/32 to r1, only if 172.16.255.3/32 +is received from r3. + +Also, withdraw if 172.16.255.3/32 disappears. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import ( + step, +) + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_conditional_advertisement_track_peer(): + tgen = get_topogen() + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(): + output = json.loads( + r2.vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.1.1 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": {"172.16.255.2/32": None}, + "totalPrefixCounter": 0, + "filteredPrefixCounter": 0, + } + return topotest.json_cmp(output, expected) + + # Verify if R2 does not send any routes to R1 + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "R2 SHOULD not send any routes to R1" + + step("Enable session between R2 and R3") + r3.vtysh_cmd( + """ + configure terminal + router bgp + no neighbor 192.168.2.2 shutdown + """ + ) + + def _bgp_check_conditional_static_routes_from_r2(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "172.16.255.2/32": [{"valid": True, "nexthops": [{"hostname": "r2"}]}] + } + } + return topotest.json_cmp(output, expected) + + # Verify if R1 received 172.16.255.2/32 from R2 + test_func = functools.partial(_bgp_check_conditional_static_routes_from_r2) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "R1 SHOULD receive 172.16.255.2/32 from R2" + + step("Disable session between R2 and R3 again") + r3.vtysh_cmd( + """ + configure terminal + router bgp + neighbor 192.168.2.2 shutdown + """ + ) + + # Verify if R2 is not sending any routes to R1 again + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "R2 SHOULD not send any routes to R1" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_confed1/__init__.py b/tests/topotests/bgp_confed1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_confed1/r1/bgp_ipv4_unicast.json b/tests/topotests/bgp_confed1/r1/bgp_ipv4_unicast.json new file mode 100644 index 0000000..d3988eb --- /dev/null +++ b/tests/topotests/bgp_confed1/r1/bgp_ipv4_unicast.json @@ -0,0 +1,63 @@ +{ + "vrfId":0, + "vrfName":"default", + "routerId":"203.0.113.1", + "defaultLocPrf":100, + "localAS":100, + "routes":{"203.0.113.0/28":[ + { + "network":"203.0.113.0\/28", + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "afi":"ipv4", + "used":true + } + ] + } +],"203.0.113.16/28":[ + { + "network":"203.0.113.16\/28", + "peerId":"192.0.2.2", + "path":"300", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.0.2.2", + "afi":"ipv4", + "used":true + } + ] + } +],"203.0.113.32/28":[ + { + "network":"203.0.113.32\/28", + "peerId":"192.0.2.2", + "path":"300", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.0.2.2", + "afi":"ipv4", + "used":true + } + ] + } +],"203.0.113.48/28":[ + { + "network":"203.0.113.48\/28", + "peerId":"192.0.2.2", + "path":"300 400", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.0.2.2", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_confed1/r1/bgp_summary.json b/tests/topotests/bgp_confed1/r1/bgp_summary.json new file mode 100644 index 0000000..f999021 --- /dev/null +++ b/tests/topotests/bgp_confed1/r1/bgp_summary.json @@ -0,0 +1,13 @@ +{ + "ipv4Unicast":{ + "routerId":"203.0.113.1", + "as":100, + "peers":{ + "192.0.2.2":{ + "remoteAs": 300, + "state": "Established", + "peerState":"OK" + } + } + } +} diff --git a/tests/topotests/bgp_confed1/r1/bgpd.conf b/tests/topotests/bgp_confed1/r1/bgpd.conf new file mode 100644 index 0000000..107d2ad --- /dev/null +++ b/tests/topotests/bgp_confed1/r1/bgpd.conf @@ -0,0 +1,13 @@ +!debug bgp neighbor-events +!debug bgp nht +!debug bgp updates in +!debug bgp updates out +! +router bgp 100 + no bgp ebgp-requires-policy +! + neighbor 192.0.2.2 remote-as 300 + address-family ipv4 unicast + network 203.0.113.0/28 + exit-address-family +! diff --git a/tests/topotests/bgp_confed1/r1/isisd.conf b/tests/topotests/bgp_confed1/r1/isisd.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_confed1/r1/zebra.conf b/tests/topotests/bgp_confed1/r1/zebra.conf new file mode 100644 index 0000000..5f76e74 --- /dev/null +++ b/tests/topotests/bgp_confed1/r1/zebra.conf @@ -0,0 +1,6 @@ +interface r1-eth0 + ip address 192.0.2.1/28 +! +interface lo + ip address 203.0.113.1/28 +! diff --git a/tests/topotests/bgp_confed1/r2/bgp_ipv4_unicast.json b/tests/topotests/bgp_confed1/r2/bgp_ipv4_unicast.json new file mode 100644 index 0000000..b4a0946 --- /dev/null +++ b/tests/topotests/bgp_confed1/r2/bgp_ipv4_unicast.json @@ -0,0 +1,63 @@ +{ + "vrfId":0, + "vrfName":"default", + "routerId":"203.0.113.17", + "defaultLocPrf":100, + "localAS":200, + "routes":{"203.0.113.0/28": [ + { + "network":"203.0.113.0\/28", + "peerId":"192.0.2.1", + "path":"100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.0.2.1", + "afi":"ipv4", + "used":true + } + ] + } +],"203.0.113.16/28":[ + { + "network":"203.0.113.16\/28", + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "afi":"ipv4", + "used":true + } + ] + } +],"203.0.113.32/28":[ + { + "network":"203.0.113.32\/28", + "peerId":"192.0.2.18", + "path":"(300)", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.0.2.18", + "afi":"ipv4", + "used":true + } + ] + } +],"203.0.113.48/28":[ + { + "network":"203.0.113.48\/28", + "peerId":"192.0.2.18", + "path":"(300) 400", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.0.2.50", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_confed1/r2/bgp_summary.json b/tests/topotests/bgp_confed1/r2/bgp_summary.json new file mode 100644 index 0000000..c2690a1 --- /dev/null +++ b/tests/topotests/bgp_confed1/r2/bgp_summary.json @@ -0,0 +1,18 @@ +{ + "ipv4Unicast":{ + "routerId":"203.0.113.17", + "as":200, + "peers":{ + "192.0.2.1":{ + "remoteAs":100, + "state":"Established", + "peerState":"OK" + }, + "192.0.2.18":{ + "remoteAs":300, + "state":"Established", + "peerState":"OK" + } + } + } +} diff --git a/tests/topotests/bgp_confed1/r2/bgpd.conf b/tests/topotests/bgp_confed1/r2/bgpd.conf new file mode 100644 index 0000000..fe13dfe --- /dev/null +++ b/tests/topotests/bgp_confed1/r2/bgpd.conf @@ -0,0 +1,18 @@ +!debug bgp neighbor-events +!debug bgp nht +!debug bgp updates in +!debug bgp updates out +! +router bgp 200 + no bgp ebgp-requires-policy + bgp confederation identifier 300 + bgp confederation peers 300 + neighbor 192.0.2.1 remote-as 100 + neighbor 192.0.2.18 remote-as 300 + ! + address-family ipv4 unicast + network 203.0.113.16/28 + neighbor 192.0.2.18 default-originate + exit-address-family +! + diff --git a/tests/topotests/bgp_confed1/r2/isisd.conf b/tests/topotests/bgp_confed1/r2/isisd.conf new file mode 100644 index 0000000..135bb00 --- /dev/null +++ b/tests/topotests/bgp_confed1/r2/isisd.conf @@ -0,0 +1,8 @@ +interface r2-eth1 + ip router isis 1 + isis circuit-type level-2-only + +router isis 1 + is-type level-2-only + net 49.0001.0002.0002.0002.00 + redistribute ipv4 connected level-2 diff --git a/tests/topotests/bgp_confed1/r2/zebra.conf b/tests/topotests/bgp_confed1/r2/zebra.conf new file mode 100644 index 0000000..85ebe9e --- /dev/null +++ b/tests/topotests/bgp_confed1/r2/zebra.conf @@ -0,0 +1,9 @@ +interface r2-eth0 + ip address 192.0.2.2/28 +! +interface r2-eth1 + ip address 192.0.2.17/28 +! +interface lo + ip address 203.0.113.17/28 +! diff --git a/tests/topotests/bgp_confed1/r3/bgp_ipv4_unicast.json b/tests/topotests/bgp_confed1/r3/bgp_ipv4_unicast.json new file mode 100644 index 0000000..a263a9f --- /dev/null +++ b/tests/topotests/bgp_confed1/r3/bgp_ipv4_unicast.json @@ -0,0 +1,77 @@ +{ + "vrfId":0, + "vrfName":"default", + "routerId":"203.0.113.33", + "defaultLocPrf":100, + "localAS":300, + "routes":{"0.0.0.0/0":[ + { + "network":"0.0.0.0\/0", + "peerId":"192.0.2.17", + "path":"(200)", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.0.2.17", + "afi":"ipv4", + "used":true + } + ] + } +],"203.0.113.0/28":[ + { + "network":"203.0.113.0\/28", + "peerId":"192.0.2.17", + "path":"(200) 100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.0.2.1", + "afi":"ipv4", + "used":true + } + ] + } +],"203.0.113.16/28":[ + { + "network":"203.0.113.16\/28", + "peerId":"192.0.2.17", + "path":"(200)", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.0.2.17", + "afi":"ipv4", + "used":true + } + ] + } +],"203.0.113.32/28":[ + { + "network":"203.0.113.32\/28", + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "afi":"ipv4", + "used":true + } + ] + } +],"203.0.113.48/28":[ + { + "network":"203.0.113.48\/28", + "peerId":"192.0.2.50", + "path":"400", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.0.2.50", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_confed1/r3/bgp_summary.json b/tests/topotests/bgp_confed1/r3/bgp_summary.json new file mode 100644 index 0000000..0cc0d53 --- /dev/null +++ b/tests/topotests/bgp_confed1/r3/bgp_summary.json @@ -0,0 +1,18 @@ +{ + "ipv4Unicast":{ + "routerId":"203.0.113.33", + "as":300, + "peers":{ + "192.0.2.17":{ + "remoteAs":200, + "state":"Established", + "peerState":"OK" + }, + "192.0.2.50":{ + "remoteAs":400, + "state":"Established", + "peerState":"OK" + } + } + } +} diff --git a/tests/topotests/bgp_confed1/r3/bgpd.conf b/tests/topotests/bgp_confed1/r3/bgpd.conf new file mode 100644 index 0000000..74d5fd6 --- /dev/null +++ b/tests/topotests/bgp_confed1/r3/bgpd.conf @@ -0,0 +1,17 @@ +!debug bgp neighbor-events +!debug bgp nht +!debug bgp updates in +!debug bgp updates out +! +router bgp 300 + no bgp ebgp-requires-policy + bgp confederation identifier 300 + bgp confederation peers 200 + neighbor 192.0.2.17 remote-as 200 + neighbor 192.0.2.50 remote-as 400 +! + address-family ipv4 unicast + network 203.0.113.32/28 + exit-address-family +! + diff --git a/tests/topotests/bgp_confed1/r3/isisd.conf b/tests/topotests/bgp_confed1/r3/isisd.conf new file mode 100644 index 0000000..a0b1200 --- /dev/null +++ b/tests/topotests/bgp_confed1/r3/isisd.conf @@ -0,0 +1,8 @@ +interface r3-eth1 + ip router isis 1 + isis circuit-type level-2-only + +router isis 1 + is-type level-2-only + net 49.0001.0003.0003.0003.00 + redistribute ipv4 connected level-2 diff --git a/tests/topotests/bgp_confed1/r3/zebra.conf b/tests/topotests/bgp_confed1/r3/zebra.conf new file mode 100644 index 0000000..555c8ed --- /dev/null +++ b/tests/topotests/bgp_confed1/r3/zebra.conf @@ -0,0 +1,10 @@ +! +interface r3-eth0 + ip address 192.0.2.49/28 +! +interface r3-eth1 + ip address 192.0.2.18/28 +! +interface lo + ip address 203.0.113.33/28 +! diff --git a/tests/topotests/bgp_confed1/r4/bgp_ipv4_unicast.json b/tests/topotests/bgp_confed1/r4/bgp_ipv4_unicast.json new file mode 100644 index 0000000..8a1f0b6 --- /dev/null +++ b/tests/topotests/bgp_confed1/r4/bgp_ipv4_unicast.json @@ -0,0 +1,77 @@ +{ + "vrfId":0, + "vrfName":"default", + "routerId":"203.0.113.49", + "defaultLocPrf":100, + "localAS":400, + "routes":{"0.0.0.0/0":[ + { + "network":"0.0.0.0\/0", + "peerId":"192.0.2.49", + "path":"300", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.0.2.49", + "afi":"ipv4", + "used":true + } + ] + } +],"203.0.113.0/28":[ + { + "network":"203.0.113.0\/28", + "peerId":"192.0.2.49", + "path":"300 100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.0.2.49", + "afi":"ipv4", + "used":true + } + ] + } +],"203.0.113.16/28":[ + { + "network":"203.0.113.16\/28", + "peerId":"192.0.2.49", + "path":"300", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.0.2.49", + "afi":"ipv4", + "used":true + } + ] + } +],"203.0.113.32/28":[ + { + "network":"203.0.113.32\/28", + "peerId":"192.0.2.49", + "path":"300", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.0.2.49", + "afi":"ipv4", + "used":true + } + ] + } +],"203.0.113.48/28":[ + { + "network":"203.0.113.48\/28", + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_confed1/r4/bgp_summary.json b/tests/topotests/bgp_confed1/r4/bgp_summary.json new file mode 100644 index 0000000..11a0c45 --- /dev/null +++ b/tests/topotests/bgp_confed1/r4/bgp_summary.json @@ -0,0 +1,13 @@ +{ + "ipv4Unicast":{ + "routerId":"203.0.113.49", + "as":400, + "peers":{ + "192.0.2.49":{ + "remoteAs":300, + "state":"Established", + "peerState":"OK" + } + } + } +} diff --git a/tests/topotests/bgp_confed1/r4/bgpd.conf b/tests/topotests/bgp_confed1/r4/bgpd.conf new file mode 100644 index 0000000..89a85e5 --- /dev/null +++ b/tests/topotests/bgp_confed1/r4/bgpd.conf @@ -0,0 +1,14 @@ +!debug bgp neighbor-events +!debug bgp nht +!debug bgp updates in +!debug bgp updates out +! +router bgp 400 + no bgp ebgp-requires-policy + bgp disable-ebgp-connected-route-check +! + neighbor 192.0.2.49 remote-as 300 + address-family ipv4 unicast + network 203.0.113.48/28 + exit-address-family +! diff --git a/tests/topotests/bgp_confed1/r4/isisd.conf b/tests/topotests/bgp_confed1/r4/isisd.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_confed1/r4/zebra.conf b/tests/topotests/bgp_confed1/r4/zebra.conf new file mode 100644 index 0000000..28c448e --- /dev/null +++ b/tests/topotests/bgp_confed1/r4/zebra.conf @@ -0,0 +1,6 @@ +interface r4-eth0 + ip address 192.0.2.50/28 +! +interface lo + ip address 203.0.113.49/28 +! diff --git a/tests/topotests/bgp_confed1/test_bgp_confed1.py b/tests/topotests/bgp_confed1/test_bgp_confed1.py new file mode 100644 index 0000000..57a8522 --- /dev/null +++ b/tests/topotests/bgp_confed1/test_bgp_confed1.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_confed1.py +# +# Copyright 2022 6WIND S.A. +# + +""" +test_bgp_confed1.py: Test the FRR BGP confederations with AS member +same as confederation Id, verify BGP prefixes and path distribution +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + +def setup_module(mod): + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_convergence(): + "Assert that BGP is converging." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bgp peers to go up") + + for router in tgen.routers().values(): + ref_file = "{}/{}/bgp_summary.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=125, wait=2.0) + assertmsg = "{}: bgp did not converge".format(router.name) + assert res is None, assertmsg + + +def test_bgp_confed_ipv4_unicast(): + "Assert that BGP is exchanging BGP route." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bgp peers exchanging UPDATES") + + for router in tgen.routers().values(): + ref_file = "{}/{}/bgp_ipv4_unicast.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show bgp ipv4 unicast json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=40, wait=2.5) + assertmsg = "{}: BGP UPDATE exchange failure".format(router.name) + assert res is None, assertmsg + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_confederation_astype/__init__.py b/tests/topotests/bgp_confederation_astype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_confederation_astype/r1/bgpd.conf b/tests/topotests/bgp_confederation_astype/r1/bgpd.conf new file mode 100644 index 0000000..1859a1b --- /dev/null +++ b/tests/topotests/bgp_confederation_astype/r1/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65001 + no bgp ebgp-requires-policy + bgp confederation identifier 65300 + bgp confederation peers 65002 65003 + neighbor fabric peer-group + neighbor fabric remote-as external + neighbor 192.168.1.2 peer-group fabric + neighbor 192.168.2.2 remote-as external + address-family ipv4 unicast + neighbor fabric activate + exit-address-family +! diff --git a/tests/topotests/bgp_confederation_astype/r1/zebra.conf b/tests/topotests/bgp_confederation_astype/r1/zebra.conf new file mode 100644 index 0000000..608a241 --- /dev/null +++ b/tests/topotests/bgp_confederation_astype/r1/zebra.conf @@ -0,0 +1,7 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +int r1-eth1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/bgp_confederation_astype/r2/bgpd.conf b/tests/topotests/bgp_confederation_astype/r2/bgpd.conf new file mode 100644 index 0000000..697af97 --- /dev/null +++ b/tests/topotests/bgp_confederation_astype/r2/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 65002 + no bgp ebgp-requires-policy + no bgp network import-check + bgp confederation identifier 65300 + bgp confederation peers 65001 + neighbor fabric peer-group + neighbor fabric remote-as external + neighbor 192.168.1.1 peer-group fabric + address-family ipv4 unicast + network 172.16.255.254/32 + neighbor fabric activate + exit-address-family +! diff --git a/tests/topotests/bgp_confederation_astype/r2/zebra.conf b/tests/topotests/bgp_confederation_astype/r2/zebra.conf new file mode 100644 index 0000000..cffe827 --- /dev/null +++ b/tests/topotests/bgp_confederation_astype/r2/zebra.conf @@ -0,0 +1,4 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_confederation_astype/r3/bgpd.conf b/tests/topotests/bgp_confederation_astype/r3/bgpd.conf new file mode 100644 index 0000000..c1f93f6 --- /dev/null +++ b/tests/topotests/bgp_confederation_astype/r3/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65003 + no bgp ebgp-requires-policy + no bgp network import-check + bgp confederation identifier 65300 + bgp confederation peers 65001 + neighbor 192.168.2.1 remote-as external + address-family ipv4 unicast + network 172.16.255.254/32 + exit-address-family +! diff --git a/tests/topotests/bgp_confederation_astype/r3/zebra.conf b/tests/topotests/bgp_confederation_astype/r3/zebra.conf new file mode 100644 index 0000000..e5a37c9 --- /dev/null +++ b/tests/topotests/bgp_confederation_astype/r3/zebra.conf @@ -0,0 +1,4 @@ +! +int r3-eth0 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/bgp_confederation_astype/test_bgp_confederation_astype.py b/tests/topotests/bgp_confederation_astype/test_bgp_confederation_astype.py new file mode 100644 index 0000000..5310d3b --- /dev/null +++ b/tests/topotests/bgp_confederation_astype/test_bgp_confederation_astype.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Test if BGP confederation works properly when using +remote-as internal/external. + +Also, check if the same works with peer-groups as well. +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2"), "s2": ("r1", "r3")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_confederation_astype(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp summary json")) + expected = { + "ipv4Unicast": { + "peerCount": 2, + "peers": { + "192.168.1.2": { + "hostname": "r2", + "remoteAs": 65002, + "localAs": 65001, + "pfxRcd": 1, + "state": "Established", + }, + "192.168.2.2": { + "hostname": "r3", + "remoteAs": 65003, + "localAs": 65001, + "pfxRcd": 1, + "state": "Established", + }, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge" + + def _bgp_check_neighbors(): + output = json.loads(r1.vtysh_cmd("show bgp neighbors json")) + expected = { + "192.168.1.2": { + "nbrCommonAdmin": True, + "nbrConfedExternalLink": True, + "hostname": "r2", + }, + "192.168.2.2": { + "nbrCommonAdmin": True, + "nbrConfedExternalLink": True, + "hostname": "r3", + }, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_neighbors) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't see neighbors to be in BGP confederation" + + def _bgp_check_routes(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "172.16.255.254/32": [ + { + "valid": True, + "pathFrom": "external", + "path": "(65003)", + }, + { + "valid": True, + "pathFrom": "external", + "path": "(65002)", + }, + ] + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't see routes to be in BGP confederation" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_afi_safi/__init__.py b/tests/topotests/bgp_default_afi_safi/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_default_afi_safi/r1/bgpd.conf b/tests/topotests/bgp_default_afi_safi/r1/bgpd.conf new file mode 100644 index 0000000..bf39152 --- /dev/null +++ b/tests/topotests/bgp_default_afi_safi/r1/bgpd.conf @@ -0,0 +1,3 @@ +! +router bgp 65001 +! diff --git a/tests/topotests/bgp_default_afi_safi/r1/zebra.conf b/tests/topotests/bgp_default_afi_safi/r1/zebra.conf new file mode 100644 index 0000000..6977651 --- /dev/null +++ b/tests/topotests/bgp_default_afi_safi/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! + diff --git a/tests/topotests/bgp_default_afi_safi/r2/bgpd.conf b/tests/topotests/bgp_default_afi_safi/r2/bgpd.conf new file mode 100644 index 0000000..abbd1b8 --- /dev/null +++ b/tests/topotests/bgp_default_afi_safi/r2/bgpd.conf @@ -0,0 +1,5 @@ +! +router bgp 65001 + no bgp default ipv4-unicast + bgp default ipv6-unicast +! diff --git a/tests/topotests/bgp_default_afi_safi/r2/zebra.conf b/tests/topotests/bgp_default_afi_safi/r2/zebra.conf new file mode 100644 index 0000000..606c17b --- /dev/null +++ b/tests/topotests/bgp_default_afi_safi/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default_afi_safi/r3/bgpd.conf b/tests/topotests/bgp_default_afi_safi/r3/bgpd.conf new file mode 100644 index 0000000..f3ec3f0 --- /dev/null +++ b/tests/topotests/bgp_default_afi_safi/r3/bgpd.conf @@ -0,0 +1,5 @@ +! +router bgp 65001 + no bgp default ipv4-unicast + bgp default l2vpn-evpn +! diff --git a/tests/topotests/bgp_default_afi_safi/r3/zebra.conf b/tests/topotests/bgp_default_afi_safi/r3/zebra.conf new file mode 100644 index 0000000..e9fdfb7 --- /dev/null +++ b/tests/topotests/bgp_default_afi_safi/r3/zebra.conf @@ -0,0 +1,6 @@ +! +interface r3-eth0 + ip address 192.168.255.3/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default_afi_safi/r4/bgpd.conf b/tests/topotests/bgp_default_afi_safi/r4/bgpd.conf new file mode 100644 index 0000000..8a6af55 --- /dev/null +++ b/tests/topotests/bgp_default_afi_safi/r4/bgpd.conf @@ -0,0 +1,5 @@ +! +router bgp 65001 + bgp default ipv6-unicast + bgp default l2vpn-evpn +! diff --git a/tests/topotests/bgp_default_afi_safi/r4/zebra.conf b/tests/topotests/bgp_default_afi_safi/r4/zebra.conf new file mode 100644 index 0000000..e9fdfb7 --- /dev/null +++ b/tests/topotests/bgp_default_afi_safi/r4/zebra.conf @@ -0,0 +1,6 @@ +! +interface r3-eth0 + ip address 192.168.255.3/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default_afi_safi/test_bgp-default-afi-safi.py b/tests/topotests/bgp_default_afi_safi/test_bgp-default-afi-safi.py new file mode 100644 index 0000000..a0014c7 --- /dev/null +++ b/tests/topotests/bgp_default_afi_safi/test_bgp-default-afi-safi.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by +# Donatas Abraitis +# + +""" +Test if `bgp default ipv4-unicast`, `bgp default ipv6-unicast` +and `bgp default l2vpn-evpn` commands work as expected. + +STEP 1: 'Check if neighbor 192.168.255.254 is enabled for ipv4 address-family only' +STEP 2: 'Check if neighbor 192.168.255.254 is enabled for ipv6 address-family only' +STEP 3: 'Check if neighbor 192.168.255.254 is enabled for l2vpn evpn address-family only' +STEP 4: 'Check if neighbor 192.168.255.254 is enabled for ipv4/ipv6 unicast and l2vpn evpn address-families' +""" + +import os +import sys +import json +import pytest + +pytestmark = [pytest.mark.bgpd] + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_default_ipv4_ipv6_unicast(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Check if neighbor 192.168.255.254 is enabled for ipv4 address-family only") + + def _bgp_neighbor_ipv4_af_only(): + tgen.gears["r1"].vtysh_cmd( + "conf t\nrouter bgp\nneighbor 192.168.255.254 remote-as external" + ) + + output = json.loads(tgen.gears["r1"].vtysh_cmd("show bgp summary json")) + + if len(output.keys()) == 1 and "ipv4Unicast" in output: + return True + return False + + assert _bgp_neighbor_ipv4_af_only() == True + + step("Check if neighbor 192.168.255.254 is enabled for ipv6 address-family only") + + def _bgp_neighbor_ipv6_af_only(): + tgen.gears["r2"].vtysh_cmd( + "conf t\nrouter bgp\nneighbor 192.168.255.254 remote-as external" + ) + + output = json.loads(tgen.gears["r2"].vtysh_cmd("show bgp summary json")) + + if len(output.keys()) == 1 and "ipv6Unicast" in output: + return True + return False + + assert _bgp_neighbor_ipv6_af_only() == True + + step("Check if neighbor 192.168.255.254 is enabled for evpn address-family only") + + def _bgp_neighbor_evpn_af_only(): + tgen.gears["r3"].vtysh_cmd( + "conf t\nrouter bgp\nneighbor 192.168.255.254 remote-as external" + ) + + output = json.loads(tgen.gears["r3"].vtysh_cmd("show bgp summary json")) + + if len(output.keys()) == 1 and "l2VpnEvpn" in output: + return True + return False + + assert _bgp_neighbor_evpn_af_only() == True + + step( + "Check if neighbor 192.168.255.254 is enabled for ipv4/ipv6 unicast and evpn address-families" + ) + + def _bgp_neighbor_ipv4_ipv6_and_evpn_af(): + tgen.gears["r4"].vtysh_cmd( + "conf t\nrouter bgp\nneighbor 192.168.255.254 remote-as external" + ) + + output = json.loads(tgen.gears["r4"].vtysh_cmd("show bgp summary json")) + + if ( + len(output.keys()) == 3 + and "ipv4Unicast" in output + and "ipv6Unicast" in output + and "l2VpnEvpn" in output + ): + return True + return False + + assert _bgp_neighbor_ipv4_ipv6_and_evpn_af() == True + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_originate/bgp_default_orginate_vrf.json b/tests/topotests/bgp_default_originate/bgp_default_orginate_vrf.json new file mode 100644 index 0000000..1a3d66b --- /dev/null +++ b/tests/topotests/bgp_default_originate/bgp_default_orginate_vrf.json @@ -0,0 +1,325 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "192.168.0.0", + "ipv4mask": 3024, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "192.168.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r0": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r0": {} + } + } + } + } + } + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [ + { + "local_as": "1000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r0": { + "dest_link": { + "r1": {} + } + }, + "r2": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r0": { + "dest_link": { + "r1": {} + } + }, + "r2": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + ] + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + } + }, + "vrfs": [ + { + "name": "RED", + "id": "1" + } + ], + "bgp": [ + { + "local_as": "2000", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + }, + { + "local_as": "1000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + ] + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback", + "vrf": "RED" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "vrfs": [ + { + "name": "RED", + "id": "1" + } + ], + "bgp": [ + { + "local_as": "3000", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + }, + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + ] + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [ + { + "local_as": "500", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + ] + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_default_originate/bgp_default_originate_2links.json b/tests/topotests/bgp_default_originate/bgp_default_originate_2links.json new file mode 100644 index 0000000..9e98235 --- /dev/null +++ b/tests/topotests/bgp_default_originate/bgp_default_originate_2links.json @@ -0,0 +1,136 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "192.168.0.0", + "ipv4mask": 3024, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "192.168.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": {"neighbor": {"r1": {"dest_link": {"r0": {}}}}} + }, + "ipv6": { + "unicast": {"neighbor": {"r1": {"dest_link": {"r0": {}}}}} + } + } + } + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r0": {"dest_link": {"r1": {}}}, + "r2": {"dest_link": {"r1-link1": {}, "r1-link2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r0": {"dest_link": {"r1": {}}}, + "r2": {"dest_link": {"r1-link1": {}, "r1-link2": {}}} + } + } + } + } + } + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2-link1": {}, "r2-link2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2-link1": {}, "r2-link2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + } + } + } + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "500", + "address_family": { + "ipv4": { + "unicast": {"neighbor": {"r3": {"dest_link": {"r4": {}}}}} + }, + "ipv6": { + "unicast": {"neighbor": {"r3": {"dest_link": {"r4": {}}}}} + } + } + } + } + } +} diff --git a/tests/topotests/bgp_default_originate/bgp_default_originate_topo1.json b/tests/topotests/bgp_default_originate/bgp_default_originate_topo1.json new file mode 100644 index 0000000..5fae34d --- /dev/null +++ b/tests/topotests/bgp_default_originate/bgp_default_originate_topo1.json @@ -0,0 +1,294 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "192.168.0.0", + "ipv4mask": 3024, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "192.168.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r0": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r0": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r0": { + "dest_link": { + "r1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r0": { + "dest_link": { + "r1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {"keepalivetimer": 1, + "holddowntimer": 3} + } + }, + "r3": { + "dest_link": { + "r2": {"keepalivetimer": 1, + "holddowntimer": 3} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {"keepalivetimer": 1, + "holddowntimer": 3} + } + }, + "r3": { + "dest_link": { + "r2": {"keepalivetimer": 1, + "holddowntimer": 3} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"keepalivetimer": 1, + "holddowntimer": 3} + } + }, + "r4": { + "dest_link": { + "r3": {"keepalivetimer": 1, + "holddowntimer": 3} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"keepalivetimer": 1, + "holddowntimer": 3} + } + }, + "r4": { + "dest_link": { + "r3": {"keepalivetimer": 1, + "holddowntimer": 3} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "500", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {"keepalivetimer": 1, + "holddowntimer": 3} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {"keepalivetimer": 1, + "holddowntimer": 3} + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_default_originate/test_bgp_default_originate_2links.py b/tests/topotests/bgp_default_originate/test_bgp_default_originate_2links.py new file mode 100644 index 0000000..75e6656 --- /dev/null +++ b/tests/topotests/bgp_default_originate/test_bgp_default_originate_2links.py @@ -0,0 +1,1808 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Shreenidhi A R +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# +""" +Following tests are covered. +1. Verify default-originate route with default static and network command +2. Verify default-originate route with aggregate summary command +3. Verfiy default-originate behaviour in ecmp +""" +import os +import sys +import time +import pytest +import datetime +from copy import deepcopy +from lib.topolog import logger +from time import sleep + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json +from lib.topolog import logger +from lib import topotest + +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, + get_dut_as_number, + verify_rib_default_route, + verify_fib_default_route, +) +from lib.common_config import ( + verify_fib_routes, + step, + create_prefix_lists, + run_frr_cmd, + create_route_maps, + shutdown_bringup_interface, + get_frr_ipv6_linklocal, + start_topology, + apply_raw_config, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + create_static_routes, + check_router_status, +) + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers + +# Global variables +topo = None +NETWORK1_1 = {"ipv4": "198.51.1.1/32", "ipv6": "2001:DB8::1:1/128"} +NETWORK1_2 = {"ipv4": "198.51.1.2/32", "ipv6": "2001:DB8::1:2/128"} +NETWORK1_3 = {"ipv4": "198.51.1.3/32", "ipv6": "2001:DB8::1:3/128"} +NETWORK1_4 = {"ipv4": "198.51.1.4/32", "ipv6": "2001:DB8::1:4/128"} +NETWORK1_5 = {"ipv4": "198.51.1.5/32", "ipv6": "2001:DB8::1:5/128"} + +ipv4_uptime_dict = { + "r2": { + "static_routes": [ + {"network": "0.0.0.0/0"}, + ] + } +} + +ipv6_uptime_dict = { + "r2": { + "static_routes": [ + {"network": "::/0"}, + ] + } +} + +DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_default_originate_2links.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global ADDR_TYPES + global BGP_CONVERGENCE + global DEFAULT_ROUTES + global DEFAULT_ROUTE_NXT_HOP_LINK1, DEFAULT_ROUTE_NXT_HOP_LINK2 + ADDR_TYPES = check_address_types() + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + interface = topo["routers"]["r1"]["links"]["r2-link1"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface) + ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0] + ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0] + DEFAULT_ROUTE_NXT_HOP_LINK1 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + + interface = topo["routers"]["r1"]["links"]["r2-link2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface) + ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2-link2"]["ipv4"].split("/")[0] + ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2-link2"]["ipv6"].split("/")[0] + DEFAULT_ROUTE_NXT_HOP_LINK2 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Local API's +# +##################################################### + + +def get_rib_route_uptime(tgen, addr_type, dut, input_dict): + """ + Verify route uptime in RIB using "show ip route" + + Parameters + ---------- + * `tgen` : topogen object + * `addr_type` : ip type, ipv4/ipv6 + * `dut`: Device Under Test, for which user wants to test the data + * `input_dict` : input dict, has details of static routes + * `route_uptime`: uptime of the routes + + Usage + ----- + # Creating static routes for r1 + input_dict_r1 = { + "r1": { + "static_routes": [ + { + "network": "147.10.13.4/32" + }, + { + "network": "147.10.12.0/24" + }, + { + "network": "147.10.13.4/32" + }, + { + "network": "147.10.13.4/32" + }, + { + "network": "147.10.13.4/32" + } + ] + } + } + + + Returns + ------- + errormsg(str) or True + """ + + logger.info("Entering lib API: get_rib_route_uptime()") + route_time = [] + out_route_dict = {} + router_list = tgen.routers() + for routerInput in input_dict.keys(): + for router, rnode in router_list.items(): + if router != dut: + continue + + logger.info("Checking router %s RIB:", router) + + # Verifying RIB routes + if addr_type == "ipv4": + command = "show ip route" + else: + command = "show ipv6 route" + + if "static_routes" in input_dict[routerInput]: + static_routes = input_dict[routerInput]["static_routes"] + + for static_route in static_routes: + if "vrf" in static_route and static_route["vrf"] is not None: + + logger.info( + "[DUT: {}]: Verifying routes for VRF:" + " {}".format(router, static_route["vrf"]) + ) + cmd = "{} vrf {}".format(command, static_route["vrf"]) + + else: + cmd = "{}".format(command) + + cmd = "{} json".format(cmd) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + if bool(rib_routes_json) is False: + errormsg = "No route found in rib of router {}..".format(router) + return errormsg + network = static_route["network"] + route_time.append(rib_routes_json[network][0]["uptime"]) + + logger.info("Exiting lib API: get_rib_route_uptime()") + return route_time + + +def verify_the_uptime(time_stamp_before, time_stamp_after, incremented=None): + """ + time_stamp_before : string the time stamp captured + time_stamp_after : string the time stamp captured + """ + uptime_before = datetime.datetime.strptime(time_stamp_before[0], "%H:%M:%S") + uptime_after = datetime.datetime.strptime(time_stamp_after[0], "%H:%M:%S") + + if incremented == True: + if uptime_before < uptime_after: + logger.info( + " The Uptime [{}] is incremented than [{}].......PASSED ".format( + time_stamp_before, time_stamp_after + ) + ) + return True + else: + logger.error( + " The Uptime [{}] is expected to be incremented than [{}].......FAILED ".format( + time_stamp_before, time_stamp_after + ) + ) + return False + else: + logger.info( + " The Uptime [{}] is not incremented than [{}] ".format( + time_stamp_before, time_stamp_after + ) + ) + return True + + +def get_best_path_route_in_FIB(tgen, topo, dut, network): + """ + API to verify the best route in FIB and return the ipv4 and ipv6 nexthop for the given route + command + ======= + show ip route + show ipv6 route + params + ====== + dut : device under test : + network ; route (ip) to which the best route to be retrieved + Returns + ======== + on success : return dict with next hops for the best hop + on failure : return error message with boolean False + """ + is_ipv4_best_path_found = False + is_ipv6_best_path_found = False + rnode = tgen.routers()[dut] + ipv4_show_bgp_json = run_frr_cmd(rnode, "sh ip bgp json ", isjson=True) + ipv6_show_bgp_json = run_frr_cmd( + rnode, "sh ip bgp ipv6 unicast json ", isjson=True + ) + output_dict = {"ipv4": None, "ipv6": None} + ipv4_nxt_hop_count = len(ipv4_show_bgp_json["routes"][network["ipv4"]]) + for index in range(ipv4_nxt_hop_count): + if "bestpath" in ipv4_show_bgp_json["routes"][network["ipv4"]][index].keys(): + best_path_ip = ipv4_show_bgp_json["routes"][network["ipv4"]][index][ + "nexthops" + ][0]["ip"] + output_dict["ipv4"] = best_path_ip + logger.info( + "[DUT [{}]] Best path for the route {} is {} ".format( + dut, network["ipv4"], best_path_ip + ) + ) + is_ipv4_best_path_found = True + else: + logger.error("ERROR....! No Best Path Found in BGP RIB.... FAILED") + + ipv6_nxt_hop_count = len(ipv6_show_bgp_json["routes"][network["ipv6"]]) + for index in range(ipv6_nxt_hop_count): + if "bestpath" in ipv6_show_bgp_json["routes"][network["ipv6"]][index].keys(): + ip_add_count = len( + ipv6_show_bgp_json["routes"][network["ipv6"]][index]["nexthops"] + ) + for i_index in range(ip_add_count): + if ( + "global" + in ipv6_show_bgp_json["routes"][network["ipv6"]][index]["nexthops"][ + i_index + ]["scope"] + ): + best_path_ip = ipv6_show_bgp_json["routes"][network["ipv6"]][index][ + "nexthops" + ][i_index]["ip"] + output_dict["ipv6"] = best_path_ip + logger.info( + "[DUT [{}]] Best path for the route {} is {} ".format( + dut, network["ipv6"], best_path_ip + ) + ) + + else: + logger.error("ERROR....! No Best Path Found in BGP RIB.... FAILED") + if is_ipv4_best_path_found: + return output_dict + else: + logger.error("ERROR...! Unable to find the Best Path in the RIB") + return False + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_verify_bgp_default_originate_with_default_static_route_p1(request): + """ + Summary: "Verify default-originate route with default static and network command " + + """ + tgen = get_topogen() + global BGP_CONVERGENCE, DEFAULT_ROUTE_NXT_HOP_LINK1, DEFAULT_ROUTE_NXT_HOP_LINK2, DEFAULT_ROUTES + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure 2 link between R1 and R2") + step("Configure IPV4 and IPV6 EBGP between R1 and R2 both the links") + step("Configure default-originate on R1 IPv4 and IPv6 BGP session link-1 only ") + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "default_originate": {"r2": {"dest-link": "r1-link1"}} + } + }, + "ipv6": { + "unicast": { + "default_originate": {"r2": {"dest-link": "r1-link1"}} + } + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify IPv4/IPv6 default originate routes present on R2 nexthop as link-1") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_LINK1[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_LINK1[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_LINK1[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure network command on R1 (0.0.0.0/0 and 0::0/0) for IPv4 and IPv6 address family " + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + for addr_type in ADDR_TYPES: + input_advertise = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + {"network": [DEFAULT_ROUTES[addr_type]]} + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_advertise) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("No change on IPv4/IPv6 default-originate route advertised from link1") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify 0.0.0.0/0 and 0::0/0 route also get advertised from link-2 ") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Before removing default originate from R1 link -1 IPv4 and IPv6 address family taking the uptime snapshot" + ) + uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step("Remove default originate from R1 link -1 IPv4 and IPv6 address family ") + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "default_originate": { + "r2": {"dest-link": "r1-link1", "delete": True} + } + } + }, + "ipv6": { + "unicast": { + "default_originate": { + "r2": {"dest-link": "r1-link1", "delete": True} + } + } + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Routes must be learned from network command") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("After removing the default originate on R1 taking the uptime snapshot") + uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step( + "After removing the default-originate uptime should get reset for link-1 learn route" + ) + result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Taking uptime snapshot before configuring default - originate") + uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + sleep(1) + + step( + "Configure default-originate on R1 link-1 again for IPv4 and IPv6 address family" + ) + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "default_originate": { + "r2": { + "dest-link": "r1-link1", + } + } + } + }, + "ipv6": { + "unicast": { + "default_originate": { + "r2": { + "dest-link": "r1-link1", + } + } + } + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify No change on R2 routing and BGP table for both the links ") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Taking snapshot after configuring default - originate") + uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step( + "After configuring the default-originate uptime should not get reset for link-1 learn route" + ) + result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=True) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=True) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Taking uptime snapshot before removing network 0.0.0.0 ") + uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step("Remove network command from R1 IPv4/IPv6 address family ") + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + for addr_type in ADDR_TYPES: + input_advertise = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "delete": True, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_advertise) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify 0.0.0.0/0 and 0::0/0 route get removed from link-2 and default-originate IPv4/IPv6 route learn on link-1" + ) + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Route from link2 is not expected \n Error: {}".format( + tc_name, result + ) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed\n Route from link2 is not expected \n Error: {}".format( + tc_name, result + ) + + uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step( + "After removing default originate command on R1 verify that the uptime got reset on R2" + ) + + result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Taking uptime snapshot before configuring static route network") + uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step( + "Configure static default route for IPv4 and IPv6 (0.0.0.0/0 next-hop Null0 and 0::0/0 next-hop Null0) on R1" + ) + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": "0.0.0.0/0", + "next_hop": NEXT_HOP_IP["ipv4"], + }, + { + "network": "0::0/0", + "next_hop": NEXT_HOP_IP["ipv6"], + }, + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verifyIPv4 and IPv6 static routes are configure and up on R1 ") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": "0.0.0.0/0", + "next_hop": NEXT_HOP_IP["ipv4"], + }, + { + "network": "0::0/0", + "next_hop": NEXT_HOP_IP["ipv6"], + }, + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure redistribute static on IPv4 and IPv6 address family") + redistribute_static = { + "r1": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify No change on IPv4/IPv6 default-originate route advertised from link1") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify 0.0.0.0/0 and 0::0/0 route also get advertised from link-2 ") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed\n Best Path sould be advertised in routes\n Error: {}".format( + tc_name, result + ) + + step("Taking uptime snapshot before removing default originate") + uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step("Remove default-originate from link-1 from IPv4 and IPv6 neighbor ") + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "default_originate": { + "r2": {"dest-link": "r1-link1", "delete": True} + } + } + }, + "ipv6": { + "unicast": { + "default_originate": { + "r2": {"dest-link": "r1-link1", "delete": True} + } + } + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Taking uptime snapshot after removing default originate") + uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step("verify the up time , up time should get reset ") + result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify No change on IPv4/IPv6 default-originate route advertised from link1") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Taking uptime snapshot before configuring default originate") + uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step( + " Configure default-originate on link-1 again for IPv4 and IPv6 address family" + ) + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "default_originate": { + "r2": { + "dest-link": "r1-link1", + } + } + } + }, + "ipv6": { + "unicast": { + "default_originate": { + "r2": { + "dest-link": "r1-link1", + } + } + } + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify No change on IPv4/IPv6 default-originate route advertised from link1") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Taking uptime snapshot after configuring default originate") + uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step("After configuring the default originate the uptime should not get reset ") + result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Taking uptime snapshot before removing redisctribute static ") + uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + sleep(1) + + step("Remove redistribute static from IPv4 and IPv6 address family ") + input_dict_1 = { + "r1": { + "bgp": { + "local_as": get_dut_as_number(tgen, dut="r1"), + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + "ipv6": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify No change on IPv4/IPv6 default-originate route advertised from link1") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Taking uptime snapshot before removing redisctribute static ") + uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step("After removing default originate the route uptime should get reset ") + result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=True) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=True) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + write_test_footer(tc_name) + + +def test_verify_bgp_default_originate_with_aggregate_summary_p1(request): + """ + Summary: "Verify default-originate route with aggregate summary command" + """ + tgen = get_topogen() + global BGP_CONVERGENCE + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + step("After changing the BGP AS Path Verify the BGP Convergence") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Configure default-originate on R1 IPv4 and IPv6 BGP session link-1 only") + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "default_originate": {"r2": {"dest-link": "r1-link1"}} + } + }, + "ipv6": { + "unicast": { + "default_originate": {"r2": {"dest-link": "r1-link1"}} + } + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify IPv4/IPv6 default originate routes present on R2 nexthop as link-1,on R2" + ) + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure 5 static route for IPv4 and IPv6 on R0") + for addr_type in ADDR_TYPES: + input_advertise = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK1_3[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK1_4[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK1_5[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_advertise) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Before configuring the aggregate route taking uptime snapshot ") + uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step("Configure aggregate summary command for IPv4 and IPv6 address family ") + local_as = get_dut_as_number(tgen, dut="r1") + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(local_as), + "address-family ipv4 unicast", + "aggregate-address {} summary-only".format("0.0.0.0/0 "), + "exit-address-family", + "address-family ipv6 unicast", + "aggregate-address {} summary-only".format("0::0/0"), + "exit-address-family", + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "verify that no change on IPv4/IPv6 default-originate route advertised from link1 0.0.0.0/0 and 0::0/0 route also get advertised from link-2 on R2" + ) + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("After configuring the aggregate route taking uptime snapshot ") + uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step( + "After Configuring the aggregate route uptime should get reset for link-1 learn route" + ) + result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Before removing default originate taking uptime snapshot ") + uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step("Remove default originate from R1 link -1 IPv4 and IPv6 address family") + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "default_originate": { + "r2": {"dest-link": "r1-link1", "delete": True} + } + } + }, + "ipv6": { + "unicast": { + "default_originate": { + "r2": {"dest-link": "r1-link1", "delete": True} + } + } + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "verify that no change on IPv4/IPv6 default-originate route advertised from link1 0.0.0.0/0 and 0::0/0 route also get advertised from link-2 on R2" + ) + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("After removing default origin taking uptime snapshot ") + uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step( + "After removing the default-originate uptime should get reset for link-1 learn route" + ) + result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Before Configuring default origin taking uptime snapshot ") + uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step( + "Configure default-originate on R1 link-1 again for IPv4 and IPv6 address family" + ) + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "default_originate": { + "r2": { + "dest-link": "r1-link1", + } + } + } + }, + "ipv6": { + "unicast": { + "default_originate": { + "r2": { + "dest-link": "r1-link1", + } + } + } + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("After Configuring default originate taking uptime snapshot") + uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step( + "After Configuring the default-originate uptime should get reset for link-1 learn route" + ) + result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Before removing aggregate -summary command taking the uptime snapshot ") + uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step("remove aggregate summary command for IPv4 and IPv6 address family ") + local_as = get_dut_as_number(tgen, dut="r1") + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(local_as), + "address-family ipv4 unicast", + "no aggregate-address {} summary-only".format("0.0.0.0/0"), + "exit-address-family", + "address-family ipv6 unicast", + "no aggregate-address {} summary-only".format("0::0/0"), + "exit-address-family", + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify Default-originate IPv4/IPv6 route learn on link-1 ") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify 0.0.0.0/0 and 0::0/0 route get removed from link-2 ") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2, + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("After removing aggregate -summary command taking the uptime snapshot ") + uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) + uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + + step("After removing aggregate command uptime should get reset ") + result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + write_test_footer(tc_name) + + +def test_verify_default_originate_with_2way_ecmp_p2(request): + """ + Summary: "Verify default-originate route with 3 way ECMP and traffic " + """ + + tgen = get_topogen() + global BGP_CONVERGENCE + global DEFAULT_ROUTES + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Populating next-hops details") + r1_r2_ipv4_neighbor_ips = [] + r1_r2_ipv6_neighbor_ips = [] + r1_link = None + for index in range(1, 3): + r1_link = "r1-link" + str(index) + r1_r2_ipv4_neighbor_ips.append( + topo["routers"]["r2"]["links"][r1_link]["ipv4"].split("/")[0] + ) + r1_r2_ipv6_neighbor_ips.append( + topo["routers"]["r2"]["links"][r1_link]["ipv6"].split("/")[0] + ) + + step( + "Configure default-originate on R1 for all the neighbor of IPv4 and IPv6 peers " + ) + local_as = get_dut_as_number(tgen, dut="r1") + for index in range(2): + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(local_as), + "address-family ipv4 unicast", + "neighbor {} default-originate".format( + r1_r2_ipv4_neighbor_ips[index] + ), + "exit-address-family", + "address-family ipv6 unicast", + "neighbor {} default-originate ".format( + r1_r2_ipv6_neighbor_ips[index] + ), + "exit-address-family", + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "After configuring default-originate command , verify default routes are advertised on R2 " + ) + + r2_link = None + for index in range(1, 3): + r2_link = "r2-link" + str(index) + ipv4_nxt_hop = topo["routers"]["r1"]["links"][r2_link]["ipv4"].split("/")[0] + interface = topo["routers"]["r1"]["links"][r2_link]["interface"] + ipv6_link_local_nxt_hop = get_frr_ipv6_linklocal(tgen, "r1", intf=interface) + DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local_nxt_hop} + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Ping R1 configure IPv4 and IPv6 loopback address from R2") + pingaddr = topo["routers"]["r1"]["links"]["lo"]["ipv4"].split("/")[0] + router = tgen.gears["r2"] + + def ping_router(): + output = router.run("ping -c 4 -w 4 {}".format(pingaddr)) + logger.info(output) + if " 0% packet loss" not in output: + return False + + _, res = topotest.run_and_expect(ping_router, None, count=10, wait=1) + logger.info("Ping from R1 to R2 ... success") + + step("Shuting up the active route") + network = {"ipv4": "0.0.0.0/0", "ipv6": "::/0"} + ipv_dict = get_best_path_route_in_FIB(tgen, topo, dut="r2", network=network) + dut_links = topo["routers"]["r1"]["links"] + active_interface = None + for key, values in dut_links.items(): + ipv4_address = dut_links[key]["ipv4"].split("/")[0] + ipv6_address = dut_links[key]["ipv6"].split("/")[0] + if ipv_dict["ipv4"] == ipv4_address and ipv_dict["ipv6"] == ipv6_address: + active_interface = dut_links[key]["interface"] + + logger.info( + "Shutting down the interface {} on router {} ".format(active_interface, "r1") + ) + shutdown_bringup_interface(tgen, "r1", active_interface, False) + + step("Verify the complete convergence to fail after shutting the interface") + result = verify_bgp_convergence(tgen, topo, expected=False) + assert ( + result is not True + ), " Testcase {} : After shuting down the interface Convergence is expected to be Failed".format( + tc_name + ) + + step( + "Verify routes from active best path is not received from r1 after shuting the interface" + ) + r2_link = None + for index in range(1, 3): + r2_link = "r2-link" + str(index) + ipv4_nxt_hop = topo["routers"]["r1"]["links"][r2_link]["ipv4"].split("/")[0] + interface = topo["routers"]["r1"]["links"][r2_link]["interface"] + ipv6_link_local_nxt_hop = get_frr_ipv6_linklocal(tgen, "r1", intf=interface) + DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local_nxt_hop} + if index == 1: + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + else: + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Ping R1 configure IPv4 and IPv6 loopback address from R2") + pingaddr = topo["routers"]["r1"]["links"]["lo"]["ipv4"].split("/")[0] + router = tgen.gears["r2"] + output = router.run("ping -c 4 -w 4 {}".format(pingaddr)) + assert " 0% packet loss" in output, "Ping R1->R2 FAILED" + logger.info("Ping from R1 to R2 ... success") + + step("No Shuting up the active route") + + shutdown_bringup_interface(tgen, "r1", active_interface, True) + + step("Verify the complete convergence after bringup the interface") + result = verify_bgp_convergence(tgen, topo) + assert ( + result is True + ), " Testcase {} : After bringing up the interface complete convergence is expected ".format( + tc_name + ) + + step("Verify all the routes are received from r1 after no shuting the interface") + r2_link = None + for index in range(1, 3): + r2_link = "r2-link" + str(index) + ipv4_nxt_hop = topo["routers"]["r1"]["links"][r2_link]["ipv4"].split("/")[0] + interface = topo["routers"]["r1"]["links"][r2_link]["interface"] + ipv6_link_local_nxt_hop = get_frr_ipv6_linklocal(tgen, "r1", intf=interface) + DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local_nxt_hop} + if index == 1: + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + else: + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure IPv4 and IPv6 route-map with deny option on R2 to filter default route 0.0.0.0/0 and 0::0/0" + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + input_dict_3 = { + "r2": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + { + "seqid": "1", + "network": DEFAULT_ROUTES["ipv4"], + "action": "permit", + } + ] + }, + "ipv6": { + "Pv6": [ + { + "seqid": "1", + "network": DEFAULT_ROUTES["ipv6"], + "action": "permit", + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_3 = { + "r2": { + "route_maps": { + "RMv4": [ + { + "action": "deny", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + }, + ], + "RMv6": [ + { + "action": "deny", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Apply route-map IN direction of R2 ( R2-R1) for IPv4 and IPv6 BGP neighbors") + r2_link = None + for index in range(1, 3): + r2_link = "r2-link" + str(index) + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + r2_link: { + "route_maps": [ + {"name": "RMv4", "direction": "in"} + ] + }, + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + r2_link: { + "route_maps": [ + {"name": "RMv6", "direction": "in"} + ] + }, + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("After applying the route-map the routes are not expected in RIB ") + r2_link = None + for index in range(1, 3): + r2_link = "r2-link" + str(index) + ipv4_nxt_hop = topo["routers"]["r1"]["links"][r2_link]["ipv4"].split("/")[0] + interface = topo["routers"]["r1"]["links"][r2_link]["interface"] + ipv6_link_local_nxt_hop = get_frr_ipv6_linklocal(tgen, "r1", intf=interface) + DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local_nxt_hop} + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_1.py b/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_1.py new file mode 100644 index 0000000..6156968 --- /dev/null +++ b/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_1.py @@ -0,0 +1,2527 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Shreenidhi A R +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# +""" +Following tests are covered. +1. Verify BGP default-originate route with IBGP peer +2. Verify BGP default-originate route with EBGP peer +3. Verify BGP default route when default-originate configured with route-map over IBGP peer +4. Verify BGP default route when default-originate configured with route-map over EBGP peer" + +""" +import os +import sys +import time +import pytest +from time import sleep +from copy import deepcopy +from lib.topolog import logger + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json +from lib.topolog import logger + +from lib.bgp import ( + verify_bgp_convergence, + verify_graceful_restart, + create_router_bgp, + verify_router_id, + modify_as_number, + verify_as_numbers, + clear_bgp_and_verify, + clear_bgp, + verify_bgp_rib, + get_prefix_count_route, + get_dut_as_number, + verify_rib_default_route, + verify_fib_default_route, + verify_bgp_advertised_routes_from_neighbor, + verify_bgp_received_routes_from_neighbor, +) +from lib.common_config import ( + interface_status, + verify_prefix_lists, + verify_fib_routes, + kill_router_daemons, + start_router_daemons, + shutdown_bringup_interface, + step, + required_linux_kernel_version, + stop_router, + start_router, + create_route_maps, + create_prefix_lists, + get_frr_ipv6_linklocal, + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + create_static_routes, + check_router_status, + delete_route_maps, +) + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers + +# Global variables +topo = None +KEEPALIVETIMER = 1 +HOLDDOWNTIMER = 3 +# Global variables +NETWORK1_1 = {"ipv4": "1.1.1.1/32", "ipv6": "1::1/128"} +NETWORK1_2 = {"ipv4": "1.1.1.2/32", "ipv6": "1::2/128"} +NETWORK2_1 = {"ipv4": "2.1.1.1/32", "ipv6": "2::1/128"} +NETWORK2_2 = {"ipv4": "2.1.1.2/32", "ipv6": "2::2/128"} +NETWORK3_1 = {"ipv4": "3.1.1.1/32", "ipv6": "3::1/128"} +NETWORK3_2 = {"ipv4": "3.1.1.2/32", "ipv6": "3::2/128"} +NETWORK4_1 = {"ipv4": "4.1.1.1/32", "ipv6": "4::1/128"} +NETWORK4_2 = {"ipv4": "4.1.1.2/32", "ipv6": "4::2/128"} +NETWORK5_1 = {"ipv4": "5.1.1.1/32", "ipv6": "5::1/128"} +NETWORK5_2 = {"ipv4": "5.1.1.2/32", "ipv6": "5::2/128"} +DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + +IPV4_RM = "RMVIPV4" +IPV6_RM = "RMVIPV6" + +IPV4_RM1 = "RMVIPV41" +IPV6_RM1 = "RMVIPV61" + +IPV4_RM2 = "RMVIPV42" +IPV6_RM2 = "RMVIPV62" + +IPV4_PL_1 = "PV41" +IPV4_PL_2 = "PV42" + +IPV6_PL_1 = "PV61" +IPV6_PL_2 = "PV62" + + +r1_ipv4_loopback = "1.0.1.0/24" +r2_ipv4_loopback = "1.0.2.0/24" +r3_ipv4_loopback = "1.0.3.0/24" +r4_ipv4_loopback = "1.0.4.0/24" +r1_ipv6_loopback = "2001:db8:f::1:0/120" +r2_ipv6_loopback = "2001:db8:f::2:0/120" +r3_ipv6_loopback = "2001:db8:f::3:0/120" +r4_ipv6_loopback = "2001:db8:f::4:0/120" + +r0_connected_address_ipv4 = "192.168.0.0/24" +r0_connected_address_ipv6 = "fd00::/64" +r1_connected_address_ipv4 = "192.168.1.0/24" +r1_connected_address_ipv6 = "fd00:0:0:1::/64" +r3_connected_address_ipv4 = "192.168.2.0/24" +r3_connected_address_ipv6 = "fd00:0:0:2::/64" +r4_connected_address_ipv4 = "192.168.3.0/24" +r4_connected_address_ipv6 = "fd00:0:0:3::/64" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_default_originate_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global ADDR_TYPES + global BGP_CONVERGENCE + global DEFAULT_ROUTES + global DEFAULT_ROUTE_NXT_HOP_R1, DEFAULT_ROUTE_NXT_HOP_R3 + global R0_NETWORK_LOOPBACK, R0_NETWORK_LOOPBACK_NXTHOP, R1_NETWORK_LOOPBACK, R1_NETWORK_LOOPBACK_NXTHOP + global R0_NETWORK_CONNECTED, R0_NETWORK_CONNECTED_NXTHOP, R1_NETWORK_CONNECTED, R1_NETWORK_CONNECTED_NXTHOP + global R4_NETWORK_LOOPBACK, R4_NETWORK_LOOPBACK_NXTHOP, R3_NETWORK_LOOPBACK, R3_NETWORK_LOOPBACK_NXTHOP + global R4_NETWORK_CONNECTED, R4_NETWORK_CONNECTED_NXTHOP, R3_NETWORK_CONNECTED, R3_NETWORK_CONNECTED_NXTHOP + + ADDR_TYPES = check_address_types() + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + # There are the global varibles used through out the file these are acheived only after building the topology. + + r0_loopback_address_ipv4 = topo["routers"]["r0"]["links"]["lo"]["ipv4"] + r0_loopback_address_ipv4_nxt_hop = topo["routers"]["r0"]["links"]["r1"][ + "ipv4" + ].split("/")[0] + r0_loopback_address_ipv6 = topo["routers"]["r0"]["links"]["lo"]["ipv6"] + r0_loopback_address_ipv6_nxt_hop = topo["routers"]["r0"]["links"]["r1"][ + "ipv6" + ].split("/")[0] + + r1_loopback_address_ipv4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"] + r1_loopback_address_ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"][ + "ipv4" + ].split("/")[0] + r1_loopback_address_ipv6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"] + r1_loopback_address_ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"][ + "ipv6" + ].split("/")[0] + + r4_loopback_address_ipv4 = topo["routers"]["r4"]["links"]["lo"]["ipv4"] + r4_loopback_address_ipv4_nxt_hop = topo["routers"]["r4"]["links"]["r3"][ + "ipv4" + ].split("/")[0] + r4_loopback_address_ipv6 = topo["routers"]["r4"]["links"]["lo"]["ipv6"] + r4_loopback_address_ipv6_nxt_hop = topo["routers"]["r4"]["links"]["r3"][ + "ipv6" + ].split("/")[0] + + r3_loopback_address_ipv4 = topo["routers"]["r3"]["links"]["lo"]["ipv4"] + r3_loopback_address_ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"][ + "ipv4" + ].split("/")[0] + r3_loopback_address_ipv6 = topo["routers"]["r3"]["links"]["lo"]["ipv6"] + r3_loopback_address_ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"][ + "ipv6" + ].split("/")[0] + + R0_NETWORK_LOOPBACK = { + "ipv4": r0_loopback_address_ipv4, + "ipv6": r0_loopback_address_ipv6, + } + R0_NETWORK_LOOPBACK_NXTHOP = { + "ipv4": r0_loopback_address_ipv4_nxt_hop, + "ipv6": r0_loopback_address_ipv6_nxt_hop, + } + + R1_NETWORK_LOOPBACK = { + "ipv4": r1_loopback_address_ipv4, + "ipv6": r1_loopback_address_ipv6, + } + R1_NETWORK_LOOPBACK_NXTHOP = { + "ipv4": r1_loopback_address_ipv4_nxt_hop, + "ipv6": r1_loopback_address_ipv6_nxt_hop, + } + + R0_NETWORK_CONNECTED = { + "ipv4": r0_connected_address_ipv4, + "ipv6": r0_connected_address_ipv6, + } + R0_NETWORK_CONNECTED_NXTHOP = { + "ipv4": r0_loopback_address_ipv4_nxt_hop, + "ipv6": r0_loopback_address_ipv6_nxt_hop, + } + + R1_NETWORK_CONNECTED = { + "ipv4": r1_connected_address_ipv4, + "ipv6": r1_connected_address_ipv6, + } + R1_NETWORK_CONNECTED_NXTHOP = { + "ipv4": r1_loopback_address_ipv4_nxt_hop, + "ipv6": r1_loopback_address_ipv6_nxt_hop, + } + + R4_NETWORK_LOOPBACK = { + "ipv4": r4_loopback_address_ipv4, + "ipv6": r4_loopback_address_ipv6, + } + R4_NETWORK_LOOPBACK_NXTHOP = { + "ipv4": r4_loopback_address_ipv4_nxt_hop, + "ipv6": r4_loopback_address_ipv6_nxt_hop, + } + + R3_NETWORK_LOOPBACK = { + "ipv4": r3_loopback_address_ipv4, + "ipv6": r3_loopback_address_ipv6, + } + R3_NETWORK_LOOPBACK_NXTHOP = { + "ipv4": r3_loopback_address_ipv4_nxt_hop, + "ipv6": r3_loopback_address_ipv6_nxt_hop, + } + + R4_NETWORK_CONNECTED = { + "ipv4": r4_connected_address_ipv4, + "ipv6": r4_connected_address_ipv6, + } + R4_NETWORK_CONNECTED_NXTHOP = { + "ipv4": r4_loopback_address_ipv4_nxt_hop, + "ipv6": r4_loopback_address_ipv6_nxt_hop, + } + + R3_NETWORK_CONNECTED = { + "ipv4": r3_connected_address_ipv4, + "ipv6": r3_connected_address_ipv6, + } + R3_NETWORK_CONNECTED_NXTHOP = { + "ipv4": r3_loopback_address_ipv4_nxt_hop, + "ipv6": r3_loopback_address_ipv6_nxt_hop, + } + + # populating the nexthop for default routes + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + interface = topo["routers"]["r1"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface) + ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0] + ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0] + DEFAULT_ROUTE_NXT_HOP_R1 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + + interface = topo["routers"]["r3"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface) + ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0] + ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv6"].split("/")[0] + DEFAULT_ROUTE_NXT_HOP_R3 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + +##################################################### +# +# Testcases +# +##################################################### + +def test_verify_bgp_default_originate_in_IBGP_p0(request): + """ + Verify BGP default-originate route with IBGP peer + """ + tgen = get_topogen() + global BGP_CONVERGENCE + global topo + # test case name + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + step("Configure IPv4 and IPv6 , IBGP neighbor between R1 and R2") + step("Configure IPv4 and IPv6 Loopback interface on R1, R0 and R2") + step("Configure IPv4 and IPv6 EBGP neighbor between R0 and R1") + + r0_local_as = topo["routers"]["r0"]["bgp"]["local_as"] + r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"] + r2_local_as = topo["routers"]["r2"]["bgp"]["local_as"] + r3_local_as = topo["routers"]["r3"]["bgp"]["local_as"] + r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"] + + input_dict = { + "r0": { + "bgp": { + "local_as": 1000, + } + }, + "r1": { + "bgp": { + "local_as": 2000, + } + }, + "r2": { + "bgp": { + "local_as": 2000, + } + }, + "r3": { + "bgp": { + "local_as": r3_local_as, + } + }, + "r4": { + "bgp": { + "local_as": r4_local_as, + } + }, + } + result = modify_as_number(tgen, topo, input_dict) + try: + assert result is True + except AssertionError: + logger.info("Expected behaviour: {}".format(result)) + logger.info("BGP config is not created because of invalid ASNs") + + step("After changing the BGP AS Path Verify the BGP Convergence") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, " Complete Convergence is expected after changing the ASN but failed to converge --> :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Configure IPv4 and IPv6 static route on R1 next-hop as NULL0") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed to configure the static routes {} on router R1 \n Error: {}".format( + tc_name,static_routes_input, result + ) + step("verify IPv4 and IPv6 static route are configured and up on R1") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input) + assert result is True, "Testcase {} : Failed \n After configuring the static routes {} , the routes are not found in FIB \n Error: {}".format( + tc_name,static_routes_input, result + ) + + step( + "Configure redistribute static and connected on R0 and R1, for IPv4 and IPv6 address family " + ) + redistribute_static = { + "r0": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + }, + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert result is True, "Testcase {} : Failed to configure the redistribute static configuration \n Error: {}".format(tc_name, result) + + step( + "After configuring redistribute command , verify static and connected routes ( loopback connected routes) are advertised on R2" + ) + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_LOOPBACK[addr_type]], + "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_CONNECTED[addr_type]], + "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : After redistributing static routes the routes {} expected in FIB but NOT FOUND ......! \n Error: {}".format( + tc_name, static_routes_input,result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : After redistributing static routes the routes {} expected in RIB but NOT FOUND ......! \n Error: {}".format( + tc_name, static_routes_input , result + ) + + step( + "Taking the snapshot of the prefix count before configuring the default originate" + ) + snapshot1 = get_prefix_count_route(tgen, topo, dut="r2", peer="r1") + + step( + "Configure Default originate on R1 for R1 to R2, for IPv4 and IPv6 BGP address family " + ) + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": {"unicast": {"default_originate": {"r2": {}}}}, + "ipv6": {"unicast": {"default_originate": {"r2": {}}}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed Configuring default originate configuration. \n Error: {}".format(tc_name, result) + + step( + "After configuring default-originate command , verify default routes are advertised on R2 " + " R1 static and loopback routes received on R2 BGP and FIB" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R1_NETWORK_LOOPBACK[addr_type]], + "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_CONNECTED[addr_type]], + "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : post configuring the BGP Default originate configuration static and connected routes should not be effected but impacted on FIB .......! FAILED \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failedpost configuring the BGP Default originate configuration static and connected routes should not be effected but impacted on RIB......! FAILED \n Error: {}".format( + tc_name, result + ) + step( + "Verify default route for IPv4 and IPv6 present with path=igp metric =0 , local-preference= 100 " + ) + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + metric=0, + locPrf=100, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + step( + "Taking the snapshot2 of the prefix count after configuring the default originate" + ) + snapshot2 = get_prefix_count_route(tgen, topo, dut="r2", peer="r1") + + step("verifying the prefix count incrementing or not ") + isIPv4prefix_incremented = False + isIPv6prefix_incremented = False + if snapshot1["ipv4_count"] < snapshot2["ipv4_count"]: + isIPv4prefix_incremented = True + if snapshot1["ipv6_count"] < snapshot2["ipv6_count"]: + isIPv6prefix_incremented = True + + assert ( + isIPv4prefix_incremented is True + ), "Testcase {} : Failed Error: IPV4 Prefix is not incremented on receiveing ".format( + tc_name + ) + + assert ( + isIPv6prefix_incremented is True + ), "Testcase {} : Failed Error: IPV6 Prefix is not incremented on receiveing ".format( + tc_name + ) + write_test_footer(tc_name) + + +def test_verify_bgp_default_originate_in_EBGP_p0(request): + """ + Verify BGP default-originate route with EBGP peer + """ + tgen = get_topogen() + global BGP_CONVERGENCE + global topo + # test case name + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + step("Configure IPv4 and IPv6 , EBGP neighbor between R3 and R2") + step("Configure lPv4 and IPv6 Loopback interface on R3, R4 and R2") + step("Configure IPv4 and IPv6 IBGP neighbor between R4 and R3") + r0_local_as = topo["routers"]["r0"]["bgp"]["local_as"] + r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"] + r2_local_as = topo["routers"]["r2"]["bgp"]["local_as"] + r3_local_as = topo["routers"]["r3"]["bgp"]["local_as"] + r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"] + + input_dict = { + "r0": { + "bgp": { + "local_as": r0_local_as, + } + }, + "r1": { + "bgp": { + "local_as": r1_local_as, + } + }, + "r2": { + "bgp": { + "local_as": r2_local_as, + } + }, + "r3": { + "bgp": { + "local_as": 4000, + } + }, + "r4": { + "bgp": { + "local_as": 4000, + } + }, + } + result = modify_as_number(tgen, topo, input_dict) + try: + assert result is True + except AssertionError: + logger.info("Expected behaviour: {}".format(result)) + logger.info("BGP config is not created because of invalid ASNs") + step("After changing the BGP AS Path Verify the BGP Convergence") + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "Complete convergence is expeceted after changing the ASN os the routes ..! :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step(" Configure IPv4 and IPv6 static route on R3 next-hop on R4 interface") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed to configure the static routes ....! Failed \n Error: {}".format( + tc_name, result + ) + step("verify IPv4 and IPv6 static route are configured and up on R1") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input) + assert result is True, "Testcase {} : Route is not found in {} in FIB ......! Failed \n Error: {}".format( + tc_name, static_routes_input,result + ) + + step( + "Configure redistribute static and connected on R3 and R4 for IPv4 and IPv6 address family " + ) + redistribute_static = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert result is True, "Testcase {} : Failed to configure redistribute configuratin \n Error: {}".format(tc_name, result) + + step( + "After configuring redistribute command , verify static and connected routes ( loopback connected routes) are advertised on R2" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R3_NETWORK_LOOPBACK[addr_type]], + "next_hop": R3_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R3_NETWORK_CONNECTED[addr_type]], + "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : static & and connected routes are expected but not found in FIB .... ! \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : static & and connected routes are expected but not found in RIB .... ! \n Error: {}".format( + tc_name, result + ) + snapshot1 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3") + step( + "Configure Default originate on R3 for R3 to R2, on IPv4 and IPv6 BGP address family" + ) + local_as = get_dut_as_number(tgen, dut="r3") + default_originate_config = { + "r3": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": {"unicast": {"default_originate": {"r2": {}}}}, + "ipv6": {"unicast": {"default_originate": {"r2": {}}}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed to configure the default originate configuration \n Error: {}".format(tc_name, result) + + step( + "After configuring default-originate command , verify default routes are advertised on R2 on both BGP RIB and FIB" + ) + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + }, + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : static route from R1 {} and default route from R3 is expected in R2 FIB .....! NOT FOUND \n Error: {}".format( + tc_name, NETWORK1_1,result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : static route from R1 {} and default route from R3 is expected in R2 RIB .....! NOT FOUND \n Error: {}".format( + tc_name,NETWORK1_1, result + ) + + step( + "Verify default route for IPv4 and IPv6 present with path = ebgp as path, metric =0 " + ) + # local preference will bgp not applicable for eBGP + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + metric=0, + expected_aspath="4000", + ) + assert result is True, "Testcase {} : Default route from R3 is expected with attributes in R2 RIB .....! NOT FOUND Error: {}".format(tc_name, result) + + step( + "Taking the snapshot2 of the prefix count after configuring the default originate" + ) + snapshot2 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3") + step( + "Verify out-prefix count is incremented default route on IPv4 and IPv6 neighbor" + ) + isIPv4prefix_incremented = False + isIPv6prefix_incremented = False + if snapshot1["ipv4_count"] < snapshot2["ipv4_count"]: + isIPv4prefix_incremented = True + if snapshot1["ipv6_count"] < snapshot2["ipv6_count"]: + isIPv6prefix_incremented = True + + assert ( + isIPv4prefix_incremented is True + ), "Testcase {} : Failed Error: IPV4 Prefix is not incremented on receiveing ".format( + tc_name + ) + + assert ( + isIPv6prefix_incremented is True + ), "Testcase {} : Failed Error: IPV6 Prefix is not incremented on receiveing ".format( + tc_name + ) + write_test_footer(tc_name) + + +def test_verify_bgp_default_originate_in_IBGP_with_route_map_p0(request): + """ + test_verify_bgp_default_originate_in_IBGP_with_route_map_p0 + """ + tgen = get_topogen() + global BGP_CONVERGENCE + global topo + # test case name + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + step("Configure IPv4 and IPv6 , IBGP neighbor between R1 and R2") + step("Configure IPv4 and IPv6 , EBGP neighbor between R1 and R0") + r0_local_as = topo["routers"]["r0"]["bgp"]["local_as"] + r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"] + r2_local_as = topo["routers"]["r2"]["bgp"]["local_as"] + r3_local_as = topo["routers"]["r3"]["bgp"]["local_as"] + r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"] + + input_dict = { + "r0": { + "bgp": { + "local_as": r0_local_as, + } + }, + "r1": { + "bgp": { + "local_as": 1000, + } + }, + "r2": { + "bgp": { + "local_as": 1000, + } + }, + "r3": { + "bgp": { + "local_as": r3_local_as, + } + }, + "r4": { + "bgp": { + "local_as": r4_local_as, + } + }, + } + result = modify_as_number(tgen, topo, input_dict) + try: + assert result is True + except AssertionError: + logger.info("Expected behaviour: {}".format(result)) + logger.info("BGP config is not created because of invalid ASNs") + + step("After changing the BGP AS Path Verify the BGP Convergence") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "Complete convergence is expected after changing ASN ....! ERROR :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Configure 2 IPv4 and 2 IPv6 Static route on R0 with next-hop as Null0") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r0": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Static Configuration is Failed \n Error: {}".format( + tc_name, result + ) + + step("verify IPv4 and IPv6 static route are configured and up on R0") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r0": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r0", static_routes_input) + assert result is True, "Testcase {} : routes {} unable is not found in R0 FIB \n Error: {}".format( + tc_name, static_routes_input,result + ) + + step( + "Configure redistribute static on IPv4 and IPv6 address family on R0 for R0 to R1 neighbor " + ) + redistribute_static = { + "r0": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert result is True, "Testcase {} : Failed to configure redistribute static configuration....! \n Error: {}".format(tc_name, result) + + step("verify IPv4 and IPv6 static route are configured and up on R1") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r0": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input) + assert result is True, "Testcase {} : Failed... Routes {} expected in r0 FIB after configuring the redistribute config \n Error: {}".format( + tc_name,static_routes_input, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r1", static_routes_input) + assert result is True, "Testcase {} : Failed... Routes {} expected in r0 RIB after configuring the redistribute config \n Error: {}".format( + tc_name, static_routes_input,result + ) + + step( + "Configure IPv4 prefix-list Pv4 and and IPv6 prefix-list Pv6 on R1 to match BGP route Sv41, Sv42, IPv6 route Sv61 Sv62 permit " + ) + input_dict_3 = { + "r1": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv4"], + "action": "permit", + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv4"], + "action": "permit", + }, + ] + }, + "ipv6": { + "Pv6": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv6"], + "action": "permit", + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv6"], + "action": "permit", + }, + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed to configure the prefix list \n Error: {}".format(tc_name, result) + + step( + "Configure IPV4 and IPv6 route-map (RMv4 and RMv6 ) matching prefix-list (Pv4 and Pv6) respectively on R1" + ) + input_dict_3 = { + "r1": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed to configure the route map \n Error: {}".format(tc_name, result) + + step( + "Configure default-originate with route-map (RMv4 and RMv6) on R1, on BGP IPv4 and IPv6 address family " + ) + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv4"}}} + }, + "ipv6": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv6"}}} + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed to configure the default originate \n Error: {}".format(tc_name, result) + + step("Verify the default route is received in BGP RIB and FIB") + step( + "After configuring default-originate command , verify default routes are advertised on R2 " + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + } + ] + } + } + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed...! Expected default route from R1 not found in FIB \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed...! Expected default route from R1 not found in RIB \n Error: {}".format( + tc_name, result + ) + step("Remove route-map RMv4 and RMv6 from default-originate command in R1") + NOTE = """ Configuring the default-originate should remove the previously applied default originate with condtional route-map""" + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": {"unicast": {"default_originate": {"r2": {}}}}, + "ipv6": {"unicast": {"default_originate": {"r2": {}}}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed to remove the default originate conditional route-map \n Error: {}".format(tc_name, result) + + step( + "Verify BGP RIB and FIB After removing route-map , default route still present on R2" + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed Default route from R1 is not found in FIB \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed Default route from R1 is not found in RIB \n Error: {}".format( + tc_name, result + ) + + step("Configure default-originate with route-map (RMv4 and RMv6) on R1 ") + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "default_originate": { + "r2": { + "route_map": "RMv4", + } + } + } + }, + "ipv6": { + "unicast": { + "default_originate": { + "r2": { + "route_map": "RMv6", + } + } + } + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed to configure the Default originate route-map \n Error: {}".format(tc_name, result) + + step( + "After configuring default-originate command , verify default routes are advertised on R2 " + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed Default Route from R1 is not found in FIB \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed Default Route from R1 is not found in RIB \n Error: {}".format( + tc_name, result + ) + + step("Delete prefix list using no prefix-list") + input_dict_3 = { + "r1": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv4"], + "action": "permit", + "delete": True, + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv4"], + "action": "permit", + "delete": True, + }, + ] + }, + "ipv6": { + "Pv6": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv6"], + "action": "permit", + "delete": True, + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv6"], + "action": "permit", + "delete": True, + }, + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed to delete the prefix list Error: {}".format(tc_name, result) + + step( + "Verify BGP RIB and FIB After deleting prefix-list , verify IPv4 and IPv6 default route got removed from DUT " + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + } + ] + } + } + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed\n After deleteing prefix default route is not expected in FIB \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n After deleteing prefix default route is not expected in RIB \n Error: {}".format( + tc_name, result + ) + + step("Configure prefix-list and delete route-map using no route-map") + input_dict_3 = { + "r1": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv4"], + "action": "permit", + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv4"], + "action": "permit", + }, + ] + }, + "ipv6": { + "Pv6": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv6"], + "action": "permit", + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv6"], + "action": "permit", + }, + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed to configure the prefix lists Error: {}".format(tc_name, result) + + step( + "After configuring the Prefixlist cross checking the BGP Default route is configured again , before deleting the route map" + ) + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + } + ] + } + } + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + expected=True, + ) + assert result is True, "Testcase {} : Failed Default route from R1 is expected in FIB but not found \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + expected=True, + ) + assert result is True, "Testcase {} : Failed Default route from R1 is expected in RIB but not found \n Error: {}".format( + tc_name, result + ) + + step("Deleting the routemap") + input_dict = {"r1": {"route_maps": ["RMv4", "RMv6"]}} + result = delete_route_maps(tgen, input_dict) + assert result is True, "Testcase {} : Failed to delete the Route-map \n Error: {}".format(tc_name, result) + + step( + "Verify BGP RIB and FIB ,After deleting route-map , verify IPv4 and IPv6 default route got removed from DUT" + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n After deleteing route-map default route is not expected in FIB \nError: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n After deleteing route-map default route is not expected in RIB \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_default_originate_in_EBGP_with_route_map_p0(request): + """ + test_verify_bgp_default_originate_in_EBGP_with_route_map_p0 + """ + tgen = get_topogen() + global BGP_CONVERGENCE + global topo + # test case name + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + step("Configure IPv4 and IPv6 , EBGP neighbor between R3 and R2") + step("Configure IPv4 and IPv6 IBGP neighbor between R3 and R4") + r0_local_as = topo["routers"]["r0"]["bgp"]["local_as"] + r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"] + r2_local_as = topo["routers"]["r2"]["bgp"]["local_as"] + r3_local_as = topo["routers"]["r3"]["bgp"]["local_as"] + r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"] + + input_dict = { + "r0": { + "bgp": { + "local_as": r0_local_as, + } + }, + "r1": { + "bgp": { + "local_as": r1_local_as, + } + }, + "r2": { + "bgp": { + "local_as": r2_local_as, + } + }, + "r3": { + "bgp": { + "local_as": 4000, + } + }, + "r4": { + "bgp": { + "local_as": 4000, + } + }, + } + result = modify_as_number(tgen, topo, input_dict) + try: + assert result is True + except AssertionError: + logger.info("Expected behaviour: {}".format(result)) + logger.info("BGP config is not created because of invalid ASNs") + step("After changing the BGP AS Path Verify the BGP Convergence") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Configure 2 IPv4 and 2 IPv6, Static route on R4 with next-hop as Null0 IPv4 route Sv41, Sv42, IPv6 route Sv61 Sv62" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed to configure the static routes \n Error: {}".format( + tc_name, result + ) + step("verify IPv4 and IPv6 static route are configured and up on R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input) + assert result is True, "Testcase {} : Failed Static route {} is not found in R4 FIB \n Error: {}".format( + tc_name, static_routes_input,result + ) + + step( + "Configure redistribute static on IPv4 and IPv6 address family on R4 for R4 to R3 neighbo" + ) + redistribute_static = { + "r4": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert result is True, "Testcase {} : Failed to configure the redistribute static \n Error: {}".format(tc_name, result) + + step("verify IPv4 and IPv6 static route are configured and up on R3") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input) + assert result is True, "Testcase {} : Failed static routes from R1 and R3 is not found in FIB \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes_input) + assert result is True, "Testcase {} : Failed static routes from R1 and R3 is not found in RIB \n Error: {}".format( + tc_name, result + ) + + step( + "Configure IPv4 prefix-list Pv4 and and IPv6 prefix-list Pv6 on R3 so new route which is not present on R3" + ) + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + { + "seqid": "1", + "network": NETWORK3_1["ipv4"], + "action": "permit", + } + ] + }, + "ipv6": { + "Pv6": [ + { + "seqid": "1", + "network": NETWORK3_1["ipv6"], + "action": "permit", + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed to configure the prefix lists \n Error: {}".format(tc_name, result) + + step("verify IPv4 and IPv6 Prefix list got configured on R3") + input_dict = {"r3": {"prefix_lists": ["Pv4", "Pv6"]}} + result = verify_prefix_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed ..! configured prefix lists {} are not found \n Error: {}".format(tc_name,input_dict, result) + + step( + "Configure IPv4 and IPv6 route-map ( RMv4 and RMv6 ) matching prefix-list (Pv4 and Pv6 ) respectively on R3" + ) + input_dict_3 = { + "r3": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed to configure the route-map \n Error: {}".format(tc_name, result) + step( + "Taking the snapshot of the prefix count before configuring the default originate" + ) + snapshot1 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3") + step( + "Configure default-originate with IPv4 and IPv6 route-map (RMv4 and RMv6) on R3" + ) + local_as = get_dut_as_number(tgen, dut="r3") + default_originate_config = { + "r3": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv4"}}} + }, + "ipv6": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv6"}}} + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed to configure default-originate \n Error: {}".format(tc_name, result) + + step("Verify the default route is NOT received in BGP RIB and FIB on R2 ") + step( + "After configuring default-originate command , verify default routes are not Received on R2 " + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Default route is not expected due to deny in prefix list \nError: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \nDefault route is not expected due to deny in prefix list\n Error: {}".format( + tc_name, result + ) + + step("Add route Sv41, Sv42, IPv6 route Sv61 Sv62 on prefix list Pv4 and Pv6") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv4"], + "action": "permit", + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv4"], + "action": "permit", + }, + ] + }, + "ipv6": { + "Pv6": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv6"], + "action": "permit", + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv6"], + "action": "permit", + }, + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed to configure the prefix lists Error: {}".format(tc_name, result) + + step("Verify BGP default route for IPv4 and IPv6 is received on R2") + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed Default routes are expected in R2 FIB from R3 but not found ....! \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed Default routes are expected in R2 RIB from R3 but not found ....! \n Error: {}".format( + tc_name, result + ) + + step("Remove route Sv41, Sv42, IPv6 route Sv61 Sv62 on prefix list Pv4 and Pv6") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv4"], + "action": "permit", + "delete": True, + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv4"], + "action": "permit", + "delete": True, + }, + ] + }, + "ipv6": { + "Pv6": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv6"], + "action": "permit", + "delete": True, + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv6"], + "action": "permit", + "delete": True, + }, + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed to remove prefix-lists from R3 Error: {}".format(tc_name, result) + + step( + "After Removing route BGP default route for IPv4 and IPv6 is NOT received on R2" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n After Removing route in prefix list the default route is not expected in FIB \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n After Removing route in prefix list the default route is not expected in RIB\n Error: {}".format( + tc_name, result + ) + + step(" Add route Sv41, Sv42, IPv6 route Sv61 Sv62 on prefix list Pv4 and Pv6") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv4"], + "action": "permit", + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv4"], + "action": "permit", + }, + ] + }, + "ipv6": { + "Pv6": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv6"], + "action": "permit", + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv6"], + "action": "permit", + }, + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify BGP default route for IPv4 and IPv6 is received on R2") + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Change IPv4 and IPv6 prefix-list permit and deny ") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + {"seqid": "1", "network": NETWORK1_1["ipv4"], "action": "deny"}, + {"seqid": "2", "network": NETWORK2_1["ipv4"], "action": "deny"}, + ] + }, + "ipv6": { + "Pv6": [ + {"seqid": "1", "network": NETWORK1_1["ipv6"], "action": "deny"}, + {"seqid": "2", "network": NETWORK2_1["ipv6"], "action": "deny"}, + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify BGP default route for IPv4 and IPv6 is not received on R2") + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n after denying the prefix list default route is not expected in FIB \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n after denying the prefix list default route is not expected in RIB \n Error: {}".format( + tc_name, result + ) + + step("Change IPv4 and IPv6 prefix-list deny to permit ") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv4"], + "action": "permit", + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv4"], + "action": "permit", + }, + ] + }, + "ipv6": { + "Pv6": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv6"], + "action": "permit", + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv6"], + "action": "permit", + }, + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify BGP default route for IPv4 and IPv6 is received on R2") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Taking the snapshot2 of the prefix count after configuring the default originate" + ) + snapshot2 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3") + + step("verifying the prefix count incrementing or not ") + isIPv4prefix_incremented = False + isIPv6prefix_incremented = False + if snapshot1["ipv4_count"] < snapshot2["ipv4_count"]: + isIPv4prefix_incremented = True + if snapshot1["ipv6_count"] < snapshot2["ipv6_count"]: + isIPv6prefix_incremented = True + + assert ( + isIPv4prefix_incremented is True + ), "Testcase {} : Failed Error: IPV4 Prefix is not incremented on receiveing ".format( + tc_name + ) + + assert ( + isIPv6prefix_incremented is True + ), "Testcase {} : Failed Error: IPV6 Prefix is not incremented on receiveing ".format( + tc_name + ) + + step( + "Configure another IPv4 and IPv6 route-map and match same prefix-list (Sv41, Sv42, IPv6 route Sv61 Sv62) with deny statement " + ) + input_dict_3 = { + "r3": { + "route_maps": { + "RMv41": [ + { + "action": "deny", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + }, + ], + "RMv61": [ + { + "action": "deny", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Attach route-map on IPv4 and IP6 BGP neighbor on fly") + local_as = get_dut_as_number(tgen, dut="r3") + default_originate_config = { + "r3": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv41"}}} + }, + "ipv6": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv61"}}} + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "After attaching route-map verify IPv4 and IPv6 default route is withdrawn from the R2" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Change the recently added Routemap from deny to permit") + input_dict_3 = { + "r3": { + "route_maps": { + "RMv41": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + }, + ], + "RMv61": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify IPv4 and IPv6 default route is advertised from the R2") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Delete default-originate route-map command while configuring ( neighbor x.x.x default-originate) for IPv4 and IPv6 BGP neighbor " + ) + """ Configuring the Default originate on neighbor must remove the previously assigned deault-originate with routemap config """ + local_as = get_dut_as_number(tgen, dut="r3") + default_originate_config = { + "r3": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": {"unicast": {"default_originate": {"r2": {}}}}, + "ipv6": {"unicast": {"default_originate": {"r2": {}}}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify in running config from BGP that default-originate with route-map command is removed and default-originate command is still present and default route for IPv4 and IPv6 present in RIB and FIB" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure default-originate with conditional route-map command on IPv4 and IPv6 address family " + ) + local_as = get_dut_as_number(tgen, dut="r3") + default_originate_config = { + "r3": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv41"}}} + }, + "ipv6": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv61"}}} + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify in running config from BGP that default-originate with route-map command is present and default route for IPv4 and IPv6 present" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Delete default originate with 'no bgp default-originate' from IPV4 and IPV6 address family " + ) + local_as = get_dut_as_number(tgen, dut="r3") + default_originate_config = { + "r3": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": {"default_originate": {"r2": {"delete": True}}} + }, + "ipv6": { + "unicast": {"default_originate": {"r2": {"delete": True}}} + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + " Verify in running config from BGP that default-originate complete CLI is removed for IPV4 and IPV6 address family and default originate routes got deleted" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Default Route is not expected in FIB \nError: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Default Route is not expected in RIB\nError: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_2.py b/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_2.py new file mode 100644 index 0000000..59f833b --- /dev/null +++ b/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_2.py @@ -0,0 +1,2427 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Shreenidhi A R +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# +""" +Following tests are covered. +5. Verify BGP default originate route-map with OUT route-map +6. Verify BGP default originate route-map with IN route-map +8. Verify BGP default route after removing default-originate +9. Verify default-originate route with GR +""" +import os +import sys +import time +import pytest +from time import sleep +from copy import deepcopy +from lib.topolog import logger + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json +from lib.topolog import logger + +from lib.bgp import ( + verify_bgp_convergence, + verify_graceful_restart, + create_router_bgp, + verify_router_id, + modify_as_number, + verify_as_numbers, + clear_bgp_and_verify, + clear_bgp, + verify_bgp_rib, + get_prefix_count_route, + get_dut_as_number, + verify_rib_default_route, + verify_fib_default_route, + verify_bgp_advertised_routes_from_neighbor, + verify_bgp_received_routes_from_neighbor, +) +from lib.common_config import ( + interface_status, + verify_prefix_lists, + verify_fib_routes, + kill_router_daemons, + start_router_daemons, + shutdown_bringup_interface, + step, + required_linux_kernel_version, + stop_router, + start_router, + create_route_maps, + create_prefix_lists, + get_frr_ipv6_linklocal, + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + create_static_routes, + check_router_status, + delete_route_maps, +) + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers + +# Global variables +topo = None +KEEPALIVETIMER = 1 +HOLDDOWNTIMER = 3 +# Global variables +NETWORK1_1 = {"ipv4": "1.1.1.1/32", "ipv6": "1::1/128"} +NETWORK1_2 = {"ipv4": "1.1.1.2/32", "ipv6": "1::2/128"} +NETWORK2_1 = {"ipv4": "2.1.1.1/32", "ipv6": "2::1/128"} +NETWORK2_2 = {"ipv4": "2.1.1.2/32", "ipv6": "2::2/128"} +NETWORK3_1 = {"ipv4": "3.1.1.1/32", "ipv6": "3::1/128"} +NETWORK3_2 = {"ipv4": "3.1.1.2/32", "ipv6": "3::2/128"} +NETWORK4_1 = {"ipv4": "4.1.1.1/32", "ipv6": "4::1/128"} +NETWORK4_2 = {"ipv4": "4.1.1.2/32", "ipv6": "4::2/128"} +NETWORK5_1 = {"ipv4": "5.1.1.1/32", "ipv6": "5::1/128"} +NETWORK5_2 = {"ipv4": "5.1.1.2/32", "ipv6": "5::2/128"} +DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + +IPV4_RM = "RMVIPV4" +IPV6_RM = "RMVIPV6" + +IPV4_RM1 = "RMVIPV41" +IPV6_RM1 = "RMVIPV61" + +IPV4_RM2 = "RMVIPV42" +IPV6_RM2 = "RMVIPV62" + +IPV4_PL_1 = "PV41" +IPV4_PL_2 = "PV42" + +IPV6_PL_1 = "PV61" +IPV6_PL_2 = "PV62" + + +r1_ipv4_loopback = "1.0.1.0/24" +r2_ipv4_loopback = "1.0.2.0/24" +r3_ipv4_loopback = "1.0.3.0/24" +r4_ipv4_loopback = "1.0.4.0/24" +r1_ipv6_loopback = "2001:db8:f::1:0/120" +r2_ipv6_loopback = "2001:db8:f::2:0/120" +r3_ipv6_loopback = "2001:db8:f::3:0/120" +r4_ipv6_loopback = "2001:db8:f::4:0/120" + +r0_connected_address_ipv4 = "192.168.0.0/24" +r0_connected_address_ipv6 = "fd00::/64" +r1_connected_address_ipv4 = "192.168.1.0/24" +r1_connected_address_ipv6 = "fd00:0:0:1::/64" +r3_connected_address_ipv4 = "192.168.2.0/24" +r3_connected_address_ipv6 = "fd00:0:0:2::/64" +r4_connected_address_ipv4 = "192.168.3.0/24" +r4_connected_address_ipv6 = "fd00:0:0:3::/64" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_default_originate_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global ADDR_TYPES + global BGP_CONVERGENCE + global DEFAULT_ROUTES + global DEFAULT_ROUTE_NXT_HOP_R1, DEFAULT_ROUTE_NXT_HOP_R3 + global R0_NETWORK_LOOPBACK, R0_NETWORK_LOOPBACK_NXTHOP, R1_NETWORK_LOOPBACK, R1_NETWORK_LOOPBACK_NXTHOP + global R0_NETWORK_CONNECTED, R0_NETWORK_CONNECTED_NXTHOP, R1_NETWORK_CONNECTED, R1_NETWORK_CONNECTED_NXTHOP + global R4_NETWORK_LOOPBACK, R4_NETWORK_LOOPBACK_NXTHOP, R3_NETWORK_LOOPBACK, R3_NETWORK_LOOPBACK_NXTHOP + global R4_NETWORK_CONNECTED, R4_NETWORK_CONNECTED_NXTHOP, R3_NETWORK_CONNECTED, R3_NETWORK_CONNECTED_NXTHOP + + ADDR_TYPES = check_address_types() + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + # There are the global varibles used through out the file these are acheived only after building the topology. + + r0_loopback_address_ipv4 = topo["routers"]["r0"]["links"]["lo"]["ipv4"] + r0_loopback_address_ipv4_nxt_hop = topo["routers"]["r0"]["links"]["r1"][ + "ipv4" + ].split("/")[0] + r0_loopback_address_ipv6 = topo["routers"]["r0"]["links"]["lo"]["ipv6"] + r0_loopback_address_ipv6_nxt_hop = topo["routers"]["r0"]["links"]["r1"][ + "ipv6" + ].split("/")[0] + + r1_loopback_address_ipv4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"] + r1_loopback_address_ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"][ + "ipv4" + ].split("/")[0] + r1_loopback_address_ipv6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"] + r1_loopback_address_ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"][ + "ipv6" + ].split("/")[0] + + r4_loopback_address_ipv4 = topo["routers"]["r4"]["links"]["lo"]["ipv4"] + r4_loopback_address_ipv4_nxt_hop = topo["routers"]["r4"]["links"]["r3"][ + "ipv4" + ].split("/")[0] + r4_loopback_address_ipv6 = topo["routers"]["r4"]["links"]["lo"]["ipv6"] + r4_loopback_address_ipv6_nxt_hop = topo["routers"]["r4"]["links"]["r3"][ + "ipv6" + ].split("/")[0] + + r3_loopback_address_ipv4 = topo["routers"]["r3"]["links"]["lo"]["ipv4"] + r3_loopback_address_ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"][ + "ipv4" + ].split("/")[0] + r3_loopback_address_ipv6 = topo["routers"]["r3"]["links"]["lo"]["ipv6"] + r3_loopback_address_ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"][ + "ipv6" + ].split("/")[0] + + R0_NETWORK_LOOPBACK = { + "ipv4": r0_loopback_address_ipv4, + "ipv6": r0_loopback_address_ipv6, + } + R0_NETWORK_LOOPBACK_NXTHOP = { + "ipv4": r0_loopback_address_ipv4_nxt_hop, + "ipv6": r0_loopback_address_ipv6_nxt_hop, + } + + R1_NETWORK_LOOPBACK = { + "ipv4": r1_loopback_address_ipv4, + "ipv6": r1_loopback_address_ipv6, + } + R1_NETWORK_LOOPBACK_NXTHOP = { + "ipv4": r1_loopback_address_ipv4_nxt_hop, + "ipv6": r1_loopback_address_ipv6_nxt_hop, + } + + R0_NETWORK_CONNECTED = { + "ipv4": r0_connected_address_ipv4, + "ipv6": r0_connected_address_ipv6, + } + R0_NETWORK_CONNECTED_NXTHOP = { + "ipv4": r0_loopback_address_ipv4_nxt_hop, + "ipv6": r0_loopback_address_ipv6_nxt_hop, + } + + R1_NETWORK_CONNECTED = { + "ipv4": r1_connected_address_ipv4, + "ipv6": r1_connected_address_ipv6, + } + R1_NETWORK_CONNECTED_NXTHOP = { + "ipv4": r1_loopback_address_ipv4_nxt_hop, + "ipv6": r1_loopback_address_ipv6_nxt_hop, + } + + R4_NETWORK_LOOPBACK = { + "ipv4": r4_loopback_address_ipv4, + "ipv6": r4_loopback_address_ipv6, + } + R4_NETWORK_LOOPBACK_NXTHOP = { + "ipv4": r4_loopback_address_ipv4_nxt_hop, + "ipv6": r4_loopback_address_ipv6_nxt_hop, + } + + R3_NETWORK_LOOPBACK = { + "ipv4": r3_loopback_address_ipv4, + "ipv6": r3_loopback_address_ipv6, + } + R3_NETWORK_LOOPBACK_NXTHOP = { + "ipv4": r3_loopback_address_ipv4_nxt_hop, + "ipv6": r3_loopback_address_ipv6_nxt_hop, + } + + R4_NETWORK_CONNECTED = { + "ipv4": r4_connected_address_ipv4, + "ipv6": r4_connected_address_ipv6, + } + R4_NETWORK_CONNECTED_NXTHOP = { + "ipv4": r4_loopback_address_ipv4_nxt_hop, + "ipv6": r4_loopback_address_ipv6_nxt_hop, + } + + R3_NETWORK_CONNECTED = { + "ipv4": r3_connected_address_ipv4, + "ipv6": r3_connected_address_ipv6, + } + R3_NETWORK_CONNECTED_NXTHOP = { + "ipv4": r3_loopback_address_ipv4_nxt_hop, + "ipv6": r3_loopback_address_ipv6_nxt_hop, + } + + # populating the nexthop for default routes + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + interface = topo["routers"]["r1"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface) + ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0] + ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0] + DEFAULT_ROUTE_NXT_HOP_R1 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + + interface = topo["routers"]["r3"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface) + ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0] + ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv6"].split("/")[0] + DEFAULT_ROUTE_NXT_HOP_R3 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Local API's +# +##################################################### + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + This function groups the repetitive function calls into one function. + """ + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + return True + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_verify_bgp_default_originate_route_map_in_OUT_p1(request): + """ + test_verify_bgp_default_originate_route_map_in_OUT_p1 + """ + tgen = get_topogen() + global BGP_CONVERGENCE + global topo + # test case name + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + step("Configure IPv4 and IPv6 , EBGP neighbor between R3 and R2") + step("Configure IPv4 and IPv6 IBGP neighbor between R3 and R4") + r0_local_as = topo["routers"]["r0"]["bgp"]["local_as"] + r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"] + r2_local_as = topo["routers"]["r2"]["bgp"]["local_as"] + r3_local_as = topo["routers"]["r3"]["bgp"]["local_as"] + r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"] + input_dict = { + "r0": { + "bgp": { + "local_as": r0_local_as, + } + }, + "r1": { + "bgp": { + "local_as": r1_local_as, + } + }, + "r2": { + "bgp": { + "local_as": r2_local_as, + } + }, + "r3": { + "bgp": { + "local_as": 4000, + } + }, + "r4": { + "bgp": { + "local_as": 4000, + } + }, + } + result = modify_as_number(tgen, topo, input_dict) + try: + assert result is True + except AssertionError: + logger.info("Expected behaviour: {}".format(result)) + logger.info("BGP config is not created because of invalid ASNs") + step("After changing the BGP AS Path Verify the BGP Convergence") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Configure 2 IPv4 and 2 IPv6, Static route on R4 with next-hop as Null0 IPv4 route Sv41, Sv42, IPv6 route Sv61 Sv62" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("verify IPv4 and IPv6 static route are configured and up on R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure redistribute static knob on R4 , for R4 to R3 neighbor ") + redistribute_static = { + "r4": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + expected_routes = { + "ipv4": [ + {"network": NETWORK1_1["ipv4"], "nexthop": NEXT_HOP_IP["ipv4"]}, + {"network": NETWORK2_1["ipv4"], "nexthop": NEXT_HOP_IP["ipv4"]}, + ], + "ipv6": [ + {"network": NETWORK1_1["ipv6"], "nexthop": NEXT_HOP_IP["ipv4"]}, + {"network": NETWORK2_1["ipv6"], "nexthop": NEXT_HOP_IP["ipv4"]}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r4", peer="r3", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "After redistribute static verify the routes is recevied in router R3 in RIB and FIB" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure IPv4 prefix-list Pv4 and and IPv6 prefix-list Pv6 on R3 to match BGP route Sv41, IPv6 route Sv61 with permit option " + ) + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv4"], + "action": "permit", + } + ] + }, + "ipv6": { + "Pv6": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv6"], + "action": "permit", + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify IPv4 and IPv6 Prefix list got configured on R3") + input_dict = {"r3": {"prefix_lists": ["Pv4", "Pv6"]}} + result = verify_prefix_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure IPv4 and IPv6 route-map RMv4 and RMv6 matching prefix-list Pv4 and Pv6 with permit option " + ) + input_dict_3 = { + "r3": { + "route_maps": { + "RM4": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + }, + ], + "RM6": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure IPv4 prefix-list Pv42 and and IPv6 prefix-list Pv62 on R3 to match BGP route Sv42, IPv6 route Sv62 with deny option" + ) + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "Pv42": [ + {"seqid": "1", "network": NETWORK2_1["ipv4"], "action": "deny"} + ] + }, + "ipv6": { + "Pv62": [ + {"seqid": "1", "network": NETWORK2_1["ipv6"], "action": "deny"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify IPv4 and IPv6 Prefix list got configured on R3") + input_dict = {"r3": {"prefix_lists": ["Pv42", "Pv62"]}} + result = verify_prefix_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure IPv4 and IPv6 route-map (RMv42 and RMv62 )matching prefix-list Pv42 and Pv62 with permit option " + ) + input_dict_3 = { + "r3": { + "route_maps": { + "RMv42": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv42"}}, + }, + ], + "RMv62": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv62"}}, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Apply IPv4 and IPv6 route-map RMv4 and RMv6 with default-originate on R3 , for R3 to R2 peers and Apply IPv4 and IPv6 out route-map RMv42 and RMv62 on R3 , for R3 to R2 peers " + ) + local_as = get_dut_as_number(tgen, "r3") + default_originate_config = { + "r3": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": {"default_originate": {"r2": {"route_map": "RM4"}}} + }, + "ipv6": { + "unicast": {"default_originate": {"r2": {"route_map": "RM6"}}} + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + updated_topo = topo + updated_topo["routers"]["r0"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r0") + updated_topo["routers"]["r1"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r1") + updated_topo["routers"]["r2"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r2") + updated_topo["routers"]["r3"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r3") + updated_topo["routers"]["r4"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r4") + + step( + "Apply IPv4 and IPv6 route-map RMv42 and RMv62 on R3 (OUT Direction), for R3 to R2 peers " + ) + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "route_maps": [ + {"name": "RMv42", "direction": "out"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "route_maps": [ + {"name": "RMv62", "direction": "out"} + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, updated_topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + NOTE = """ + After applying route-map on neighbor verify default BGP route IPv4 IPv6 route populated in R2 BGP and routing table , verify using "show ip bgp json" "show ipv6 bgp json" "show ip route json" "show ip route json" + Sv42 and Sv62 route should not be present on R2 + """ + step(NOTE) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + + result = verify_fib_routes( + tgen, addr_type, "r2", static_routes_input, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Static routes are not expected due to conditions \nError: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, addr_type, "r2", static_routes_input, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Static routes are not expected due to conditions\n Error: {}".format( + tc_name, result + ) + + step("Change IPv4 prefix-list Pv42 and and IPv6 prefix-list Pv62 deny to permit") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "Pv42": [ + { + "seqid": "1", + "network": NETWORK2_1["ipv4"], + "action": "permit", + } + ] + }, + "ipv6": { + "Pv62": [ + { + "seqid": "1", + "network": NETWORK2_1["ipv6"], + "action": "permit", + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify IPv4 and IPv6 Prefix list got configured on R3") + input_dict = {"r3": {"prefix_lists": ["Pv42", "Pv62"]}} + result = verify_prefix_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + NOTE = """Default BGP route and IPv4 ( Sv42) , IPv6 (Sv62) route populated in R2 BGP and routing table""" + step(NOTE) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("IPv4 prefix-list Pv4 and and IPv6 prefix-list Pv6 permit to deny ") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + {"seqid": "1", "network": NETWORK1_1["ipv4"], "action": "deny"} + ] + }, + "ipv6": { + "Pv6": [ + {"seqid": "1", "network": NETWORK1_1["ipv6"], "action": "deny"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + NOTE = """ + Verify default-originate route (IPv4 and IPv6 ) not present on R2 + IPv4 ( Sv42) , IPv6 (Sv62) route populated in R2 BGP + """ + step(NOTE) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + } + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n default-route in FIB is not expected due to conditions \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n default-route in RIB is not expected due to conditions \n Error: {}".format( + tc_name, result + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + +def test_verify_bgp_default_originate_route_map_in_IN_p1(request): + """Verify BGP default originate route-map with IN route-map""" + tgen = get_topogen() + global BGP_CONVERGENCE + global topo + # test case name + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + step("Configure IPv4 and IPv6 , EBGP neighbor between R1 and R2") + step("Configure IPv4 and IPv6 , IBGP neighbor between R1 and R0") + r0_local_as = topo["routers"]["r0"]["bgp"]["local_as"] + r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"] + r2_local_as = topo["routers"]["r2"]["bgp"]["local_as"] + r3_local_as = topo["routers"]["r3"]["bgp"]["local_as"] + r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"] + input_dict = { + "r0": { + "bgp": { + "local_as": 1000, + } + }, + "r1": { + "bgp": { + "local_as": 1000, + } + }, + "r2": { + "bgp": { + "local_as": r2_local_as, + } + }, + "r3": { + "bgp": { + "local_as": r3_local_as, + } + }, + "r4": { + "bgp": { + "local_as": r4_local_as, + } + }, + } + result = modify_as_number(tgen, topo, input_dict) + try: + assert result is True + except AssertionError: + logger.info("Expected behaviour: {}".format(result)) + logger.info("BGP config is not created because of invalid ASNs") + step("After changing the BGP AS Path Verify the BGP Convergence") + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Configure 2 IPv4 and 2 IPv6, Static route on R0 with next-hop as Null0 IPv4 route Sv41, Sv42, IPv6 route Sv61 Sv62" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r0": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("verifyIPv4 and IPv6 static routes are configure and up on R0 ") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r0": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r0", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure redistribute static knob on R0 , for R0 to R1 IPv4 and IPv6 neighbor" + ) + redistribute_static = { + "r0": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify IPv4 and IPv6 route received on R1 ") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r1", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure IPv4 prefix-list Pv4 and and IPv6 prefix-list Pv6 on R1 to match BGP route Sv41, Sv42, IPv6 route Sv61 Sv62" + ) + input_dict_3 = { + "r1": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv4"], + "action": "permit", + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv4"], + "action": "permit", + }, + ] + }, + "ipv6": { + "Pv6": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv6"], + "action": "permit", + }, + { + "seqid": "2", + "network": NETWORK2_1["ipv6"], + "action": "permit", + }, + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify IPv4 and IPv6 Prefix list got configured on R1") + input_dict = {"r1": {"prefix_lists": ["Pv4", "Pv6"]}} + result = verify_prefix_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure IPv4 and IPv6 route-map RMv4 and RMv6 matching prefix-list Pv4 and Pv6 with deny option on R1" + ) + input_dict_3 = { + "r1": { + "route_maps": { + "RMv4": [ + { + "action": "deny", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + }, + ], + "RMv6": [ + { + "action": "deny", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Apply route-map IN direction in R1 (R1 to R0) IPv4 and IPv6 neighbor") + updated_topo = topo + updated_topo["routers"]["r0"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r0") + updated_topo["routers"]["r1"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r1") + updated_topo["routers"]["r2"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r2") + updated_topo["routers"]["r3"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r3") + updated_topo["routers"]["r4"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r4") + + local_as_r1 = get_dut_as_number(tgen, dut="r1") + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r0": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "RMv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r0": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "RMv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, updated_topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + STEP = "After applying route-map verify that IPv4 route Sv41, Sv42, IPv6 route Sv61 Sv62 should not present on R1 BGP and routing table " + step(STEP) + + step( + "After applying route-map verify that IPv4 route Sv41, Sv42, IPv6 route Sv61 Sv62 should not present on R1 " + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + + result = verify_fib_routes( + tgen, addr_type, "r1", static_routes_input, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n default-route in FIB is not expected due to conditions \nError: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, addr_type, "r1", static_routes_input, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n default-route in FIB is not expected due to conditions \nError: {}".format( + tc_name, result + ) + # Routes should come to dut but not the shown in RIB thus verifying using show ip bgp nbr xxx received route + step( + " Verify the received routes \n using 'show ip bgp nbr xxx received route' in Router R1" + ) + expected_routes = { + "ipv4": [ + {"network": NETWORK1_1["ipv4"], "nexthop": NEXT_HOP_IP["ipv4"]}, + {"network": NETWORK2_1["ipv4"], "nexthop": NEXT_HOP_IP["ipv4"]}, + ], + "ipv6": [ + {"network": NETWORK1_1["ipv6"], "nexthop": NEXT_HOP_IP["ipv6"]}, + {"network": NETWORK2_1["ipv6"], "nexthop": NEXT_HOP_IP["ipv6"]}, + ], + } + result = verify_bgp_received_routes_from_neighbor( + tgen, topo, dut="r1", peer="r0", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure default-originate on R1 for R1 to R2 IPv4 and IPv6 neighbor ") + local_as_r1 = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as_r1, + "address_family": { + "ipv4": {"unicast": {"default_originate": {"r2": {}}}}, + "ipv6": {"unicast": {"default_originate": {"r2": {}}}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify Default originate knob is configured and default route advertised to R2 , verify on R1 " + ) + expected_routes = { + "ipv4": [ + {"network": "0.0.0.0/0", "nexthop": ""}, + ], + "ipv6": [ + {"network": "::/0", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r1", peer="r2", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify the Default route Route in FIB in R2") + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + } + ] + } + } + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Change route-map RMv4 and RMv6 from deny to permit") + input_dict_3 = { + "r1": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + NOTE = """After changing route-map to permit verify that IPv4 routes Sv41, Sv42, IPv6 routes Sv61 Sv62 present on R1 BGP and routing table , using "show ip route " "show ip bgp nbr xxx received route " "show ipv6 route " "show ipv6 bgp nbr xxx receied route """ + step(NOTE) + expected_routes = { + "ipv4": [{"network": NETWORK1_1["ipv4"], "nexthop": NEXT_HOP_IP["ipv4"]}], + "ipv6": [{"network": NETWORK1_1["ipv6"], "nexthop": NEXT_HOP_IP["ipv4"]}], + } + result = verify_bgp_received_routes_from_neighbor( + tgen, topo, dut="r1", peer="r0", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = verify_bgp_rib(tgen, addr_type, "r1", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure default static route (IPv4 and IPv6) on R2 nexthop as R1 ") + NEXT_HOP_IP_R1 = {} + r1_r2_ipv4_neighbor = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0] + r1_r2_ipv6_neighbor = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0] + NEXT_HOP_IP_R1["ipv4"] = r1_r2_ipv4_neighbor + NEXT_HOP_IP_R1["ipv6"] = r1_r2_ipv6_neighbor + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": "0.0.0.0/0", + "next_hop": NEXT_HOP_IP_R1["ipv4"], + }, + { + "network": "0::0/0", + "next_hop": NEXT_HOP_IP_R1["ipv6"], + }, + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify Static default route is taking preference over BGP default routes , BGP default route is inactive IN RIB and static is up and installed in RIB and FIB " + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0] + ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0] + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_nxt_hop} + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP[addr_type], + "protocol": "static", + } + ] + } + } + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + write_test_footer(tc_name) + +def test_verify_default_originate_after_removing_default_originate_p1(request): + """Verify BGP default route after removing default-originate""" + + tgen = get_topogen() + global BGP_CONVERGENCE + global topo + # test case name + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + step("Configure EBGP between R0 to R1 and IBGP between R1 to R2") + step("Configure EBGP between R2 to R3 and IBGP between R3 to R4") + r0_local_as = topo["routers"]["r0"]["bgp"]["local_as"] + r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"] + r2_local_as = topo["routers"]["r2"]["bgp"]["local_as"] + r3_local_as = topo["routers"]["r3"]["bgp"]["local_as"] + r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"] + input_dict = { + "r0": { + "bgp": { + "local_as": r0_local_as, + } + }, + "r1": { + "bgp": { + "local_as": 2000, + } + }, + "r2": { + "bgp": { + "local_as": 2000, + } + }, + "r3": { + "bgp": { + "local_as": 5000, + } + }, + "r4": { + "bgp": { + "local_as": 5000, + } + }, + } + result = modify_as_number(tgen, topo, input_dict) + try: + assert result is True + except AssertionError: + logger.info("Expected behaviour: {}".format(result)) + logger.info("BGP config is not created because of invalid ASNs") + step("After changing the BGP AS Path Verify the BGP Convergence") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Configure IPv4 and IPv6 static route on R0 and R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r0": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + }, + "r4": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + }, + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("verify IPv4 and IPv6 static route are configured and up on R0 and R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r0": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r0", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Configure redistribute connected and static on R0 (R0-R1) on R4 ( R4-R3) IPv4 and IPv6 address family " + ) + redistribute_static = { + "r0": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + }, + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify IPv4 and IPv6 static route are configured and up on R1 and R3") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_LOOPBACK[addr_type]], + "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_CONNECTED[addr_type]], + "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("verify IPv4 and IPv6 static route are configured and up on R1 and R3") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R3_NETWORK_LOOPBACK[addr_type]], + "next_hop": R3_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R3_NETWORK_CONNECTED[addr_type]], + "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure default-originate on R1 for R1 to R2 neighbor for IPv4 and IPv6 peer " + ) + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": {"unicast": {"default_originate": {"r2": {}}}}, + "ipv6": {"unicast": {"default_originate": {"r2": {}}}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "Verify all the static , connected and loopback routes from R0,R1,R3 and R4 is receieved on R2 " + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_LOOPBACK[addr_type]], + "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_CONNECTED[addr_type]], + "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R3_NETWORK_LOOPBACK[addr_type]], + "next_hop": R3_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R3_NETWORK_CONNECTED[addr_type]], + "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify the Default Originate on R2 nexthop as R1") + + interface = topo["routers"]["r1"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface) + ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0] + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + expected=True, + ) + assert ( + result is True + ), "Testcase {} : Failed \n Error: After Deactivating the BGP neighbor the default route is expected but found in RIB -> {}".format( + tc_name, result + ) + + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + expected=True, + ) + assert ( + result is True + ), "Testcase {} : Failed \n Error: After Deactivating the BGP neighbor the default route is expected but found in FIB -> {}".format( + tc_name, result + ) + + step( + "Configure default-originate on R3 for R3 to R2 neighbor for IPv4 and IPv6 peer " + ) + local_as = get_dut_as_number(tgen, dut="r3") + default_originate_config = { + "r3": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": {"unicast": {"default_originate": {"r2": {}}}}, + "ipv6": {"unicast": {"default_originate": {"r2": {}}}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + STEP = """After configuring the Default Originate From R3 --> R2 + Both Default routes from R1 and R3 Should present in R2 BGP RIB + The Deafult Route from iBGP is prefferedover EBGP thus + Default Route From R1->r2 should only present in R2 FIB """ + step(STEP) + + interface = topo["routers"]["r3"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface) + ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0] + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: Only IBGP default originate is expected in FIB over EBGP {}".format( + tc_name, result + ) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "No change on static and connected routes which got advertised from R0, R1, R3 and R4" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_LOOPBACK[addr_type]], + "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_CONNECTED[addr_type]], + "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R3_NETWORK_LOOPBACK[addr_type]], + "next_hop": R3_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R3_NETWORK_CONNECTED[addr_type]], + "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + " Remove default-originate on R1 for R1 to R2 neighbor for IPv4 and IPv6 peer " + ) + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": {"default_originate": {"r2": {"delete": True}}} + }, + "ipv6": { + "unicast": {"default_originate": {"r2": {"delete": True}}} + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify the Default Originate reoute from R1 to r2 is removed in R2 ") + interface = topo["routers"]["r1"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface) + ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0] + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n After removing the default originate the route should not be present in FIB \n Error: {}".format( + tc_name, result + ) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n After removing the default originate the route should not be present in RIB \n Error: {}".format( + tc_name, result + ) + + NOTE = """ after removing the Default originate from R1-->R2 + Verify the BGP Default route received from R3 is present in both BGP RIB and FIB on R2 + """ + interface = topo["routers"]["r3"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface) + ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0] + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "No change on static and connected routes which got advertised from R0, R1, R3 and R4" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_LOOPBACK[addr_type]], + "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_CONNECTED[addr_type]], + "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R3_NETWORK_LOOPBACK[addr_type]], + "next_hop": R3_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R3_NETWORK_CONNECTED[addr_type]], + "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Remove default-originate on R3 for R3 to R2 neighbor for IPv4 and IPv6 peer " + ) + local_as = get_dut_as_number(tgen, dut="r3") + default_originate_config = { + "r3": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": {"default_originate": {"r2": {"delete": True}}} + }, + "ipv6": { + "unicast": {"default_originate": {"r2": {"delete": True}}} + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "After removing default originate , verify default IPv4 and IPv6 BGP routes removed on R2 from R1 ( next-hop as R3) " + ) + interface = topo["routers"]["r3"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface) + ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0] + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n After removing the default originate the route should not be present in FIB \n Error: {}".format( + tc_name, result + ) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n After removing the default originate the route should not be present in RIB \n Error: {}".format( + tc_name, result + ) + step( + "No change on static and connected routes which got advertised from R0, R1, R3 and R4" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_LOOPBACK[addr_type]], + "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_CONNECTED[addr_type]], + "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R3_NETWORK_LOOPBACK[addr_type]], + "next_hop": R3_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R3_NETWORK_CONNECTED[addr_type]], + "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + write_test_footer(tc_name) + +def test_verify_default_originate_route_with_GR_p1(request): + """ "Verify default-originate route with GR " + """ + tgen = get_topogen() + global BGP_CONVERGENCE + global topo + # test case name + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + + step("Configure IPV4 and IPV6 IBGP between R1 and R2 ") + step("Configure IPV4 and IPV6 EBGP between R2 to R3 ") + r0_local_as = topo['routers']['r0']['bgp']['local_as'] + r1_local_as = topo['routers']['r1']['bgp']['local_as'] + r2_local_as = topo['routers']['r2']['bgp']['local_as'] + r3_local_as = topo['routers']['r3']['bgp']['local_as'] + r4_local_as = topo['routers']['r4']['bgp']['local_as'] + input_dict = { + "r0": { + "bgp": { + "local_as": r0_local_as, + } + }, + "r1": { + "bgp": { + "local_as": 1000, + } + }, + "r2": { + "bgp": { + "local_as": 1000, + } + }, + "r3": { + "bgp": { + "local_as": r3_local_as, + } + }, + "r4": { + "bgp": { + "local_as": r4_local_as, + } + }, + } + result = modify_as_number(tgen, topo, input_dict) + try: + assert result is True + except AssertionError: + logger.info("Expected behaviour: {}".format(result)) + logger.info("BGP config is not created because of invalid ASNs") + step("After changing the BGP AS Path Verify the BGP Convergence") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Configure per peer Graceful restart on R2 ( restarting router) and R3 helper router " + ) + input_dict = { + "r2": { + "bgp": { + "local_as": get_dut_as_number(tgen, "r2"), + "graceful-restart": { + "graceful-restart": True, + "preserve-fw-state": True, + }, + } + }, + "r3": { + "bgp": { + "local_as": get_dut_as_number(tgen, "r3"), + "graceful-restart": {"graceful-restart-helper": True}, + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r2", peer="r3") + + step("verify Graceful restart at R2") + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r3" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step( + "Configure default-originate on R1 for R1-R2 neighbor for IPv4 and IPv6 BGP peers " + ) + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "default_originate":{ + "r2":{ + + } + + } + + } + }, "ipv6": { + "unicast": { + "default_originate":{ + "r2":{ + + } + + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step( + "R2 received default-originate routes and advertised it to R3 , verify on R2 and R3" + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + step( + "After configuring default-originate command , verify default routes are advertised on R2 " + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + } + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input,next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type]) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input,next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type]) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + + step(" Kill BGPd session on R2") + kill_router_daemons(tgen, "r2", ["bgpd"]) + start_router_daemons(tgen, "r2", ["bgpd"]) + + step("verify default route is relearned after clear bgp on R2 on BGP RIB and") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + } + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input,next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type]) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input,next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type]) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + write_test_footer(tc_name) + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_3.py b/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_3.py new file mode 100644 index 0000000..eca41e3 --- /dev/null +++ b/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_3.py @@ -0,0 +1,2528 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Shreenidhi A R +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# +""" +Following tests are covered. +10. Verify default-originate route after BGP and FRR process restart +11. Verify default-originate route after shut/no shut and clear BGP neighbor +""" +import os +import sys +import time +import pytest +from lib.topolog import logger + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json +from lib.topolog import logger + +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + modify_as_number, + clear_bgp, + verify_bgp_rib, + get_dut_as_number, + verify_rib_default_route, + verify_fib_default_route, +) +from lib.common_config import ( + interface_status, + verify_prefix_lists, + verify_fib_routes, + kill_router_daemons, + start_router_daemons, + shutdown_bringup_interface, + step, + required_linux_kernel_version, + stop_router, + start_router, + create_route_maps, + create_prefix_lists, + get_frr_ipv6_linklocal, + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + create_static_routes, + check_router_status, +) + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers + +# Global variables +topo = None +KEEPALIVETIMER = 1 +HOLDDOWNTIMER = 3 +# Global variables +NETWORK1_1 = {"ipv4": "198.51.1.1/32", "ipv6": "2001:DB8::1:1/128"} +NETWORK2_1 = {"ipv4": "198.51.1.2/32", "ipv6": "2001:DB8::1:2/128"} +DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + +r0_connected_address_ipv4 = "192.168.0.0/24" +r0_connected_address_ipv6 = "fd00::/64" +r1_connected_address_ipv4 = "192.168.1.0/24" +r1_connected_address_ipv6 = "fd00:0:0:1::/64" +r3_connected_address_ipv4 = "192.168.2.0/24" +r3_connected_address_ipv6 = "fd00:0:0:2::/64" +r4_connected_address_ipv4 = "192.168.3.0/24" +r4_connected_address_ipv6 = "fd00:0:0:3::/64" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_default_originate_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls micronet initialization functions. + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global ADDR_TYPES + global BGP_CONVERGENCE + global DEFAULT_ROUTES + global DEFAULT_ROUTE_NXT_HOP_R1, DEFAULT_ROUTE_NXT_HOP_R3 + global R0_NETWORK_LOOPBACK, R0_NETWORK_LOOPBACK_NXTHOP, R1_NETWORK_LOOPBACK + global R0_NETWORK_CONNECTED, R0_NETWORK_CONNECTED_NXTHOP, R1_NETWORK_CONNECTED, R1_NETWORK_CONNECTED_NXTHOP + global R4_NETWORK_LOOPBACK, R4_NETWORK_LOOPBACK_NXTHOP, R3_NETWORK_LOOPBACK + global R4_NETWORK_CONNECTED, R4_NETWORK_CONNECTED_NXTHOP, R3_NETWORK_CONNECTED, R3_NETWORK_CONNECTED_NXTHOP + + ADDR_TYPES = check_address_types() + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + # There are the global varibles used through out the file these are acheived only after building the topology. + + r0_loopback_address_ipv4 = topo["routers"]["r0"]["links"]["lo"]["ipv4"] + r0_loopback_address_ipv4_nxt_hop = topo["routers"]["r0"]["links"]["r1"][ + "ipv4" + ].split("/")[0] + r0_loopback_address_ipv6 = topo["routers"]["r0"]["links"]["lo"]["ipv6"] + r0_loopback_address_ipv6_nxt_hop = topo["routers"]["r0"]["links"]["r1"][ + "ipv6" + ].split("/")[0] + + r1_loopback_address_ipv4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"] + r1_loopback_address_ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"][ + "ipv4" + ].split("/")[0] + r1_loopback_address_ipv6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"] + r1_loopback_address_ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"][ + "ipv6" + ].split("/")[0] + + r4_loopback_address_ipv4 = topo["routers"]["r4"]["links"]["lo"]["ipv4"] + r4_loopback_address_ipv4_nxt_hop = topo["routers"]["r4"]["links"]["r3"][ + "ipv4" + ].split("/")[0] + r4_loopback_address_ipv6 = topo["routers"]["r4"]["links"]["lo"]["ipv6"] + r4_loopback_address_ipv6_nxt_hop = topo["routers"]["r4"]["links"]["r3"][ + "ipv6" + ].split("/")[0] + + r3_loopback_address_ipv4 = topo["routers"]["r3"]["links"]["lo"]["ipv4"] + r3_loopback_address_ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"][ + "ipv4" + ].split("/")[0] + r3_loopback_address_ipv6 = topo["routers"]["r3"]["links"]["lo"]["ipv6"] + r3_loopback_address_ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"][ + "ipv6" + ].split("/")[0] + + R0_NETWORK_LOOPBACK = { + "ipv4": r0_loopback_address_ipv4, + "ipv6": r0_loopback_address_ipv6, + } + R0_NETWORK_LOOPBACK_NXTHOP = { + "ipv4": r0_loopback_address_ipv4_nxt_hop, + "ipv6": r0_loopback_address_ipv6_nxt_hop, + } + + R1_NETWORK_LOOPBACK = { + "ipv4": r1_loopback_address_ipv4, + "ipv6": r1_loopback_address_ipv6, + } + + R0_NETWORK_CONNECTED = { + "ipv4": r0_connected_address_ipv4, + "ipv6": r0_connected_address_ipv6, + } + R0_NETWORK_CONNECTED_NXTHOP = { + "ipv4": r0_loopback_address_ipv4_nxt_hop, + "ipv6": r0_loopback_address_ipv6_nxt_hop, + } + + R1_NETWORK_CONNECTED = { + "ipv4": r1_connected_address_ipv4, + "ipv6": r1_connected_address_ipv6, + } + R1_NETWORK_CONNECTED_NXTHOP = { + "ipv4": r1_loopback_address_ipv4_nxt_hop, + "ipv6": r1_loopback_address_ipv6_nxt_hop, + } + + R4_NETWORK_LOOPBACK = { + "ipv4": r4_loopback_address_ipv4, + "ipv6": r4_loopback_address_ipv6, + } + R4_NETWORK_LOOPBACK_NXTHOP = { + "ipv4": r4_loopback_address_ipv4_nxt_hop, + "ipv6": r4_loopback_address_ipv6_nxt_hop, + } + + R3_NETWORK_LOOPBACK = { + "ipv4": r3_loopback_address_ipv4, + "ipv6": r3_loopback_address_ipv6, + } + R4_NETWORK_CONNECTED = { + "ipv4": r4_connected_address_ipv4, + "ipv6": r4_connected_address_ipv6, + } + R4_NETWORK_CONNECTED_NXTHOP = { + "ipv4": r4_loopback_address_ipv4_nxt_hop, + "ipv6": r4_loopback_address_ipv6_nxt_hop, + } + + R3_NETWORK_CONNECTED = { + "ipv4": r3_connected_address_ipv4, + "ipv6": r3_connected_address_ipv6, + } + R3_NETWORK_CONNECTED_NXTHOP = { + "ipv4": r3_loopback_address_ipv4_nxt_hop, + "ipv6": r3_loopback_address_ipv6_nxt_hop, + } + + # populating the nexthop for default routes + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + interface = topo["routers"]["r1"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface) + ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0] + ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0] + DEFAULT_ROUTE_NXT_HOP_R1 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + + interface = topo["routers"]["r3"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface) + ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0] + ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv6"].split("/")[0] + DEFAULT_ROUTE_NXT_HOP_R3 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_verify_default_originate_after_BGP_and_FRR_restart_p2(request): + """ + Summary: "Verify default-originate route after BGP and FRR process restart " + """ + tgen = get_topogen() + global BGP_CONVERGENCE + global topo + # test case name + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + step("Configure EBGP between R0 to R1 and IBGP between R1 to R2") + step("Configure EBGP between R2 to R3 and IBGP between R3 to R4") + input_dict = { + "r0": { + "bgp": { + "local_as": 999, + } + }, + "r1": { + "bgp": { + "local_as": 1000, + } + }, + "r2": { + "bgp": { + "local_as": 1000, + } + }, + "r3": { + "bgp": { + "local_as": 4000, + } + }, + "r4": { + "bgp": { + "local_as": 4000, + } + }, + } + result = modify_as_number(tgen, topo, input_dict) + try: + assert result is True + except AssertionError: + logger.info("Expected behaviour: {}".format(result)) + logger.info("BGP config is not created because of invalid ASNs") + step("After changing the BGP AS Path Verify the BGP Convergence") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert ( + BGP_CONVERGENCE is True + ), " Failed convergence after chaning the AS number :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Configure IPv4 and IPv6 static route (Sv4 , Sv6) on R0 and (S1v4, S1v6)on R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r0": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert ( + result is True + ), "Testcase {} : Failed to configure the static route on R0 \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert ( + result is True + ), "Testcase {} : Failed to configure the static route on R4 \n Error: {}".format( + tc_name, result + ) + + step("verify IPv4 and IPv6 static route are configured and up on R0") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r0": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r0", static_routes_input) + assert ( + result is True + ), "Testcase {} : Failed Route {} not found in R0 FIB \n Error: {}".format( + tc_name, NETWORK1_1, result + ) + + step("verify IPv4 and IPv6 static route are configured and up on R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input) + assert ( + result is True + ), "Testcase {} : Failed Route {} not found in R4 FIB \n Error: {}".format( + tc_name, NETWORK2_1, result + ) + + step( + "Configure redistribute connected and static on R0 (R0-R1) on R4 ( R4-R3) IPv4 and IPv6 address family" + ) + redistribute_static = { + "r0": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert ( + result is True + ), "Testcase {} : Failed to configure the static route \n Error: {}".format( + tc_name, result + ) + + step("verify IPv4 and IPv6 static route are configured and up on R1") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + result = verify_bgp_rib(tgen, addr_type, "r1", static_routes_input) + assert ( + result is True + ), "Testcase {} : Failed : Redistributed routes from R0 is not learned in Router R1 RIB \n Error: {}".format( + tc_name, result + ) + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_LOOPBACK[addr_type]], + "next_hop": R1_NETWORK_LOOPBACK[addr_type], + }, + { + "network": [R1_NETWORK_CONNECTED[addr_type]], + "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input) + assert ( + result is True + ), "Testcase {} : Failed : Redistributed routes from R0 is not learned in Router R1 FIB \n Error: {}".format( + tc_name, result + ) + + step("verify IPv4 and IPv6 static route are configured and up on R3") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes_input) + assert ( + result is True + ), "Testcase {} : Failed : Redistributed routes from R4 is not learned in Router R3 RIB \n Error: {}".format( + tc_name, result + ) + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R3_NETWORK_LOOPBACK[addr_type]], + "next_hop": R3_NETWORK_LOOPBACK[addr_type], + }, + { + "network": [R3_NETWORK_CONNECTED[addr_type]], + "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input) + assert ( + result is True + ), "Testcase {} : Redistributed routes from R4 is not learned in Router R3 FIB \n Error: {}".format( + tc_name, result + ) + + step("Configure IPv4 and IPv6 prefix-list on R1 for (Sv4 , Sv6) route") + input_dict_3 = { + "r1": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv4"], + "action": "permit", + } + ] + }, + "ipv6": { + "Pv6": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv6"], + "action": "permit", + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert ( + result is True + ), "Testcase {} : Failed to configure the prefix lists \n Error: {}".format( + tc_name, result + ) + + step("Verify the Prefix - lists") + input_dict = {"r3": {"prefix_lists": ["Pv4", "Pv6"]}} + result = verify_prefix_lists(tgen, input_dict) + assert ( + result is True + ), "Testcase {} : Failed to verify the prefix lists in router R3 \n Error: {}".format( + tc_name, result + ) + + step("Configure IPv4 (RMv4) and IPv6 (RMv6) route-map on R1") + input_dict_3 = { + "r1": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert ( + result is True + ), "Testcase {} : Failed to configure the route-map \n Error: {}".format( + tc_name, result + ) + + step( + " Configure default originate with route-map RMv4 and RMv6 for IPv4 and IPv6 bgp neighbors on R1 ( R1-R2) " + ) + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv4"}}} + }, + "ipv6": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv6"}}} + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert ( + result is True + ), "Testcase {} : Failed to configure the default-originate in R1 towards R2 \n Error: {}".format( + tc_name, result + ) + + step("verify IPv4 and IPv6 default route received on R2 with R1 nexthop ") + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + ) + assert ( + result is True + ), "Testcase {} : Failed : Default routes are not learned in R2 FIB \n Error: {}".format( + tc_name, result + ) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + ) + assert ( + result is True + ), "Testcase {} : Failed : Default routes are not learned in R2 RIB\n Error: {}".format( + tc_name, result + ) + + step( + "Configure redistribute connected and static on R1 IPv4 and IPv6 address family" + ) + redistribute_static = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify IPv4 and IPv6 static and loopback route advertised from R4 and R0 are received on R2" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step(" Configure default-originate on R3 for R3 to R2 IPv4 and IPv6 BGP neighbors ") + local_as = get_dut_as_number(tgen, dut="r3") + default_originate_config = { + "r3": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": {"unicast": {"default_originate": {"r2": {}}}}, + "ipv6": {"unicast": {"default_originate": {"r2": {}}}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + STEP = """After configuring the Default Originate From R3 --> R2 + Both Default routes from R1 and R3 Should present in R2 BGP RIB. + 'The Deafult Route from iBGP is preffered over EBGP' thus + Default Route From R1->r2 should only present in R2 FIB """ + step(STEP) + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n IBGP default route should be preffered over EBGP default-originate \n Error: {}".format( + tc_name, result + ) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify the default route from R1 is recieved both on RIB and FIB on R2") + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=False, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify the static and loopback route advertised from R0 and R4 are received on R2 " + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step(" BGP Daemon restart operation") + routers = ["r1", "r2"] + for dut in routers: + step( + "Restart BGPD process on {}, when all the processes are running use watchfrr ".format( + dut + ) + ) + kill_router_daemons(tgen, dut, ["bgpd"]) + start_router_daemons(tgen, dut, ["bgpd"]) + + step("After restarting the BGP daomon Verify the default originate ") + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n IBGP default route should be prefeered over EBGP \n Error: {}".format( + tc_name, result + ) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify the default route from R1 is is recieved both on RIB and FIB on R2" + ) + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=False, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify the static and loopback route advertised from R0 and R4 are received on R2 " + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step(" Restarting FRR routers operation") + """ + NOTE : Verify that iBGP default route is preffered over eBGP default route + """ + routers = ["r1", "r2"] + for dut in routers: + step( + "Restart FRR router process on {}, when all the processes are running use watchfrr ".format( + dut + ) + ) + + stop_router(tgen, dut) + start_router(tgen, dut) + + result = verify_bgp_convergence(tgen, topo) + assert ( + result is True + ), " Testcase {} : After Restarting {} Convergence Failed".format(tc_name, dut) + + step("After restarting the FRR Router Verify the default originate ") + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify the default route from R1 is is recieved both on RIB and FIB on R2" + ) + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed\n IBGP default route should be preffered over EBGP default route \n Error: {}".format( + tc_name, result + ) + + step( + "Verify the static and loopback route advertised from R0 and R4 are received on R2 " + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_default_originate_after_shut_no_shut_bgp_neighbor_p1(request): + """ + Summary: "Verify default-originate route after shut/no shut and clear BGP neighbor " + """ + tgen = get_topogen() + global BGP_CONVERGENCE + global topo + # test case name + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + step("Configure EBGP between R0 to R1 and IBGP between R1 to R2") + step("Configure EBGP between R2 to R3 and IBGP between R3 to R4") + input_dict = { + "r0": { + "bgp": { + "local_as": 999, + } + }, + "r1": { + "bgp": { + "local_as": 1000, + } + }, + "r2": { + "bgp": { + "local_as": 1000, + } + }, + "r3": { + "bgp": { + "local_as": 4000, + } + }, + "r4": { + "bgp": { + "local_as": 4000, + } + }, + } + result = modify_as_number(tgen, topo, input_dict) + try: + assert result is True + except AssertionError: + logger.info("Expected behaviour: {}".format(result)) + logger.info("BGP config is not created because of invalid ASNs") + step("After changing the BGP AS Path Verify the BGP Convergence") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Configure one IPv4 and one IPv6 static route on R0 and R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r0": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify IPv4 and IPv6 static route configured on R0 and R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r0": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r0", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure redistribute connected and static on R0 (R0-R1) on R4 ( R4-R3) IPv4 and IPv6 address family" + ) + redistribute_static = { + "r0": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + }, + { + "redist_type": "connected", + }, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + }, + { + "redist_type": "connected", + }, + ] + } + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + }, + { + "redist_type": "connected", + }, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + }, + { + "redist_type": "connected", + }, + ] + } + }, + } + } + }, + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "connected", + } + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "connected", + } + ] + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify IPv4 and IPv6 static route configured on R1 from R0") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + result = verify_bgp_rib(tgen, addr_type, "r1", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify IPv4 and IPv6 static route configured on R3 from R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure default-originate on R1 for R1 to R2 neighbor for IPv4 and IPv6 peer " + ) + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": {"unicast": {"default_originate": {"r2": {}}}}, + "ipv6": {"unicast": {"default_originate": {"r2": {}}}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify IPv4 and IPv6 bgp default route received on R2 nexthop as R1") + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + step( + "After configuring default-originate command , verify default routes are advertised on R2 from R0 and R4" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + }, + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure default-originate on R3 for R3 to R2 neighbor for IPv4 and IPv6 peer" + ) + local_as = get_dut_as_number(tgen, dut="r3") + default_originate_config = { + "r3": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": {"unicast": {"default_originate": {"r2": {}}}}, + "ipv6": {"unicast": {"default_originate": {"r2": {}}}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + STEP = """After configuring the Default Originate From R3 --> R2 + Both Default routes from R1 and R3 Should present in R2 BGP RIB. + 'The Deafult Route from iBGP is preffered over EBGP' thus + Default Route From R1->r2 should only present in R2 FIB """ + step(STEP) + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n IBGP default route should be preffered over EBGP \n Error: {}".format( + tc_name, result + ) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify the default route from R1 is recieved both on RIB and FIB on R2") + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "After configuring default-originate command , verify static ,connected and loopback routes are advertised on R2 from R0 and R4" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + # updating the topology with the updated AS-Number to avoid conflict in con configuring the AS + updated_topo = topo + updated_topo["routers"]["r0"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r0") + updated_topo["routers"]["r1"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r1") + updated_topo["routers"]["r2"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r2") + updated_topo["routers"]["r3"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r3") + updated_topo["routers"]["r4"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r4") + + step( + "Shut R1 to R2 IPv4 and IPv6 BGP neighbor from R1 IPv4 and IPv6 address family " + ) + + local_as = get_dut_as_number(tgen, dut="r1") + shut_neighbor = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"shutdown": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"shutdown": True}}} + } + } + }, + }, + } + } + } + result = create_router_bgp(tgen, updated_topo, shut_neighbor) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + interface = topo["routers"]["r2"]["links"]["r1"]["interface"] + input_dict = {"r2": {"interface_list": [interface], "status": "down"}} + + result = interface_status(tgen, topo, input_dict) + assert ( + result is True + ), "Testcase {} : Bring down interface failed ! \n Error: {}".format( + tc_name, result + ) + + step( + "Verify IPv4 and IPv6 default static and loopback route which received from R1 are deleted from R2" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + result = verify_bgp_rib( + tgen, addr_type, "r2", static_routes_input, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n after shutting down interface routes are not expected \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes( + tgen, addr_type, "r2", static_routes_input, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n after shutting down interface routes are not expected \n Error: {}".format( + tc_name, result + ) + + step("verify that No impact on IPv4 IPv6 and default route received from R3 ") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "No-Shut R1 to R2 IPv4 and IPv6 BGP neighbor from R1 IPv4 and IPv6 address family " + ) + local_as = get_dut_as_number(tgen, dut="r1") + shut_neighbor = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"shutdown": False}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"shutdown": False}}} + } + } + }, + }, + } + } + } + result = create_router_bgp(tgen, updated_topo, shut_neighbor) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + interface = topo["routers"]["r2"]["links"]["r1"]["interface"] + input_dict = {"r2": {"interface_list": [interface], "status": "up"}} + + result = interface_status(tgen, topo, input_dict) + assert ( + result is True + ), "Testcase {} : Bring up interface failed ! \n Error: {}".format(tc_name, result) + + step( + "After no shut Verify IPv4 and IPv6 bgp default route next hop as R1 , static ,connected and loopback received on R2 from r0 and r4 " + ) + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + }, + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Shut R3 to R2 IPv4 and IPv6 BGP neighbor from R2 IPv4 and IPv6 address family" + ) + local_as = get_dut_as_number(tgen, dut="r3") + shut_neighbor = { + "r3": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {"shutdown": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {"shutdown": True}}} + } + } + }, + }, + } + } + } + result = create_router_bgp(tgen, updated_topo, shut_neighbor) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + interface = topo["routers"]["r2"]["links"]["r3"]["interface"] + input_dict = {"r2": {"interface_list": [interface], "status": "down"}} + + result = interface_status(tgen, topo, input_dict) + assert ( + result is True + ), "Testcase {} : Bring down interface failed ! \n Error: {}".format( + tc_name, result + ) + + step( + "Verify IPv4 and IPv6 default static and loopback route which received from R3 are deleted from R2 " + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + result = verify_bgp_rib( + tgen, addr_type, "r2", static_routes_input, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed\n After shutting down the interface routes are not expected \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes( + tgen, addr_type, "r2", static_routes_input, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n After shutting down the interface routes are not expected \n Error: {}".format( + tc_name, result + ) + + step("Verify that Default route is removed i.e advertised from R3") + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n After shutting down the interface Default route are not expected \n Error: {}".format( + tc_name, result + ) + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n After shutting down the interface Default route are not expected \n Error: {}".format( + tc_name, result + ) + + step("Verify that No impact on IPv4 IPv6 and default route received from R1") + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + }, + ] + } + } + + result = verify_fib_routes( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "No-Shut R3 to R2 IPv4 and IPv6 BGP neighbor from R2 IPv4 and IPv6 address family" + ) + local_as = get_dut_as_number(tgen, dut="r3") + shut_neighbor = { + "r3": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {"shutdown": False}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {"shutdown": False}}} + } + } + }, + }, + } + } + } + result = create_router_bgp(tgen, updated_topo, shut_neighbor) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + interface = topo["routers"]["r2"]["links"]["r3"]["interface"] + input_dict = {"r2": {"interface_list": [interface], "status": "up"}} + + result = interface_status(tgen, topo, input_dict) + assert ( + result is True + ), "Testcase {} : Bring up interface failed ! \n Error: {}".format(tc_name, result) + + step( + "Verify that a static ,connected and loopback routes are received from R0 and R4 on R2 " + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("verify that default route is received on R2 from R1") + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that default route is received on R2 from R3") + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("Clear IPv4 and IP6 BGP session from R2 and R1 one by one ") + routers = ["r1", "r2"] + for dut in routers: + for addr_type in ADDR_TYPES: + + clear_bgp(tgen, addr_type, dut) + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("verify that default route is received on R2 from R3") + + interface = topo["routers"]["r3"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface) + ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0] + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify the static , loopback and connected routes received from r0 and r4" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Shut BGP neighbor interface R2 (R2 to R1) link ") + intf_r2_r1 = topo["routers"]["r2"]["links"]["r1"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_r1, False) + + step("Verify the bgp Convergence ") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo, expected=False) + assert ( + BGP_CONVERGENCE is not True + ), " :Failed After shutting interface BGP convergence is expected to be faileed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify that default route from R1 got deleted from BGP and RIB table") + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed\n After shuting interface default route should be removed from RIB \n Error: {}".format( + tc_name, result + ) + + step("No - Shut BGP neighbor interface R2 (R2 to R1) link ") + intf_r2_r1 = topo["routers"]["r2"]["links"]["r1"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_r1, True) + + step("Verify the bgp Convergence ") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step("verify that default route is received on R2 from R3") + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify the static , loopback and connected routes received from r0 and r4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Shut link from R3 to R2 from R3") + intf_r3_r2 = topo["routers"]["r3"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, "r3", intf_r3_r2, False) + + step("Verify the bgp Convergence ") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo, expected=False) + assert ( + BGP_CONVERGENCE is not True + ), " :Failed \nAfter Shuting the interface BGP convegence is expected to be failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify that default route from R3 got deleted from BGP and RIB table") + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("No-Shut link from R3 to R2 from R3") + + ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0] + ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0] + + DEFAULT_ROUTE_NXT_HOP_1 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_nxt_hop} + + ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0] + ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0] + + DEFAULT_ROUTE_NXT_HOP_3 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_nxt_hop} + + intf_r3_r2 = topo["routers"]["r3"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, "r3", intf_r3_r2, True) + + step("Verify the bgp Convergence ") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo, expected=True) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step("verify that default route is received on R2 from R3") + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=True, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify the static , loopback and connected routes received from r0 and r4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R0_NETWORK_LOOPBACK[addr_type]], + "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R0_NETWORK_CONNECTED[addr_type]], + "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_LOOPBACK[addr_type]], + "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R4_NETWORK_CONNECTED[addr_type]], + "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_originate/test_default_orginate_vrf.py b/tests/topotests/bgp_default_originate/test_default_orginate_vrf.py new file mode 100644 index 0000000..4dedac5 --- /dev/null +++ b/tests/topotests/bgp_default_originate/test_default_orginate_vrf.py @@ -0,0 +1,1377 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Shreenidhi A R +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# +import os +import sys +import time +import pytest +from time import sleep +from copy import deepcopy +from lib.topolog import logger + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json +from lib.topolog import logger + +from lib.bgp import ( + verify_bgp_convergence, + verify_graceful_restart, + create_router_bgp, + verify_router_id, + modify_as_number, + verify_as_numbers, + clear_bgp_and_verify, + clear_bgp, + verify_bgp_rib, + get_prefix_count_route, + get_dut_as_number, + verify_rib_default_route, + verify_fib_default_route, + verify_bgp_advertised_routes_from_neighbor, + verify_bgp_received_routes_from_neighbor, +) +from lib.common_config import ( + interface_status, + verify_prefix_lists, + verify_rib, + kill_router_daemons, + start_router_daemons, + shutdown_bringup_interface, + step, + required_linux_kernel_version, + stop_router, + start_router, + create_route_maps, + create_prefix_lists, + get_frr_ipv6_linklocal, + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + create_static_routes, + check_router_status, + delete_route_maps, +) + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. +# pylint: disable=C0413 +# Import topogen and topotest helpers + +# Global variables +topo = None +KEEPALIVETIMER = 1 +HOLDDOWNTIMER = 3 + + +# Global variables +NETWORK1_1 = {"ipv4": "198.51.1.1/32", "ipv6": "2001:DB8::1:1/128"} +NETWORK2_1 = {"ipv4": "198.51.1.2/32", "ipv6": "2001:DB8::1:2/128"} +NETWORK5_1 = {"ipv4": "198.51.1.3/32", "ipv6": "2001:DB8::1:3/128"} +NETWORK5_2 = {"ipv4": "198.51.1.4/32", "ipv6": "2001:DB8::1:4/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +r0_connected_address_ipv4 = "192.168.0.0/24" +r0_connected_address_ipv6 = "fd00::/64" +r1_connected_address_ipv4 = "192.168.1.0/24" +r1_connected_address_ipv6 = "fd00:0:0:1::/64" +r3_connected_address_ipv4 = "192.168.2.0/24" +r3_connected_address_ipv6 = "fd00:0:0:2::/64" +r4_connected_address_ipv4 = "192.168.3.0/24" +r4_connected_address_ipv6 = "fd00:0:0:3::/64" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_default_orginate_vrf.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global ADDR_TYPES + global BGP_CONVERGENCE + global DEFAULT_ROUTES + global DEFAULT_ROUTE_NXT_HOP_R1, DEFAULT_ROUTE_NXT_HOP_R3 + global R1_NETWORK_LOOPBACK, R1_NETWORK_LOOPBACK_NXTHOP + global R0_NETWORK_CONNECTED_NXTHOP, R1_NETWORK_CONNECTED, R1_NETWORK_CONNECTED_NXTHOP + global R3_NETWORK_LOOPBACK, R3_NETWORK_LOOPBACK_NXTHOP + global R3_NETWORK_CONNECTED, R3_NETWORK_CONNECTED_NXTHOP + + ADDR_TYPES = check_address_types() + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + # There are the global varibles used through out the file these are acheived only after building the topology. + + r0_loopback_address_ipv4 = topo["routers"]["r0"]["links"]["lo"]["ipv4"] + r0_loopback_address_ipv4_nxt_hop = topo["routers"]["r0"]["links"]["r1"][ + "ipv4" + ].split("/")[0] + r0_loopback_address_ipv6 = topo["routers"]["r0"]["links"]["lo"]["ipv6"] + r0_loopback_address_ipv6_nxt_hop = topo["routers"]["r0"]["links"]["r1"][ + "ipv6" + ].split("/")[0] + + r1_loopback_address_ipv4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"] + r1_loopback_address_ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"][ + "ipv4" + ].split("/")[0] + r1_loopback_address_ipv6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"] + r1_loopback_address_ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"][ + "ipv6" + ].split("/")[0] + + r4_loopback_address_ipv4 = topo["routers"]["r4"]["links"]["lo"]["ipv4"] + r4_loopback_address_ipv4_nxt_hop = topo["routers"]["r4"]["links"]["r3"][ + "ipv4" + ].split("/")[0] + r4_loopback_address_ipv6 = topo["routers"]["r4"]["links"]["lo"]["ipv6"] + r4_loopback_address_ipv6_nxt_hop = topo["routers"]["r4"]["links"]["r3"][ + "ipv6" + ].split("/")[0] + + r3_loopback_address_ipv4 = topo["routers"]["r3"]["links"]["lo"]["ipv4"] + r3_loopback_address_ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"][ + "ipv4" + ].split("/")[0] + r3_loopback_address_ipv6 = topo["routers"]["r3"]["links"]["lo"]["ipv6"] + r3_loopback_address_ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"][ + "ipv6" + ].split("/")[0] + + R1_NETWORK_LOOPBACK = { + "ipv4": r1_loopback_address_ipv4, + "ipv6": r1_loopback_address_ipv6, + } + R1_NETWORK_LOOPBACK_NXTHOP = { + "ipv4": r1_loopback_address_ipv4_nxt_hop, + "ipv6": r1_loopback_address_ipv6_nxt_hop, + } + + R1_NETWORK_CONNECTED = { + "ipv4": r1_connected_address_ipv4, + "ipv6": r1_connected_address_ipv6, + } + R1_NETWORK_CONNECTED_NXTHOP = { + "ipv4": r1_loopback_address_ipv4_nxt_hop, + "ipv6": r1_loopback_address_ipv6_nxt_hop, + } + + R3_NETWORK_LOOPBACK = { + "ipv4": r3_loopback_address_ipv4, + "ipv6": r3_loopback_address_ipv6, + } + R3_NETWORK_LOOPBACK_NXTHOP = { + "ipv4": r3_loopback_address_ipv4_nxt_hop, + "ipv6": r3_loopback_address_ipv6_nxt_hop, + } + + R3_NETWORK_CONNECTED = { + "ipv4": r3_connected_address_ipv4, + "ipv6": r3_connected_address_ipv6, + } + R3_NETWORK_CONNECTED_NXTHOP = { + "ipv4": r3_loopback_address_ipv4_nxt_hop, + "ipv6": r3_loopback_address_ipv6_nxt_hop, + } + + # populating the nexthop for default routes + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + interface = topo["routers"]["r1"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface) + ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0] + ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0] + DEFAULT_ROUTE_NXT_HOP_R1 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + + interface = topo["routers"]["r3"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface) + ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0] + ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv6"].split("/")[0] + DEFAULT_ROUTE_NXT_HOP_R3 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### +def test_verify_default_originate_route_with_non_default_VRF_p1(request): + """ + "Verify default-originate route with non-default VRF" + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + # these steps are implemented as base toplopgy setup + step("Configure IPV4 and IPV6 IBGP between R1 and R2 default VRF") + step("Configure IPV4 and IPV6 EBGP between R2 to R3 non-default VRF (RED)") + step( + "Configure IPv4 and IP6 loopback address on R1 default and R3 non-default (RED) VRF" + ) + step("After changing the BGP AS Path Verify the BGP Convergence") + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + step( + "Configure IPv4 and IPv6 static route on R1 default and R3 non-default (RED) VRF with nexthop as Null ( different static route on each side)" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert ( + result is True + ), "Testcase {} : Failed to configure the static routes in router R1 default vrf \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert ( + result is True + ), "Testcase {} : Failed to configure static route in R3 non default vrf RED \n Error: {}".format( + tc_name, result + ) + + step( + "Verify IPv4 and IPv6 static route configured on R1 default vrf and R3 non-default (RED) vrf" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_rib(tgen, addr_type, "r1", static_routes_input) + assert ( + result is True + ), "Testcase {} : Failed: Routes configured on vrf is not seen in R1 default VRF FIB \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + } + } + result = verify_rib(tgen, addr_type, "r3", static_routes_input) + assert ( + result is True + ), "Testcase {} : Failed : Routes configured in non-defaul vrf in R3 FIB is \n Error: {}".format( + tc_name, result + ) + + step( + "Configure redistribute connected and static on R1 (R1-R2) and on R3 ( R2-R3 RED VRF) IPv4 and IPv6 address family " + ) + redistribute_static = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + }, + "r3": { + "bgp": { + "local_as": 3000, + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + }, + } + }, + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert ( + result is True + ), "Testcase {} : Failed to configure the redistribute on R1 and R3 \n Error: {}".format( + tc_name, result + ) + + step( + "Verify IPv4 and IPv6 static route configured on R1 received as BGP routes on R2 default VRF " + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R1_NETWORK_LOOPBACK[addr_type]], + "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_CONNECTED[addr_type]], + "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Verify IPv4 and IPv6 static route configured on R3 received as BGP routes on R2 non-default VRF " + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + }, + { + "network": [R3_NETWORK_CONNECTED[addr_type]], + "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type], + "vrf": "RED", + }, + ] + } + } + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure default-originate on R1 for R1 to R2 neighbor for IPv4 and IPv6 peer" + ) + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": {"unicast": {"default_originate": {"r2": {}}}}, + "ipv6": {"unicast": {"default_originate": {"r2": {}}}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert ( + result is True + ), "Testcase {} : Failed to configure the default originate \n Error: {}".format( + tc_name, result + ) + + step( + "After configuring default-originate command , verify default routes are advertised on R2 " + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + } + ] + } + } + + result = verify_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R1_NETWORK_LOOPBACK[addr_type]], + "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_CONNECTED[addr_type]], + "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + snapshot1 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3", vrf="RED") + + step( + "Configure default-originate on R3 for R3 to R2 neighbor (RED VRF) for IPv4 and IPv6 peer" + ) + + default_originate_config = { + "r3": { + "bgp": { + "local_as": "3000", + "vrf": "RED", + "address_family": { + "ipv4": {"unicast": {"default_originate": {"r2": {}}}}, + "ipv6": {"unicast": {"default_originate": {"r2": {}}}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify IPv4 and IPv6 bgp default route and static route received on R2 VRF red nexthop as R3" + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + }, + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + "vrf": "RED", + }, + ] + } + } + result = verify_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("verify Out-prefix count incremented for IPv4/IPv6 default route on VRF red") + snapshot2 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3", vrf="RED") + step("verifying the prefix count incrementing or not ") + isIPv4prefix_incremented = False + isIPv6prefix_incremented = False + if snapshot1["ipv4_count"] < snapshot2["ipv4_count"]: + isIPv4prefix_incremented = True + if snapshot1["ipv6_count"] < snapshot2["ipv6_count"]: + isIPv6prefix_incremented = True + + assert ( + isIPv4prefix_incremented is True + ), "Testcase {} : Failed Error: IPV4 Prefix is not incremented on receiveing ".format( + tc_name + ) + + step("Configure import VRF red on R2 for IPV4 and IPV6 BGP peer") + step("Importing the non-default vrf in default VRF ") + local_as = get_dut_as_number(tgen, "r2") + input_import_vrf = { + "r2": { + "bgp": [ + { + "local_as": local_as, + "address_family": { + "ipv4": {"unicast": {"import": {"vrf": "RED"}}}, + "ipv6": {"unicast": {"import": {"vrf": "RED"}}}, + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_import_vrf) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "Verify VRF RED IPv4 and IPv6, default-originate, \n static and loopback route are imported to R2 default VRF table ,\n default-originate route coming from VRF red should not active on R2 default VRF table" + ) + step("verifying the static routes connected and loop back routes") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R1_NETWORK_LOOPBACK[addr_type]], + "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R1_NETWORK_CONNECTED[addr_type]], + "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + { + "network": [R3_NETWORK_CONNECTED[addr_type]], + "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + STEP = """ After importing non defualt VRF into default vrf . + verify that the default originate from R1 --> R2(non -default) is preffered over R3 --> R2 + because the Default Route prefers iBGP over eBGP over + Default Route from R1 Should be present in BGP RIB and FIB + Default Route from R3 Should be present only in BGP RIB not in FIB + """ + step(STEP) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + } + ] + } + } + + result = verify_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + }, + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Configure import VRF default on R2 (R2-R3) RED VRF for IPV4 and IPV6 BGP peer" + ) + step("Importing the default vrf in non-default VRF ") + local_as = "2000" + input_import_vrf = { + "r2": { + "bgp": [ + { + "local_as": local_as, + "vrf": "RED", + "address_family": { + "ipv4": {"unicast": {"import": {"vrf": "default"}}}, + "ipv6": {"unicast": {"import": {"vrf": "default"}}}, + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_import_vrf) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "Default VR, IPv4 and IPv6 , default-originate, \n static and loopback route are imported to R2 VRF RED table \n, default-originate route coming from VRF red should not active on R2 default VRF table" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + }, + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + }, + { + "network": [R1_NETWORK_LOOPBACK[addr_type]], + "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type], + "vrf": "RED", + }, + { + "network": [R1_NETWORK_CONNECTED[addr_type]], + "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type], + "vrf": "RED", + }, + { + "network": [R3_NETWORK_CONNECTED[addr_type]], + "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type], + "vrf": "RED", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + STEP = """ After importing defualt VRF into non default vrf . + verify that the default originate from R1 --> R2(non -default) is preffered over R3 --> R2 + because the Default Route prefers iBGP over eBGP over + Default Route from R1 Should be present in BGP RIB and FIB + Default Route from R3 Should be present only in BGP RIB not in FIB + """ + step(STEP) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + "vrf": "RED", + } + ] + } + } + + result = verify_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + "vrf": "RED", + } + ] + } + } + + result = verify_rib( + tgen, + addr_type, + "r2", + static_routes_input, + next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + expected=False, + ) + assert result is not True, "Testcase {} : Failed {} \n Error: {}".format( + tc_name, STEP, result + ) + + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + "vrf": "RED", + }, + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type], + "vrf": "RED", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Remove import VRF configure in step 8 and then remove import VRF configured on step 9" + ) + local_as = get_dut_as_number(tgen, "r2") + input_import_vrf = { + "r2": { + "bgp": [ + { + "local_as": local_as, + "address_family": { + "ipv4": {"unicast": {"import": {"vrf": "RED", "delete": True}}}, + "ipv6": {"unicast": {"import": {"vrf": "RED", "delete": True}}}, + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_import_vrf) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that the routes imported from non default VRF - RED is removed") + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [R3_NETWORK_LOOPBACK[addr_type]], + "next_hop": R3_NETWORK_LOOPBACK_NXTHOP[addr_type], + }, + { + "network": [R3_NETWORK_CONNECTED[addr_type]], + "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type], + }, + ] + } + } + + result = verify_rib(tgen, addr_type, "r2", static_routes_input, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n routes imported from non default VRF is not expected Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, addr_type, "r2", static_routes_input, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n routes imported from non default VRF is not expected \nError: {}".format( + tc_name, result + ) + + step( + "Remove import VRF configure in step 8 and then remove import VRF configured on step 9" + ) + local_as = "2000" + input_import_vrf = { + "r2": { + "bgp": [ + { + "local_as": local_as, + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": {"import": {"vrf": "default", "delete": True}} + }, + "ipv6": { + "unicast": {"import": {"vrf": "default", "delete": True}} + }, + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_import_vrf) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step("Verify that the routes impoted from default VRF is removed") + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + }, + { + "network": [R1_NETWORK_LOOPBACK[addr_type]], + "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type], + "vrf": "RED", + }, + { + "network": [R1_NETWORK_CONNECTED[addr_type]], + "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type], + "vrf": "RED", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, "r2", static_routes_input, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n routes impoted from default VRF is not expected \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, addr_type, "r2", static_routes_input, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n routes impoted from default VRF is not expected \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_default_originate_route_with_non_default_VRF_with_route_map_p1(request): + """ + "Verify default-originate route with non-default VRF with route-map import " + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure IPV4 and IPV6 static route on R0 with Null nexthop") + STEP = """ + Configure IPV4 and IPV6 EBGP session between R0 and R1 + Configure IPV4 and IPV6 static route on R0 with Null nexthop """ + step(STEP) + input_dict = { + "r0": {"bgp": {"local_as": 222, "vrf": "default"}}, + "r1": {"bgp": {"local_as": 333, "vrf": "default"}}, + } + result = modify_as_number(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + try: + assert result is True + except AssertionError: + logger.info("Expected behaviour: {}".format(result)) + logger.info("BGP config is not created because of invalid ASNs") + step("After changing the BGP AS Path Verify the BGP Convergence") + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Configuring static route at R0") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r0": { + "static_routes": [ + { + "network": [NETWORK5_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step(" Configure re-distribute static on R0 for R0 to R1 for IPV4 and IPV6 peer ") + redistribute_static = { + "r0": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure default-originate on R1 for R1 to R2 neighbor for IPv4 and IPv6 peer" + ) + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": {"unicast": {"default_originate": {"r2": {}}}}, + "ipv6": {"unicast": {"default_originate": {"r2": {}}}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + snapshot1 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3", vrf="RED") + + step("Verify IPv4 and IPv6 static received on R2 default VRF as BGP routes") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK5_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + }, + ] + } + } + + result = verify_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + " Configure IPv4 and IPv6 prefix-list of of route received from R1 on R2 and for 0.0.0.0/0 0::0/0 route" + ) + input_dict_3 = { + "r2": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + { + "seqid": "1", + "network": NETWORK5_1["ipv4"], + "action": "permit", + }, + {"seqid": "2", "network": "0.0.0.0/0", "action": "permit"}, + { + "seqid": "3", + "network": NETWORK2_1["ipv4"], + "action": "permit", + }, + ] + }, + "ipv6": { + "Pv6": [ + { + "seqid": "1", + "network": NETWORK5_1["ipv6"], + "action": "permit", + }, + {"seqid": "2", "network": "0::0/0", "action": "permit"}, + { + "seqid": "3", + "network": NETWORK2_1["ipv6"], + "action": "permit", + }, + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify IPv4 and IPv6 Prefix list got configured on R3") + input_dict = {"r2": {"prefix_lists": ["Pv4", "Pv6"]}} + result = verify_prefix_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure IPv4/IPv6 route-map on R2 with deny sequence using above prefix-list" + ) + input_dict_3 = { + "r2": { + "route_maps": { + "RMv4": [ + { + "action": "deny", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + }, + ], + "RMv6": [ + { + "action": "deny", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + STEP = """ + import Route-map anf non-default VRF into defailt vrf + import vrf route-map RM1 + import vrf red + """ + step(STEP) + + local_as = get_dut_as_number(tgen, "r2") + input_import_vrf = { + "r2": { + "bgp": [ + { + "local_as": local_as, + "address_family": { + "ipv4": {"unicast": {"import": {"vrf": "RED"}}}, + "ipv6": {"unicast": {"import": {"vrf": "RED"}}}, + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_import_vrf) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step(STEP) + input_import_vrf = { + "r2": { + "bgp": [ + { + "local_as": local_as, + "address_family": { + "ipv4": {"unicast": {"import": {"vrf": "route-map RMv4"}}}, + "ipv6": {"unicast": {"import": {"vrf": "route-map RMv6"}}}, + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_import_vrf) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "Verify IPv4 and IPv6 routes present on VRF red ( static , default-originate) should not get advertised to default VRF " + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + }, + ] + } + } + + result = verify_rib(tgen, addr_type, "r2", static_routes_input, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n VRF red ( static , default-originate) should not get advertised to default VRF \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib( + tgen, addr_type, "r2", static_routes_input, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n VRF red ( static , default-originate) should not get advertised to default VRF \nError: {}".format( + tc_name, result + ) + + step("Change route-map sequence deny to permit") + input_dict_3 = { + "r2": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 and IPv6 routes present on VRF red ( static , default-originate) should get advertised to default VRF" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": [DEFAULT_ROUTES[addr_type]], + "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type], + } + ] + } + } + + result = verify_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("verify Out-prefix count incremented for IPv4/IPv6 default route on VRF red") + snapshot2 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3", vrf="RED") + step("verifying the prefix count incrementing or not ") + isIPv4prefix_incremented = False + isIPv6prefix_incremented = False + if snapshot1["ipv4_count"] <= snapshot2["ipv4_count"]: + isIPv4prefix_incremented = True + if snapshot1["ipv6_count"] <= snapshot2["ipv6_count"]: + isIPv6prefix_incremented = True + + assert ( + isIPv4prefix_incremented is True + ), "Testcase {} : Failed Error: IPV4 Prefix is not incremented on receiveing ".format( + tc_name + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_originate/test_default_originate_conditional_routemap.py b/tests/topotests/bgp_default_originate/test_default_originate_conditional_routemap.py new file mode 100644 index 0000000..82c4e7e --- /dev/null +++ b/tests/topotests/bgp_default_originate/test_default_originate_conditional_routemap.py @@ -0,0 +1,2092 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Shreenidhi A R +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# +""" +Following scenerios are covered. +1. When there is change in route-map policy associated with default-originate, changes does not reflect. +2. When route-map associated with default-originate is deleted, default route doesn't get withdrawn +3. Update message is not being sent when only route-map is removed from the default-originate config. +4. SNT counter gets incremented on change of every policy associated with default-originate +5. Route-map with multiple match clauses causes inconsistencies with default-originate. +6. BGP-Default originate behaviour with BGP attributes +""" +import os +import sys +import time +import pytest +from copy import deepcopy +from lib.topolog import logger + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json +from lib.topolog import logger + +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + get_prefix_count_route, + modify_as_number, + verify_bgp_rib, + get_dut_as_number, + verify_rib_default_route, + verify_fib_default_route, +) +from lib.common_config import ( + verify_fib_routes, + step, + required_linux_kernel_version, + create_route_maps, + interface_status, + create_prefix_lists, + get_frr_ipv6_linklocal, + start_topology, + write_test_header, + verify_prefix_lists, + check_address_types, + write_test_footer, + reset_config_on_routers, + create_static_routes, + check_router_status, + delete_route_maps, +) + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers + +# Global variables +topo = None +NETWORK1_1 = {"ipv4": "198.51.1.1/32", "ipv6": "2001:DB8::1:1/128"} +DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_default_originate_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global ADDR_TYPES + global BGP_CONVERGENCE + global DEFAULT_ROUTES + global DEFAULT_ROUTE_NXT_HOP_R1, DEFAULT_ROUTE_NXT_HOP_R3 + ADDR_TYPES = check_address_types() + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + interface = topo["routers"]["r1"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface) + ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0] + ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0] + DEFAULT_ROUTE_NXT_HOP_R1 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + + interface = topo["routers"]["r3"]["links"]["r2"]["interface"] + ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface) + ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0] + ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv6"].split("/")[0] + DEFAULT_ROUTE_NXT_HOP_R3 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local} + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_default_originate_delete_conditional_routemap(request): + """ + "scenerio covered": + 1. When there is change in route-map policy associated with default-originate, changes does not reflect. + 2. When route-map associated with default-originate is deleted, default route doesn't get withdrawn + 3. Update message is not being sent when only route-map is removed from the default-originate config. + 4. SNT counter gets incremented on change of every policy associated with default-originate + 5. Route-map with multiple match clauses causes inconsistencies with default-originate. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + global topo + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + step("Configure IPv4 and IPv6 , IBGP neighbor between R1 and R2") + step("Configure IPv4 and IPv6 , EBGP neighbor between R1 and R0") + input_dict = { + "r0": { + "bgp": { + "local_as": 999, + } + }, + "r1": { + "bgp": { + "local_as": 1000, + } + }, + "r2": { + "bgp": { + "local_as": 1000, + } + }, + "r3": { + "bgp": { + "local_as": 2000, + } + }, + "r4": { + "bgp": { + "local_as": 3000, + } + }, + } + result = modify_as_number(tgen, topo, input_dict) + try: + assert result is True + except AssertionError: + logger.info("Expected behaviour: {}".format(result)) + logger.info("BGP config is not created because of invalid ASNs") + + step("After changing the BGP remote as , Verify the BGP Convergence") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert ( + BGP_CONVERGENCE is True + ), "Complete convergence is expected after changing ASN ....! ERROR :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Configure 1 IPv4 and 1 IPv6 Static route on R0 with next-hop as Null0") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r0": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert ( + result is True + ), "Testcase {} : Failed to configure the static route \n Error: {}".format( + tc_name, result + ) + + step("verify IPv4 and IPv6 static route are configured and up on R0") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r0": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r0", static_routes_input) + assert ( + result is True + ), "Testcase {} : routes {} not found in R0 FIB \n Error: {}".format( + tc_name, static_routes_input, result + ) + + step( + "Configure redistribute static on IPv4 and IPv6 address family on R0 for R0 to R1 neighbor " + ) + redistribute_static = { + "r0": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert ( + result is True + ), "Testcase {} : Failed to configure redistribute configuration....! \n Error: {}".format( + tc_name, result + ) + + step("verify IPv4 and IPv6 static route are received on R1") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r0": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input) + assert ( + result is True + ), "Testcase {} : Failed... Routes {} expected in r1 FIB after configuring the redistribute config on R0 \n Error: {}".format( + tc_name, static_routes_input, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r1", static_routes_input) + assert ( + result is True + ), "Testcase {} : Failed... Routes {} expected in r1 RIB after configuring the redistribute config on R0\n Error: {}".format( + tc_name, static_routes_input, result + ) + + step( + "Configure IPv4 prefix-list 'Pv4' and and IPv6 prefix-list 'Pv6' on R1 to match BGP route Sv41, IPv6 route Sv61 permit " + ) + input_dict_3 = { + "r1": { + "prefix_lists": { + "ipv4": { + "Pv4": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv4"], + "action": "permit", + }, + ] + }, + "ipv6": { + "Pv6": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv6"], + "action": "permit", + }, + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert ( + result is True + ), "Testcase {} : Failed to configure the prefix list \n Error: {}".format( + tc_name, result + ) + + step( + "Configure IPV4 and IPv6 route-map (RMv4 and RMv6) matching prefix-list (Pv4 and Pv6) respectively on R1" + ) + input_dict_3 = { + "r1": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + "set": { + "path": { + "as_num": "5555", + "as_action": "prepend", + } + }, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + "set": { + "path": { + "as_num": "5555", + "as_action": "prepend", + } + }, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert ( + result is True + ), "Testcase {} : Failed to configure the route map \n Error: {}".format( + tc_name, result + ) + + step( + "Configure default-originate with route-map (RMv4 and RMv6) on R1, on BGP IPv4 and IPv6 address family " + ) + local_as = get_dut_as_number(tgen, dut="r1") + default_originate_config = { + "r1": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv4"}}} + }, + "ipv6": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv6"}}} + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert ( + result is True + ), "Testcase {} : Failed to configure the default originate \n Error: {}".format( + tc_name, result + ) + + step( + "After configuring default-originate command , verify default routes are advertised on R2 " + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + metric=0, + expected_aspath="5555", + ) + assert ( + result is True + ), "Testcase {} : Failed to configure the default originate \n Error: {}".format( + tc_name, result + ) + + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert ( + result is True + ), "Testcase {} : Failed to configure the default originate \n Error: {}".format( + tc_name, result + ) + + step("Changing the as-path policy of the existing route-map") + input_dict_3 = { + "r1": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + "set": { + "path": { + "as_num": "6666", + "as_action": "prepend", + } + }, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + "set": { + "path": { + "as_num": "6666", + "as_action": "prepend", + } + }, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert ( + result is True + ), "Testcase {} : Failed to configure the route map \n Error: {}".format( + tc_name, result + ) + + step( + "Verify prefix sent count on R1 towards R2 \n Send count shoud not be incremented on change of existing (AS-path) policy " + ) + snapshot = get_prefix_count_route( + tgen, topo, dut="r1", peer="r2", link="r1", sent=True, received=False + ) + + ipv4_prefix_count = False + ipv6_prefix_count = False + if snapshot["ipv4_count"] == 2: + ipv4_prefix_count = True + if snapshot["ipv6_count"] == 2: + ipv6_prefix_count = True + + assert ( + ipv4_prefix_count is True + ), "Testcase {} : Failed Error: Expected sent Prefix is 2 but obtained {} ".format( + tc_name, ipv4_prefix_count + ) + assert ( + ipv6_prefix_count is True + ), "Testcase {} : Failed Error: Expected sent Prefix is 2 but obtained {} ".format( + tc_name, ipv6_prefix_count + ) + + step( + "After changing the as-path policy verify the new policy is advertised to router R2" + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + metric=0, + expected_aspath="6666", + ) + assert ( + result is True + ), "Testcase {} : Default route with expected attributes is not found in BGP RIB \n Error: {}".format( + tc_name, result + ) + + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert ( + result is True + ), "Testcase {} : Default route with expected attributes is not found in BGP FIB \n Error: {}".format( + tc_name, result + ) + + step("Remove the as-path policy from the route-map") + input_dict_3 = { + "r1": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + "set": { + "path": { + "as_num": "6666", + "as_action": "prepend", + "delete": True, + } + }, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + "set": { + "path": { + "as_num": "6666", + "as_action": "prepend", + "delete": True, + } + }, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert ( + result is True + ), "Testcase {} : Failed to configure the route map \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the route policy (AS-Path) verify that as-path is removed in r2 " + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + ) + assert result is True, "Testcase {} : Failed ... ! \n Error: {}".format( + tc_name, result + ) + + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed .... !\n Error: {}".format( + tc_name, result + ) + + step("Delete the route-map ") + + delete_routemap = {"r1": {"route_maps": ["RMv4", "RMv6"]}} + result = delete_route_maps(tgen, delete_routemap) + assert ( + result is True + ), "Testcase {} : Failed to delete the route-map\n Error: {}".format( + tc_name, result + ) + + step( + "After deleting route-map , verify the default route in FIB and RIB are removed " + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + metric=0, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : After removing the route-map the default-route is not removed from R2 RIB\n Error: {}".format( + tc_name, result + ) + + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : After removing the route-map the default-route is not removed from R2 FIB \n Error: {}".format( + tc_name, result + ) + + step("Create route-map with with sequnce number 10 ") + input_dict_3 = { + "r1": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "10", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + "set": { + "path": { + "as_num": "9999", + "as_action": "prepend", + } + }, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "10", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + "set": { + "path": { + "as_num": "9999", + "as_action": "prepend", + } + }, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert ( + result is True + ), "Testcase {} : Failed to configure the route map \n Error: {}".format( + tc_name, result + ) + + step( + "After Configuring the route-map the dut is expected to receive the route policy (as-path) as 99999" + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + metric=0, + expected_aspath="9999", + ) + assert result is True, "Testcase {} : Failed...! \n Error: {}".format( + tc_name, result + ) + + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert result is True, "Testcase {} : Failed ...!\n Error: {}".format( + tc_name, result + ) + + step("Create another route-map with seq number less than the previous i. <10 ") + input_dict_3 = { + "r1": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "5", + "match": {"ipv4": {"prefix_lists": "Pv4"}}, + "set": { + "path": { + "as_num": "7777", + "as_action": "prepend", + } + }, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "5", + "match": {"ipv6": {"prefix_lists": "Pv6"}}, + "set": { + "path": { + "as_num": "7777", + "as_action": "prepend", + } + }, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert ( + result is True + ), "Testcase {} : Failed to configure the route map \n Error: {}".format( + tc_name, result + ) + + step( + "On creating new route-map the route-map with lower seq id should be considered " + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + metric=0, + expected_aspath="7777", + ) + assert ( + result is True + ), "Testcase {} : Route-map with lowest prefix is not considered \n Error: {}".format( + tc_name, result + ) + + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1, + expected=True, + ) + assert ( + result is True + ), "Testcase {} : Route-map with lowest prefix is not considered \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_default_originate_after_BGP_attributes_p1(request): + """ + "Verify different BGP attributes with default-originate route " + """ + tgen = get_topogen() + global BGP_CONVERGENCE + global topo + # test case name + tc_name = request.node.name + write_test_header(tc_name) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + step("Configure IPv4 and IPv6 , EBGP neighbor between R3 and R2") + step("Configure IPv4 and IPv6 IBGP neighbor between R3 and R4") + r0_local_as = topo['routers']['r0']['bgp']['local_as'] + r1_local_as = topo['routers']['r1']['bgp']['local_as'] + r2_local_as = topo['routers']['r2']['bgp']['local_as'] + r3_local_as = topo['routers']['r3']['bgp']['local_as'] + r4_local_as = topo['routers']['r4']['bgp']['local_as'] + input_dict = { + "r0": { + "bgp": { + "local_as": r0_local_as, + } + }, + "r1": { + "bgp": { + "local_as": r1_local_as, + } + }, + "r2": { + "bgp": { + "local_as": r2_local_as, + } + }, + "r3": { + "bgp": { + "local_as": 4000, + } + }, + "r4": { + "bgp": { + "local_as": 4000, + } + }, + } + result = modify_as_number(tgen, topo, input_dict) + + try: + assert result is True + except AssertionError: + logger.info("Expected behaviour: {}".format(result)) + logger.info("BGP config is not created because of invalid ASNs") + step("After changing the BGP AS Path Verify the BGP Convergence") + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Configure one IPv4 and one IPv6, Static route on R4 with next-hop as Null0 IPv4 route Sv41, IPv6 route Sv61 " + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("Verify IPv4 and IPv6 static routes configured on R4 in FIB") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure redistribute static knob on R4 , for R4 to R3 neighbor for IPv4 and IPv6 address family " + ) + redistribute_static = { + "r4": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify After configuring redistribute static , verify route received in BGP table of R3" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + NOTE = """Configure 2 IPv4 prefix-list Pv41 Pv42 and and 2 IPv6 prefix-list Pv61 Pv62 on R3 to match BGP IPv4 route Sv41, 200.1.1.1/24 , IPv6 route Sv61 and 200::1/64""" + step(NOTE) + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "Pv41": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv4"], + "action": "permit", + } + ], + "Pv42": [ + {"seqid": "1", "network": "200.1.1.1/24", "action": "permit"} + ], + }, + "ipv6": { + "Pv61": [ + { + "seqid": "1", + "network": NETWORK1_1["ipv6"], + "action": "permit", + } + ], + "Pv62": [ + {"seqid": " 1", "network": "200::1/64", "action": "permit"} + ], + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify IPv4 and IPv6 Prefix list got configured on R3") + input_dict = {"r3": {"prefix_lists": ["Pv41", "Pv61", "Pv42", "Pv62"]}} + result = verify_prefix_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure 2 sequence of route-map for IPv4 seq1 permit Pv41 and seq2 permit Pv42 and for IPv6 seq1 permit Pv61 , seq2 permit Pv62 on R3" + ) + input_dict_3 = { + "r3": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv4": {"prefix_lists": "Pv41"}}, + }, + { + "action": "permit", + "seq_id": "2", + "match": {"ipv4": {"prefix_lists": "Pv42"}}, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "match": {"ipv6": {"prefix_lists": "Pv61"}}, + }, + { + "action": "permit", + "seq_id": "2", + "match": {"ipv6": {"prefix_lists": "Pv62"}}, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Apply on route-map seq1 set as-path prepend to 200 and route-map seq2 set as-path prepend to 300 for IPv4 and IPv6 route-map " + ) + route_map = { + "r3": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "set": { + "path": { + "as_num": "200", + "as_action": "prepend", + } + } + + }, + { + "action": "permit", + "seq_id": "2", + "set": { + "path": { + "as_num": "300", + "as_action": "prepend", + } + } + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "set": { + "path": { + "as_num": "200", + "as_action": "prepend", + } + } + }, + { + "action": "permit", + "seq_id": "2", + "set": { + "path": { + "as_num": "300", + "as_action": "prepend", + } + } + }, + ], + } + } + } + + result = create_route_maps(tgen, route_map) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step( + " Configure default-originate with IPv4 and IPv6 route-map on R3 for R3-R2 IPv4 and IPv6 BGP neighbor" + ) + + local_as = get_dut_as_number(tgen, dut="r3") + default_originate_config = { + "r3": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv4"}}} + }, + "ipv6": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv6"}}} + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify IPv4 and IPv6 default route received on R2 with both the AS path on R2" + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + metric=0, + expected_aspath="4000 200", + ) + + step( + "Modify AS prepend path adding one more value 500 in route-map sequence 1 and 600 for route-map sequence 2 for IPv4 and IPv6 route-map" + ) + route_map = { + "r3": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "set": { + "path": { + "as_num": "500", + "as_action": "prepend", + } + } + + }, + { + "action": "permit", + "seq_id": "2", + "set": { + "path": { + "as_num": "600", + "as_action": "prepend", + } + } + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "set": { + "path": { + "as_num": "500", + "as_action": "prepend", + } + } + }, + { + "action": "permit", + "seq_id": "2", + "set": { + "path": { + "as_num": "600", + "as_action": "prepend", + } + } + }, + ], + } + } + } + + result = create_route_maps(tgen, route_map) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + + step("As path 500 added to IPv4 and IPv6 default -originate route received on R2") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + metric=0, + expected_aspath="4000 500", + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Apply on route-map seq1 set metric value to 70 and route-map seq2 set metric 80 IPv4 and IPv6 route-map" + ) + route_map = { + "r3": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "set": { + "metric": 70, + }, + }, + { + "action": "permit", + "seq_id": "2", + "set": { + "metric": 80, + }, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "set": { + "metric": 70, + }, + }, + { + "action": "permit", + "seq_id": "2", + "set": { + "metric": 80, + }, + }, + ], + } + } + } + + result = create_route_maps(tgen, route_map) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify Configured metric value received on R2 along with as-path for IPv4 and IPv6 default routes " + ) + + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "::/0"} + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + metric=70, + expected_aspath="4000 500", + ) + + + step( + "Modify route-map seq1 configure metric 50 and route-map seq2 configure metric 100 IPv4 and IPv6 route-map " + ) + route_map = { + "r3": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "set": { + "metric": 50, + }, + }, + { + "action": "permit", + "seq_id": "2", + "set": { + "metric": 100, + }, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "set": { + "metric": 50, + }, + }, + { + "action": "permit", + "seq_id": "2", + "set": { + "metric": 100, + }, + }, + ], + } + } + } + + result = create_route_maps(tgen, route_map) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify Configured metric value received on R2 along with as-path for IPv4 and IPv6 default routes " + ) + + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + metric=50, + expected_aspath="4000 500", + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Delete AS-prepend from IP4 and IPv6 route-map configured on R3 ") + route_map = { + "r3": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + + "set": { + "path": { + "as_num": "500", + "as_action": "prepend", + "delete": True, + }, + "delete": True, + }, + }, + { + "action": "permit", + "seq_id": "2", + "set": { + "path": { + "as_num": "600", + "as_action": "prepend", + "delete": True, + }, + "delete": True, + }, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "set": { + "path": { + "as_num": "500", + "as_action": "prepend", + "delete": True, + }, + "delete": True, + }, + }, + { + "action": "permit", + "seq_id": "2", + "set": { + "path": { + "as_num": "600", + "as_action": "prepend", + "delete": True, + }, + "delete": True, + }, + }, + ], + } + } + } + + result = create_route_maps(tgen, route_map) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify AS-prepend is deleted from default originate route and metric value only present on R2 for IPv4 and IPv6 default routes " + ) + + + + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + metric=50, + expected_aspath="4000", + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + + step("Delete metric value from IP4 and IPv6 route-map configured on R3 ") + route_map = { + "r3": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "set": {"metric": 50, "delete": True}, + }, + { + "action": "permit", + "seq_id": "2", + "set": {"metric": 100, "delete": True}, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "set": {"metric": 50, "delete": True}, + }, + { + "action": "permit", + "seq_id": "2", + "set": {"metric": 100, "delete": True}, + }, + ], + } + } + } + + result = create_route_maps(tgen, route_map) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify Metric value deleted from IPv4 and IPv6 default route on R2 ,verify default routes " + ) + + + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + metric=0, + expected_aspath="4000", + ) + + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + step("Change IPv4 and IPv6 , EBGP to IBGP neighbor between R3 and R2") + step("Change IPv4 and IPv6 IBGP to EBGP neighbor between R3 and R4") + r0_local_as = topo['routers']['r0']['bgp']['local_as'] + r1_local_as = topo['routers']['r1']['bgp']['local_as'] + r2_local_as = topo['routers']['r2']['bgp']['local_as'] + r3_local_as = topo['routers']['r3']['bgp']['local_as'] + r4_local_as = topo['routers']['r4']['bgp']['local_as'] + input_dict = { + "r0": { + "bgp": { + "local_as": r0_local_as, + } + }, + "r1": { + "bgp": { + "local_as": r1_local_as, + } + }, + + "r2": { + "bgp": { + "local_as": 1111, + } + }, + "r3": { + "bgp": { + "local_as": 1111, + } + }, + "r4": { + "bgp": { + "local_as": 5555, + } + }, + } + result = modify_as_number(tgen, topo, input_dict) + try: + assert result is True + except AssertionError: + logger.info("Expected behaviour: {}".format(result)) + logger.info("BGP config is not created because of invalid ASNs") + step("After changing the BGP AS Path Verify the BGP Convergence") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + step( + "Configure one IPv4 and one IPv6, Static route on R4 with next-hop as Null0 IPv4 route Sv41, IPv6 route Sv61 " + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("Verify IPv4 and IPv6 static routes configured on R4 in FIB") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure redistribute static knob on R4 , for R4 to R3 neighbor for IPv4 and IPv6 address family " + ) + redistribute_static = { + "r4": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, redistribute_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify After configuring redistribute static , verify route received in BGP table of R3" + ) + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + " Configure default-originate with IPv4 and IPv6 route-map on R3 for R3-R2 IPv4 and IPv6 BGP neighbor" + ) + local_as = get_dut_as_number(tgen, dut="r3") + default_originate_config = { + "r3": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv4"}}} + }, + "ipv6": { + "unicast": {"default_originate": {"r2": {"route_map": "RMv6"}}} + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, default_originate_config) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify IPv4 and IPv6 default route received on R2 with both the AS path on R2" + ) + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"} + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Configure local -preference to 50 on IPv4 and IPv6 route map seq1 and 60 on seq2" + ) + route_map = { + "r3": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "set": { + "locPrf": 50, + }, + }, + { + "action": "permit", + "seq_id": "2", + "set": { + "locPrf": 60, + }, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "set": { + "locPrf": 50, + }, + }, + { + "action": "permit", + "seq_id": "2", + "set": { + "locPrf": 60, + }, + }, + ], + } + } + } + + result = create_route_maps(tgen, route_map) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify Configured metric value received on R2 along with as-path for IPv4 and IPv6 default routes " + ) + + + + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + locPrf=50, + ) + + + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Modify local preference value to 150 on IPv4 and IPv6 route map seq1 and 160 on seq2" + ) + route_map = { + "r3": { + "route_maps": { + "RMv4": [ + { + "action": "permit", + "seq_id": "1", + "set": { + "locPrf": 150, + }, + }, + { + "action": "permit", + "seq_id": "2", + "set": { + "locPrf": 160, + }, + }, + ], + "RMv6": [ + { + "action": "permit", + "seq_id": "1", + "set": { + "locPrf": 150, + }, + }, + { + "action": "permit", + "seq_id": "2", + "set": { + "locPrf": 160, + }, + }, + ], + } + } + } + + result = create_route_maps(tgen, route_map) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify Modified local-preference value received on R2 for IPv4 and IPv6 default routes " + ) + + + + + DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "::/0"} + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + locPrf=150, + ) + + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + # updating the topology with the updated AS-Number to avoid conflict in con configuring the AS + updated_topo = topo + updated_topo['routers']['r0']['bgp']['local_as']=get_dut_as_number(tgen,"r0") + updated_topo['routers']['r1']['bgp']['local_as']=get_dut_as_number(tgen,"r1") + updated_topo['routers']['r2']['bgp']['local_as']=get_dut_as_number(tgen,"r2") + updated_topo['routers']['r3']['bgp']['local_as']=get_dut_as_number(tgen,"r3") + updated_topo['routers']['r4']['bgp']['local_as']=get_dut_as_number(tgen,"r4") + + step("Shut IPv4/IPv6 BGP neighbor from R4 ( R4-R3) using 'neighbor x.x.x.x shut' command ") + local_as = get_dut_as_number(tgen, dut="r4") + shut_neighbor = { + "r4": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {"shutdown":True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {"shutdown":True} + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, updated_topo, shut_neighbor) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + interface = topo['routers']['r3']['links']['r4']['interface'] + input_dict = { + "r1": { + "interface_list": [interface], + "status": "down" + } + } + + result = interface_status(tgen, topo, input_dict) + assert result is True, "Testcase {} : Shut down the interface failed ! \n Error: {}".format(tc_name, result) + + step("After shutting the interface verify the BGP convergence") + result = verify_bgp_convergence(tgen,topo,expected=False) + assert result is not True, "Testcase {} : Failed \n After shutting Down BGP convergence should Fail and return False \n Error: {}".format(tc_name, result) + + step("verify default route deleted from R2 ") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=False) + assert result is not True, "Testcase {} : Failed \n Error: After Shut down interface the default route is NOT expected but found in RIB -> {}".format( tc_name, result) + + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=False) + assert result is not True, "Testcase {} : Failed \n Error: After Shut down interface the default route is NOT expected but found in FIB -> {}".format( tc_name, result) + + + step("no Shut IPv4/IPv6 BGP neighbor from R4 ( R4-R3) using 'neighbor x.x.x.x shut' command ") + local_as = get_dut_as_number(tgen, dut="r4") + shut_neighbor = { + "r4": { + "bgp": { + "local_as": local_as, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {"shutdown":False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {"shutdown":False} + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, updated_topo, shut_neighbor) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + interface = topo['routers']['r3']['links']['r4']['interface'] + input_dict = { + "r1": { + "interface_list": [interface], + "status": "up" + } + } + + result = interface_status(tgen, topo, input_dict) + assert result is True, "Testcase {} : Bring up interface failed ! \n Error: {}".format(tc_name, result) + + step("After no shutting the interface verify the BGP convergence") + result = verify_bgp_convergence(tgen,topo,expected=True) + assert result is True, "Testcase {} : Failed \n After shutting Down BGP convergence should Fail and return False \n Error: {}".format(tc_name, result) + + step("After no shut neighbor , verify default route relearn on R2") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=True) + assert result is True, "Testcase {} : Failed \n Error: After no Shut down interface the default route is expected but found in RIB -> {}".format( tc_name, result) + + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected=True) + assert result is True, "Testcase {} : Failed \n Error: After Shut down interface the default route is expected but found in FIB -> {}".format( tc_name, result) + + + + step("Remove IPv4/IPv6 static route configure on R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "delete": True + } + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("Verify IPv4 and IPv6 static routes removed on R4 in FIB") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r4", static_routes_input, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("After removing static route , verify default route removed on R2") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected= False) + assert result is not True, "Testcase {} : Failed \n Error: After removing static the default route is NOT expected but found in RIB -> {}".format( tc_name, result) + + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected= False) + assert result is not True, "Testcase {} : Failed \n Error: After removing static the default route is NOT expected but found in FIB -> {}".format( tc_name, result) + + + step("Configuring the static route back in r4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("Verify IPv4 and IPv6 static routes configured on R4 in FIB") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input, expected=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r4", static_routes_input, expected=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("After adding static route back , verify default route learned on R2") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected= True) + assert result is True, "Testcase {} : Failed \n Error: After removing static the default route is expected but found in RIB -> {}".format( tc_name, result) + + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected= True) + assert result is True, "Testcase {} : Failed \n Error: After removing static the default route is expected but found in FIB -> {}".format( tc_name, result) + + step("Deactivate IPv4 and IPv6 neighbor configured from R4 ( R4-R3)") + + configure_bgp_on_r1 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {"deactivate": "ipv4"}}} + } + }, + + },"ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {"deactivate": "ipv6"}}} + } + }, + + } + } + } + } + } + result = create_router_bgp(tgen, updated_topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("After deactivating the BGP neighbor , verify default route removed on R2") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected= False) + assert result is not True, "Testcase {} : Failed \n Error: After Deactivating the BGP neighbor the default route is NOT expected but found in RIB -> {}".format( tc_name, result) + + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected= False) + assert result is not True, "Testcase {} : Failed \n Error: After Deactivating the BGP neighbor the default route is NOT expected but found in FIB -> {}".format( tc_name, result) + + step("Activate IPv4 and IPv6 neighbor configured from R4 ( R4-R3)") + + configure_bgp_on_r1 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {"activate": "ipv4"}}} + } + }, + + },"ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {"activate": "ipv6"}}} + } + }, + + } + } + } + } + } + result = create_router_bgp(tgen, updated_topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp convergence.") + bgp_convergence = verify_bgp_convergence(tgen, updated_topo) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + step("After Activating the BGP neighbor , verify default route learned on R2") + result = verify_rib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected= True) + assert result is True, "Testcase {} : Failed \n Error: After Deactivating the BGP neighbor the default route is expected but found in RIB -> {}".format( tc_name, result) + + result = verify_fib_default_route( + tgen, + topo, + dut="r2", + routes=DEFAULT_ROUTES, + expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3, + expected= True) + assert result is True, "Testcase {} : Failed \n Error: After Deactivating the BGP neighbor the default route is expected but found in FIB -> {}".format( tc_name, result) + write_test_footer(tc_name) + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_originate_timer/__init__.py b/tests/topotests/bgp_default_originate_timer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_default_originate_timer/r1/bgpd.conf b/tests/topotests/bgp_default_originate_timer/r1/bgpd.conf new file mode 100644 index 0000000..f2a1c90 --- /dev/null +++ b/tests/topotests/bgp_default_originate_timer/r1/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65001 + no bgp ebgp-requires-policy + bgp default-originate timer 3600 + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + address-family ipv4 + neighbor 192.168.1.2 default-originate route-map default + exit-address-family +! +bgp community-list standard r3 seq 5 permit 65003:1 +! +route-map default permit 10 + match community r3 +exit diff --git a/tests/topotests/bgp_default_originate_timer/r1/zebra.conf b/tests/topotests/bgp_default_originate_timer/r1/zebra.conf new file mode 100644 index 0000000..3692361 --- /dev/null +++ b/tests/topotests/bgp_default_originate_timer/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +interface r1-eth1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/bgp_default_originate_timer/r2/bgpd.conf b/tests/topotests/bgp_default_originate_timer/r2/bgpd.conf new file mode 100644 index 0000000..7ca65a9 --- /dev/null +++ b/tests/topotests/bgp_default_originate_timer/r2/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 +! diff --git a/tests/topotests/bgp_default_originate_timer/r2/zebra.conf b/tests/topotests/bgp_default_originate_timer/r2/zebra.conf new file mode 100644 index 0000000..0c95656 --- /dev/null +++ b/tests/topotests/bgp_default_originate_timer/r2/zebra.conf @@ -0,0 +1,4 @@ +! +interface r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_default_originate_timer/r3/bgpd.conf b/tests/topotests/bgp_default_originate_timer/r3/bgpd.conf new file mode 100644 index 0000000..0a37913 --- /dev/null +++ b/tests/topotests/bgp_default_originate_timer/r3/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 + address-family ipv4 unicast + redistribute connected route-map r1 + exit-address-family +! +route-map r1 permit 10 + set community 65003:1 +exit diff --git a/tests/topotests/bgp_default_originate_timer/r3/zebra.conf b/tests/topotests/bgp_default_originate_timer/r3/zebra.conf new file mode 100644 index 0000000..20801f9 --- /dev/null +++ b/tests/topotests/bgp_default_originate_timer/r3/zebra.conf @@ -0,0 +1,7 @@ +! +interface lo + ip address 10.10.10.10/32 +! +interface r3-eth0 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/bgp_default_originate_timer/test_bgp_default_originate_timer.py b/tests/topotests/bgp_default_originate_timer/test_bgp_default_originate_timer.py new file mode 100644 index 0000000..b2ba936 --- /dev/null +++ b/tests/topotests/bgp_default_originate_timer/test_bgp_default_originate_timer.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Check if `bgp default-originate timer` commands takes an effect: +1. Set bgp default-originate timer 3600 +2. No default route is advertised because the timer is running for 3600 seconds +3. We reduce it to 10 seconds +4. Default route is advertised +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_default_originate_timer(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + + def _bgp_default_received_from_r1(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast 0.0.0.0/0 json")) + expected = { + "paths": [ + { + "nexthops": [ + { + "hostname": "r1", + "ip": "192.168.1.1", + } + ], + } + ], + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_default_received_from_r1) + _, result = topotest.run_and_expect(test_func, not None, count=30, wait=1) + assert result is not None, "Seen default route received from r1, but should not" + + step("Set BGP default-originate timer to 10 seconds") + r1.vtysh_cmd( + """ + configure terminal + router bgp + bgp default-originate timer 10 + """ + ) + + step("Trigger BGP UPDATE from r3") + r3.vtysh_cmd( + """ + configure terminal + route-map r1 permit 10 + set metric 1 + """ + ) + + test_func = functools.partial(_bgp_default_received_from_r1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Did not see default route received from r1, but should" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_originate_withdraw/__init__.py b/tests/topotests/bgp_default_originate_withdraw/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_default_originate_withdraw/r1/bgpd.conf b/tests/topotests/bgp_default_originate_withdraw/r1/bgpd.conf new file mode 100644 index 0000000..6813b02 --- /dev/null +++ b/tests/topotests/bgp_default_originate_withdraw/r1/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + address-family ipv4 + neighbor 192.168.1.2 default-originate + exit-address-family +! diff --git a/tests/topotests/bgp_default_originate_withdraw/r1/zebra.conf b/tests/topotests/bgp_default_originate_withdraw/r1/zebra.conf new file mode 100644 index 0000000..3692361 --- /dev/null +++ b/tests/topotests/bgp_default_originate_withdraw/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +interface r1-eth1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/bgp_default_originate_withdraw/r2/bgpd.conf b/tests/topotests/bgp_default_originate_withdraw/r2/bgpd.conf new file mode 100644 index 0000000..60e6236 --- /dev/null +++ b/tests/topotests/bgp_default_originate_withdraw/r2/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + address-family ipv4 unicast + network 192.168.2.0/24 + exit-address-family +! diff --git a/tests/topotests/bgp_default_originate_withdraw/r2/zebra.conf b/tests/topotests/bgp_default_originate_withdraw/r2/zebra.conf new file mode 100644 index 0000000..0c95656 --- /dev/null +++ b/tests/topotests/bgp_default_originate_withdraw/r2/zebra.conf @@ -0,0 +1,4 @@ +! +interface r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_default_originate_withdraw/r3/bgpd.conf b/tests/topotests/bgp_default_originate_withdraw/r3/bgpd.conf new file mode 100644 index 0000000..547cf86 --- /dev/null +++ b/tests/topotests/bgp_default_originate_withdraw/r3/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 + address-family ipv4 unicast + network 0.0.0.0/0 + exit-address-family +! diff --git a/tests/topotests/bgp_default_originate_withdraw/r3/zebra.conf b/tests/topotests/bgp_default_originate_withdraw/r3/zebra.conf new file mode 100644 index 0000000..7ccdcfd --- /dev/null +++ b/tests/topotests/bgp_default_originate_withdraw/r3/zebra.conf @@ -0,0 +1,5 @@ +! +interface r3-eth0 + ip address 192.168.2.2/24 +! +ip route 0.0.0.0/0 Null0 diff --git a/tests/topotests/bgp_default_originate_withdraw/test_bgp_default_originate_withdraw.py b/tests/topotests/bgp_default_originate_withdraw/test_bgp_default_originate_withdraw.py new file mode 100644 index 0000000..e25f85a --- /dev/null +++ b/tests/topotests/bgp_default_originate_withdraw/test_bgp_default_originate_withdraw.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Check if bgpd do not crash if we use default-originate while +received a default route from the neighbor as well. 0.0.0.0/0 +MUST be kept in RIB even if we remove default-originate from +the neighbor. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_default_originate_with_default_received(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_default_received_from_r3(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 0.0.0.0/0 json")) + expected = { + "paths": [ + { + "nexthops": [ + { + "hostname": "r3", + "ip": "192.168.2.2", + } + ], + } + ], + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_default_received_from_r3) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see default route received from r3" + + def _bgp_advertised_default_originate_to_r2(): + output = json.loads( + r1.vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.1.2 advertised-routes json" + ) + ) + expected = { + "bgpOriginatingDefaultNetwork": "0.0.0.0/0", + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_advertised_default_originate_to_r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see default-originate route advertised to r2" + + step("Disable default-originate for r2") + r1.vtysh_cmd( + """ + configure + router bgp + address-family ipv4 unicast + no neighbor 192.168.1.2 default-originate + """ + ) + + def _bgp_advertised_default_from_r3_to_r2(): + output = json.loads( + r1.vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.1.2 advertised-routes json" + ) + ) + expected = { + "bgpOriginatingDefaultNetwork": None, + "advertisedRoutes": { + "0.0.0.0/0": { + "valid": True, + } + }, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_advertised_default_from_r3_to_r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see default route advertised to r2" + + step("Enable default-originate for r2") + r1.vtysh_cmd( + """ + configure + router bgp + address-family ipv4 unicast + neighbor 192.168.1.2 default-originate + do clear ip bgp * + """ + ) + + step("Check if default-originate route advertised to r2") + test_func = functools.partial(_bgp_advertised_default_originate_to_r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see default-originate route advertised to r2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_route/__init__.py b/tests/topotests/bgp_default_route/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_default_route/r1/bgpd.conf b/tests/topotests/bgp_default_route/r1/bgpd.conf new file mode 100644 index 0000000..8699d62 --- /dev/null +++ b/tests/topotests/bgp_default_route/r1/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + neighbor 192.168.255.2 default-originate + exit-address-family +! diff --git a/tests/topotests/bgp_default_route/r1/zebra.conf b/tests/topotests/bgp_default_route/r1/zebra.conf new file mode 100644 index 0000000..0a283c0 --- /dev/null +++ b/tests/topotests/bgp_default_route/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default_route/r2/bgpd.conf b/tests/topotests/bgp_default_route/r2/bgpd.conf new file mode 100644 index 0000000..6d1080c --- /dev/null +++ b/tests/topotests/bgp_default_route/r2/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_default_route/r2/zebra.conf b/tests/topotests/bgp_default_route/r2/zebra.conf new file mode 100644 index 0000000..606c17b --- /dev/null +++ b/tests/topotests/bgp_default_route/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default_route/test_bgp_default-originate.py b/tests/topotests/bgp_default_route/test_bgp_default-originate.py new file mode 100644 index 0000000..2463b05 --- /dev/null +++ b/tests/topotests/bgp_default_route/test_bgp_default-originate.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2019-2020 by +# Donatas Abraitis +# + +""" +Test if default-originate works without route-map. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_default_originate_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_check_if_received(): + output = json.loads( + tgen.gears["r2"].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json") + ) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_check_if_originated(): + output = json.loads(tgen.gears["r1"].vtysh_cmd("show ip bgp summary json")) + expected = {"ipv4Unicast": {"peers": {"192.168.255.2": {"pfxSnt": 1}}}} + return topotest.json_cmp(output, expected) + + def _bgp_default_route_is_valid(router): + output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) + expected = {"paths": [{"valid": True}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_if_received) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "No 0.0.0.0/0 at r2 from r1" + + test_func = functools.partial(_bgp_check_if_originated) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "No 0.0.0.0/0 from r1 to r2" + + test_func = functools.partial(_bgp_default_route_is_valid, tgen.gears["r2"]) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed to see 0.0.0.0/0 in r2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_route_route_map_match/__init__.py b/tests/topotests/bgp_default_route_route_map_match/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_default_route_route_map_match/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match/r1/bgpd.conf new file mode 100644 index 0000000..97b440f --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match/r1/bgpd.conf @@ -0,0 +1,17 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + network 192.168.13.0/24 route-map internal + neighbor 192.168.255.2 default-originate route-map default + exit-address-family +! +bgp community-list standard default seq 5 permit 65000:1 +! +route-map default permit 10 + match community default +! +route-map internal permit 10 + set community 65000:1 +! diff --git a/tests/topotests/bgp_default_route_route_map_match/r1/zebra.conf b/tests/topotests/bgp_default_route_route_map_match/r1/zebra.conf new file mode 100644 index 0000000..0a283c0 --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default_route_route_map_match/r2/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match/r2/bgpd.conf new file mode 100644 index 0000000..00c96cc --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match/r2/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_default_route_route_map_match/r2/zebra.conf b/tests/topotests/bgp_default_route_route_map_match/r2/zebra.conf new file mode 100644 index 0000000..606c17b --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default_route_route_map_match/test_bgp_default-originate_route-map_match.py b/tests/topotests/bgp_default_route_route_map_match/test_bgp_default-originate_route-map_match.py new file mode 100644 index 0000000..9dcb5a1 --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match/test_bgp_default-originate_route-map_match.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2019-2020 by +# Donatas Abraitis +# + +""" +Test if default-originate works with ONLY match operations. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_default_originate_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_default_route_is_valid(router): + output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) + expected = {"paths": [{"valid": True}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_default_route_is_valid, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert ( + result is None + ), 'Failed to see applied metric for default route in "{}"'.format(router) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_route_route_map_match2/__init__.py b/tests/topotests/bgp_default_route_route_map_match2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_default_route_route_map_match2/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match2/r1/bgpd.conf new file mode 100644 index 0000000..ee7a92f --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match2/r1/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + neighbor 192.168.255.2 default-originate route-map default + exit-address-family +! +ip prefix-list r2 permit 10.0.0.0/22 +! +route-map default permit 10 + match ip address prefix-list r2 +! diff --git a/tests/topotests/bgp_default_route_route_map_match2/r1/zebra.conf b/tests/topotests/bgp_default_route_route_map_match2/r1/zebra.conf new file mode 100644 index 0000000..e2c399e --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match2/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default_route_route_map_match2/r2/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match2/r2/bgpd.conf new file mode 100644 index 0000000..00c96cc --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match2/r2/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_default_route_route_map_match2/r2/zebra.conf b/tests/topotests/bgp_default_route_route_map_match2/r2/zebra.conf new file mode 100644 index 0000000..f355ab1 --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match2/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 10.0.0.1/22 +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py b/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py new file mode 100644 index 0000000..965d348 --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2021 by +# Donatas Abraitis +# + +""" +Test if default-originate works with conditional match. +If 10.0.0.0/22 is recived from r2, then we announce 0.0.0.0/0 +to r2. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_default_originate_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_default_route_is_valid(router): + output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) + expected = {"paths": [{"valid": True}]} + return topotest.json_cmp(output, expected) + + step("Converge network") + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed to see bgp convergence at r2" + + step("Withdraw 10.0.0.0/22 from R2") + router.vtysh_cmd( + "conf t\nrouter bgp\naddress-family ipv4\nno redistribute connected" + ) + + step("Check if we don't have 0.0.0.0/0 at R2") + test_func = functools.partial(_bgp_default_route_is_valid, router) + success, result = topotest.run_and_expect(test_func, not None, count=30, wait=0.5) + assert result is not None, "0.0.0.0/0 exists at r2" + + step("Announce 10.0.0.0/22 from R2") + router.vtysh_cmd("conf t\nrouter bgp\naddress-family ipv4\nredistribute connected") + + step("Check if we have 0.0.0.0/0 at R2") + test_func = functools.partial(_bgp_default_route_is_valid, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "0.0.0.0/0 does not exist at r2" + + step("Withdraw 10.0.0.0/22 from R2 again") + router.vtysh_cmd( + "conf t\nrouter bgp\naddress-family ipv4\nno redistribute connected" + ) + + step("Check if we don't have 0.0.0.0/0 at R2 again") + test_func = functools.partial(_bgp_default_route_is_valid, router) + success, result = topotest.run_and_expect(test_func, not None, count=30, wait=0.5) + assert result is not None, "0.0.0.0/0 exists at r2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_route_route_map_match_set/__init__.py b/tests/topotests/bgp_default_route_route_map_match_set/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf new file mode 100644 index 0000000..32ac7c5 --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf @@ -0,0 +1,19 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + network 192.168.13.0/24 route-map internal + neighbor 192.168.255.2 default-originate route-map default + exit-address-family +! +bgp community-list standard default seq 5 permit 65000:1 +! +route-map default permit 10 + match community default + set metric 123 + set as-path prepend 65000 65000 65000 +! +route-map internal permit 10 + set community 65000:1 +! diff --git a/tests/topotests/bgp_default_route_route_map_match_set/r1/zebra.conf b/tests/topotests/bgp_default_route_route_map_match_set/r1/zebra.conf new file mode 100644 index 0000000..0a283c0 --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match_set/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default_route_route_map_match_set/r2/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match_set/r2/bgpd.conf new file mode 100644 index 0000000..00c96cc --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match_set/r2/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_default_route_route_map_match_set/r2/zebra.conf b/tests/topotests/bgp_default_route_route_map_match_set/r2/zebra.conf new file mode 100644 index 0000000..606c17b --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match_set/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py b/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py new file mode 100644 index 0000000..f94620b --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2020 by +# Donatas Abraitis +# + +""" +Test if default-originate works with match operations. +And verify if set operations work as well. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_default_originate_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_default_route_has_metric(router): + output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) + expected = { + "paths": [ + { + "aspath": {"string": "65000 65000 65000 65000"}, + "metric": 123, + "community": None, + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_default_route_has_metric, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert ( + result is None + ), 'Failed to see applied metric for default route in "{}"'.format(router) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_route_route_map_set/__init__.py b/tests/topotests/bgp_default_route_route_map_set/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf new file mode 100644 index 0000000..6f6d394 --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + neighbor 192.168.255.2 default-originate route-map default + exit-address-family +! +route-map default permit 10 + set metric 123 + set as-path prepend 65000 65000 65000 +! diff --git a/tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf b/tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf new file mode 100644 index 0000000..0a283c0 --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf b/tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf new file mode 100644 index 0000000..00c96cc --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_default_route_route_map_set/r2/zebra.conf b/tests/topotests/bgp_default_route_route_map_set/r2/zebra.conf new file mode 100644 index 0000000..606c17b --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_set/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py b/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py new file mode 100644 index 0000000..3bd900b --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2019-2020 by +# Donatas Abraitis +# + +""" +Test if default-originate works with ONLY set operations. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_default_originate_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_default_route_has_metric(router): + output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) + expected = { + "paths": [{"aspath": {"string": "65000 65000 65000 65000"}, "metric": 123}] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_default_route_has_metric, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert ( + result is None + ), 'Failed to see applied metric for default route in "{}"'.format(router) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_disable_addpath_rx/__init__.py b/tests/topotests/bgp_disable_addpath_rx/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_disable_addpath_rx/r1/bgpd.conf b/tests/topotests/bgp_disable_addpath_rx/r1/bgpd.conf new file mode 100644 index 0000000..44b009e --- /dev/null +++ b/tests/topotests/bgp_disable_addpath_rx/r1/bgpd.conf @@ -0,0 +1,10 @@ +! +router bgp 65001 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers connect 5 + address-family ipv4 unicast + neighbor 192.168.1.2 disable-addpath-rx + exit-address-family +! diff --git a/tests/topotests/bgp_disable_addpath_rx/r1/zebra.conf b/tests/topotests/bgp_disable_addpath_rx/r1/zebra.conf new file mode 100644 index 0000000..b29940f --- /dev/null +++ b/tests/topotests/bgp_disable_addpath_rx/r1/zebra.conf @@ -0,0 +1,4 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_disable_addpath_rx/r2/bgpd.conf b/tests/topotests/bgp_disable_addpath_rx/r2/bgpd.conf new file mode 100644 index 0000000..8274e3f --- /dev/null +++ b/tests/topotests/bgp_disable_addpath_rx/r2/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 65002 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers connect 5 + neighbor 192.168.2.3 remote-as external + neighbor 192.168.2.3 timers connect 5 + neighbor 192.168.2.4 remote-as external + neighbor 192.168.2.4 timers connect 5 + address-family ipv4 unicast + neighbor 192.168.1.1 addpath-tx-all-paths + exit-address-family +! diff --git a/tests/topotests/bgp_disable_addpath_rx/r2/zebra.conf b/tests/topotests/bgp_disable_addpath_rx/r2/zebra.conf new file mode 100644 index 0000000..e4a9074 --- /dev/null +++ b/tests/topotests/bgp_disable_addpath_rx/r2/zebra.conf @@ -0,0 +1,7 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! +int r2-eth1 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/bgp_disable_addpath_rx/r3/bgpd.conf b/tests/topotests/bgp_disable_addpath_rx/r3/bgpd.conf new file mode 100644 index 0000000..98eb2e1 --- /dev/null +++ b/tests/topotests/bgp_disable_addpath_rx/r3/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65003 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers connect 5 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_disable_addpath_rx/r3/zebra.conf b/tests/topotests/bgp_disable_addpath_rx/r3/zebra.conf new file mode 100644 index 0000000..417a484 --- /dev/null +++ b/tests/topotests/bgp_disable_addpath_rx/r3/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.16.254/32 +! +int r3-eth0 + ip address 192.168.2.3/24 +! diff --git a/tests/topotests/bgp_disable_addpath_rx/r4/bgpd.conf b/tests/topotests/bgp_disable_addpath_rx/r4/bgpd.conf new file mode 100644 index 0000000..68245c4 --- /dev/null +++ b/tests/topotests/bgp_disable_addpath_rx/r4/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65004 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers connect 5 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_disable_addpath_rx/r4/zebra.conf b/tests/topotests/bgp_disable_addpath_rx/r4/zebra.conf new file mode 100644 index 0000000..241e386 --- /dev/null +++ b/tests/topotests/bgp_disable_addpath_rx/r4/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.16.254/32 +! +int r4-eth0 + ip address 192.168.2.4/24 +! diff --git a/tests/topotests/bgp_disable_addpath_rx/test_disable_addpath_rx.py b/tests/topotests/bgp_disable_addpath_rx/test_disable_addpath_rx.py new file mode 100644 index 0000000..70562ce --- /dev/null +++ b/tests/topotests/bgp_disable_addpath_rx/test_disable_addpath_rx.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2021 by +# Donatas Abraitis +# + +""" +Test if AddPath RX direction is not negotiated via AddPath capability. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_disable_addpath_rx(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + step( + "Check if r2 advertised only 2 paths to r1 (despite addpath-tx-all-paths enabled on r2)." + ) + + def check_bgp_advertised_routes(router): + output = json.loads( + router.vtysh_cmd( + "show bgp ipv4 unicast neighbor 192.168.1.1 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "172.16.16.254/32": { + "addrPrefix": "172.16.16.254", + "prefixLen": 32, + }, + "192.168.2.0/24": { + "addrPrefix": "192.168.2.0", + "prefixLen": 24, + }, + }, + "totalPrefixCounter": 2, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(check_bgp_advertised_routes, r2) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "AddPath TX not working." + + step("Check if AddPath RX is disabled on r1 and we receive only 2 paths.") + + def check_bgp_disabled_addpath_rx(router): + output = json.loads(router.vtysh_cmd("show bgp neighbor 192.168.1.2 json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "addPath": { + "ipv4Unicast": {"txReceived": True, "rxReceived": True} + }, + }, + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(check_bgp_disabled_addpath_rx, r1) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "AddPath RX advertised, but should not." + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_distance_change/__init__.py b/tests/topotests/bgp_distance_change/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_distance_change/bgp_admin_dist.json b/tests/topotests/bgp_distance_change/bgp_admin_dist.json new file mode 100755 index 0000000..e6a20a6 --- /dev/null +++ b/tests/topotests/bgp_distance_change/bgp_admin_dist.json @@ -0,0 +1,402 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + }, + "static_routes": [ + { + "network": "192.168.22.1/32", + "no_of_ip": 2, + "next_hop": "10.0.0.2" + }, + { + "network": "fc07:1::1/128", + "no_of_ip": 2, + "next_hop": "fd00::2" + }, + { + "network": "192.168.21.1/32", + "no_of_ip": 2, + "next_hop": "blackhole" + }, + { + "network": "fc07:150::1/128", + "no_of_ip": 2, + "next_hop": "blackhole" + } + ] + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r5": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + }, + "static_routes": [ + { + "network": "192.168.20.1/32", + "no_of_ip": 2, + "next_hop": "blackhole" + }, + { + "network": "fc07:50::1/128", + "no_of_ip": 2, + "next_hop": "blackhole" + }, + { + "network": "192.168.21.1/32", + "no_of_ip": 2, + "next_hop": "blackhole" + }, + { + "network": "fc07:150::1/128", + "no_of_ip": 2, + "next_hop": "blackhole" + } + ] + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json b/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json new file mode 100755 index 0000000..5528b59 --- /dev/null +++ b/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json @@ -0,0 +1,404 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + + "r2": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + } + }, + "vrfs": [ + { + "name": "RED", + "id": "1" + } + ], + "bgp": [{ + "local_as": "100", + "vrf": "RED", + + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + }], + "static_routes": [ + { + "network": "192.168.22.1/32", + "no_of_ip": 2, + "next_hop": "10.0.0.2", + "vrf": "RED" + }, + { + "network": "fc07:1::1/128", + "no_of_ip": 2, + "next_hop": "fd00::2", + "vrf": "RED" + }, + { + "network": "192.168.21.1/32", + "no_of_ip": 2, + "next_hop": "blackhole", + "vrf": "RED" + }, + { + "network": "fc07:150::1/128", + "no_of_ip": 2, + "next_hop": "blackhole", + "vrf": "RED" + } + ] + }, + "r2": { + "links": { + + "r1": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + } + }, + "vrfs": [ + { + "name": "RED", + "id": "1" + } + ], + "bgp": [{ + "local_as": "100", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + }] + }, + "r3": { + "links": { + + "r1": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + }, + "r5": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + } + }, + "vrfs": [ + { + "name": "RED", + "id": "1" + } + ], + "bgp": [{ + "local_as": "100", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + }] + }, + "r4": { + "links": { + + "r3": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + } + }, + "vrfs": [ + { + "name": "RED", + "id": "1" + } + ], + "bgp": [{ + "local_as": "200", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + }] + }, + "r5": { + "links": { + + "r3": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + } + }, + "vrfs": [ + { + "name": "RED", + "id": "1" + } + ], + "bgp": [{ + "local_as": "300", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + }], + "static_routes": [ + { + "network": "192.168.20.1/32", + "no_of_ip": 2, + "next_hop": "blackhole", + "vrf": "RED" + }, + { + "network": "fc07:50::1/128", + "no_of_ip": 2, + "next_hop": "blackhole", + "vrf": "RED" + }, + { + "network": "192.168.21.1/32", + "no_of_ip": 2, + "next_hop": "blackhole", + "vrf": "RED" + }, + { + "network": "fc07:150::1/128", + "no_of_ip": 2, + "next_hop": "blackhole", + "vrf": "RED" + } + ] + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_distance_change/r1/bgpd.conf b/tests/topotests/bgp_distance_change/r1/bgpd.conf new file mode 100644 index 0000000..07cfe2e --- /dev/null +++ b/tests/topotests/bgp_distance_change/r1/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + exit-address-family +! diff --git a/tests/topotests/bgp_distance_change/r1/zebra.conf b/tests/topotests/bgp_distance_change/r1/zebra.conf new file mode 100644 index 0000000..6e9b0b4 --- /dev/null +++ b/tests/topotests/bgp_distance_change/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_distance_change/r2/bgpd.conf b/tests/topotests/bgp_distance_change/r2/bgpd.conf new file mode 100644 index 0000000..9cd86dc --- /dev/null +++ b/tests/topotests/bgp_distance_change/r2/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_distance_change/r2/zebra.conf b/tests/topotests/bgp_distance_change/r2/zebra.conf new file mode 100644 index 0000000..93e3590 --- /dev/null +++ b/tests/topotests/bgp_distance_change/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_distance_change/test_bgp_admin_dist.py b/tests/topotests/bgp_distance_change/test_bgp_admin_dist.py new file mode 100755 index 0000000..0bd3d28 --- /dev/null +++ b/tests/topotests/bgp_distance_change/test_bgp_admin_dist.py @@ -0,0 +1,1269 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +import sys +import time +import pytest +import inspect +import os + + +"""Following tests are covered to test bgp admin distance functionality. +TC_1: + Verify bgp admin distance functionality when static route is + configured same as ebgp learnt route + +TC_2: + Verify ebgp admin distance functionality with ECMP. + +TC_3: + Verify ibgp admin distance functionality when static route is + configured same as bgp learnt route. +TC_4: + Verify ibgp admin distance functionality with ECMP. + +TC_7: Chaos - Verify bgp admin distance functionality with chaos. +""" + +################################# +# TOPOLOGY +################################# +""" + + +-------+ + +--------- | R2 | + | +-------+ + |iBGP | + +-------+ | + | R1 | |iBGP + +-------+ | + | | + | iBGP +-------+ eBGP +-------+ + +---------- | R3 |----------| R4 | + +-------+ +-------+ + | + |eBGP + | + +-------+ + | R5 | + +-------+ + + +""" + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +# Required to instantiate the topology builder class. +from lib.common_config import ( + start_topology, + write_test_header, + step, + write_test_footer, + create_static_routes, + verify_rib, + create_route_maps, + create_prefix_lists, + check_address_types, + reset_config_on_routers, + check_router_status, + stop_router, + kill_router_daemons, + start_router_daemons, + start_router, + get_frr_ipv6_linklocal, + verify_fib_routes, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_best_path_as_per_admin_distance, + clear_bgp, +) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json +from lib.topolog import logger + +# Global variables +topo = None +bgp_convergence = False +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +NETWORK = { + "ipv4": [ + "192.168.20.1/32", + "192.168.20.2/32", + "192.168.21.1/32", + "192.168.21.2/32", + "192.168.22.1/32", + "192.168.22.2/32", + ], + "ipv6": [ + "fc07:50::1/128", + "fc07:50::2/128", + "fc07:150::1/128", + "fc07:150::2/128", + "fc07:1::1/128", + "fc07:1::2/128", + ], +} + +ADDR_TYPES = check_address_types() + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global topo + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_admin_dist.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global bgp_convergence + global ADDR_TYPES + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format( + bgp_convergence + ) + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """teardown_module. + + Teardown the pytest environment. + * `mod`: module name + """ + logger.info("Running teardown_module to delete topology") + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# Tests starting +##################################################### +def test_bgp_admin_distance_ebgp_ecmp_p0(): + """ + TC: 2 + Verify ebgp admin distance functionality with ECMP. + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipping test case because of BGP Convergence failure at setup") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure base config as per the topology") + reset_config_on_routers(tgen) + + step("Configure static route in R4 and R5, redistribute in bgp") + + for addr_type in ADDR_TYPES: + + input_dict = { + "r4": { + "static_routes": [{"network": NETWORK[addr_type], "next_hop": "Null0"}] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + input_dict = { + "r5": { + "static_routes": [{"network": NETWORK[addr_type], "next_hop": "Null0"}] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that route is learnt in DUT via ebgp") + + # Verifying RIB routes + protocol = "bgp" + input_dict = topo["routers"] + dut = "r3" + nhop = {"ipv4": [], "ipv6": []} + nhop["ipv4"].append(topo["routers"]["r4"]["links"]["r3"]["ipv4"].split("/")[0]) + nhop["ipv4"].append(topo["routers"]["r5"]["links"]["r3"]["ipv4"].split("/")[0]) + nhop["ipv6"].append(get_frr_ipv6_linklocal(tgen, "r4", "r3-r4-eth1")) + nhop["ipv6"].append(get_frr_ipv6_linklocal(tgen, "r5", "r1-r3-eth1")) + + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + {"network": NETWORK[addr_type][0], "next_hop": "Null0"} + ] + } + } + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Configure the static route in R3 (Dut).") + + for addr_type in ADDR_TYPES: + + input_dict = { + "r3": { + "static_routes": [ + {"network": NETWORK[addr_type][0], "next_hop": "Null0"} + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that static route is selected as best route in zebra.") + + # Verifying RIB routes + protocol = "static" + dut = "r3" + + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + {"network": NETWORK[addr_type][0], "next_hop": "Null0"} + ] + } + } + + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step(" Configure the admin distance of 254 to static route in R3.") + + for addr_type in ADDR_TYPES: + + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type][0], + "next_hop": "Null0", + "admin_distance": 254, + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that bgp routes are selected as best routes in zebra.") + protocol = "bgp" + dut = "r3" + + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + {"network": NETWORK[addr_type][0], "next_hop": "Null0"} + ] + } + } + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + input_dict_1 = { + "r3": { + "bgp": { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 254, "ibgp": 254, "local": 254} + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 254, "ibgp": 254, "local": 254} + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that bgp routes are selected as best routes in zebra.") + # Verifying RIB routes + protocol = "bgp" + dut = "r3" + + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + {"network": NETWORK[addr_type][0], "next_hop": "Null0"} + ] + } + } + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Configure bgp admin distance 10 with CLI in dut.") + input_dict_1 = { + "r3": { + "bgp": { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": {"distance": {"ebgp": 10, "ibgp": 254, "local": 254}} + }, + "ipv6": { + "unicast": {"distance": {"ebgp": 10, "ibgp": 254, "local": 254}} + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify ebgp routes have admin distance of 10 in dut.") + + protocol = "bgp" + input_dict = topo["routers"] + dut = "r3" + + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + {"network": NETWORK[addr_type][0], "next_hop": "Null0"} + ] + } + } + result4 = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, admin_distance=10 + ) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step( + "Configure route map with weight as 200 and apply to one of the " + "neighbor (R4 neighbor)." + ) + + # Create Prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_ls_1": [ + { + "seqid": 10, + "network": NETWORK["ipv4"][0], + "le": "32", + "action": "permit", + } + ] + }, + "ipv6": { + "pf_ls_1_ipv6": [ + { + "seqid": 100, + "network": NETWORK["ipv6"][0], + "le": "128", + "action": "permit", + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + input_dict_3 = { + "r3": { + "route_maps": { + "RMAP_WEIGHT": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_ls_1"}}, + "set": {"weight": 200}, + }, + { + "action": "permit", + "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}}, + "set": {"weight": 200}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "RMAP_WEIGHT", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "RMAP_WEIGHT", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that bgp route is selected as best on by zebra in r3.") + + protocol = "bgp" + dut = "r3" + + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + {"network": NETWORK[addr_type][0], "next_hop": "Null0"} + ] + } + } + result4 = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, admin_distance=10 + ) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Static route should not be selected as best route.") + protocol = "static" + dut = "r3" + + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + {"network": NETWORK[addr_type][0], "next_hop": "Null0"} + ] + } + } + result4 = verify_fib_routes( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result4 is not True + ), "Testcase {} : Failed. Wrong route is selected as best route.\n Error: {}".format( + tc_name, result4 + ) + + step("Reconfigure the static route without admin distance") + + for addr_type in ADDR_TYPES: + + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type][0], + "next_hop": "Null0", + "admin_distance": 254, + "delete": True, + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + {"network": NETWORK[addr_type][0], "next_hop": "Null0"} + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that static route is installed as best route.") + protocol = "static" + dut = "r3" + + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + {"network": NETWORK[addr_type][0], "next_hop": "Null0"} + ] + } + } + result4 = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, fib=True + ) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Unconfigure the static route in R3.") + + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type][0], + "next_hop": "Null0", + "delete": True, + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that bgp route is selected as best on by zebra in r3.") + + protocol = "bgp" + dut = "r3" + + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + {"network": NETWORK[addr_type][0], "next_hop": "Null0"} + ] + } + } + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Un configure the route map on R3.") + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "RMAP_WEIGHT", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "RMAP_WEIGHT", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify bgp routes installed in zebra.") + + # Verifying RIB routes + protocol = "bgp" + input_dict = topo["routers"] + dut = "r3" + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + {"network": NETWORK[addr_type][0], "next_hop": "Null0"} + ] + } + } + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + write_test_footer(tc_name) + + +def test_bgp_admin_distance_ibgp_p0(): + """ + TC: 3 + Verify bgp admin distance functionality when static route is + configured same as ibgp learnt route + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipping test case because of BGP Convergence failure at setup") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure base config as per the topology") + reset_config_on_routers(tgen) + + step("Configure bgp admin distance 200 with CLI in dut.") + + input_dict_1 = { + "r3": { + "bgp": { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200} + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200} + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp routes have admin distance of 200 in dut.") + # Verifying best path + dut = "r3" + attribute = "admin_distance" + + input_dict = { + "ipv4": { + "r3": { + "static_routes": [ + { + "network": "192.168.22.1/32", + "admin_distance": 200, + }, + { + "network": "192.168.22.2/32", + "admin_distance": 200, + }, + ] + } + }, + "ipv6": { + "r3": { + "static_routes": [ + { + "network": "fc07:1::1/128", + "admin_distance": 200, + }, + { + "network": "fc07:1::2/128", + "admin_distance": 200, + }, + ] + } + }, + } + + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_admin_distance( + tgen, addr_type, dut, input_dict[addr_type], attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Modify the admin distance value to 150.") + + input_dict_1 = { + "r3": { + "bgp": { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 150, "ibgp": 150, "local": 150} + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 150, "ibgp": 150, "local": 150} + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp routes have admin distance of 150 in dut.") + # Verifying best path + dut = "r3" + attribute = "admin_distance" + + input_dict = { + "ipv4": { + "r3": { + "static_routes": [ + { + "network": "192.168.22.1/32", + "admin_distance": 150, + }, + { + "network": "192.168.22.2/32", + "admin_distance": 150, + }, + ] + } + }, + "ipv6": { + "r3": { + "static_routes": [ + { + "network": "fc07:1::1/128", + "admin_distance": 150, + }, + { + "network": "fc07:1::2/128", + "admin_distance": 150, + }, + ] + } + }, + } + + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_admin_distance( + tgen, addr_type, dut, input_dict[addr_type], attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Un configure the admin distance value on DUT") + + input_dict_1 = { + "r3": { + "bgp": { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "distance": { + "ebgp": 150, + "ibgp": 150, + "local": 150, + "delete": True, + } + } + }, + "ipv6": { + "unicast": { + "distance": { + "ebgp": 150, + "ibgp": 150, + "local": 150, + "delete": True, + } + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp routes have default admin distance in dut.") + # Verifying best path + dut = "r3" + attribute = "admin_distance" + + input_dict = { + "ipv4": { + "r3": { + "static_routes": [ + { + "network": "192.168.22.1/32", + "admin_distance": 20, + }, + { + "network": "192.168.22.2/32", + "admin_distance": 20, + }, + ] + } + }, + "ipv6": { + "r3": { + "static_routes": [ + { + "network": "fc07:1::1/128", + "admin_distance": 20, + }, + { + "network": "fc07:1::2/128", + "admin_distance": 20, + }, + ] + } + }, + } + + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_admin_distance( + tgen, addr_type, dut, input_dict[addr_type], attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Learn the same route via ebgp and ibgp peer. Configure admin " + "distance of 200 in DUT for both ebgp and ibgp peer. " + ) + + step("Verify that ebgp route is preferred over ibgp.") + + # Verifying RIB routes + protocol = "bgp" + input_dict = topo["routers"] + + for addr_type in ADDR_TYPES: + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Configure static route Without any admin distance") + + for addr_type in ADDR_TYPES: + + input_dict = { + "r3": { + "static_routes": [{"network": NETWORK[addr_type], "next_hop": "Null0"}] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that zebra selects static route.") + protocol = "static" + + for addr_type in ADDR_TYPES: + + input_dict = { + "r3": { + "static_routes": [{"network": NETWORK[addr_type], "next_hop": "Null0"}] + } + } + + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Configure static route with admin distance of 253") + for addr_type in ADDR_TYPES: + + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": "Null0", + "admin_distance": 253, + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that zebra selects bgp route.") + protocol = "bgp" + + for addr_type in ADDR_TYPES: + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Configure admin distance of 254 in bgp for route.") + + input_dict_1 = { + "r3": { + "bgp": { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 254, "ibgp": 254, "local": 254} + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 254, "ibgp": 254, "local": 254} + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that zebra selects static route.") + protocol = "static" + + for addr_type in ADDR_TYPES: + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Delete the static route.") + for addr_type in ADDR_TYPES: + + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": "Null0", + "admin_distance": 253, + "delete": True, + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that zebra selects bgp route.") + protocol = "bgp" + + for addr_type in ADDR_TYPES: + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + write_test_footer(tc_name) + + +def test_bgp_admin_distance_chaos_p2(): + """ + TC: 7 + Chaos - Verify bgp admin distance functionality with chaos. + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipping test case because of BGP Convergence failure at setup") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure base config as per the topology") + reset_config_on_routers(tgen) + + step("Configure bgp admin distance 200 with CLI in dut.") + + input_dict_1 = { + "r3": { + "bgp": { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200} + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200} + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp routes have admin distance of 200 in dut.") + # Verifying best path + dut = "r3" + attribute = "admin_distance" + + input_dict = { + "ipv4": { + "r3": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "admin_distance": 200, + }, + { + "network": NETWORK["ipv4"][1], + "admin_distance": 200, + }, + ] + } + }, + "ipv6": { + "r3": { + "static_routes": [ + { + "network": NETWORK["ipv6"][0], + "admin_distance": 200, + }, + { + "network": NETWORK["ipv6"][1], + "admin_distance": 200, + }, + ] + } + }, + } + + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_admin_distance( + tgen, addr_type, dut, input_dict[addr_type], attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Restart frr on R3") + stop_router(tgen, "r3") + start_router(tgen, "r3") + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step("Verify ebgp and ibgp routes have admin distance of 200 in dut.") + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_admin_distance( + tgen, addr_type, dut, input_dict[addr_type], attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Restart bgpd process on R3") + kill_router_daemons(tgen, "r3", ["bgpd"]) + start_router_daemons(tgen, "r3", ["bgpd"]) + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step("Verify ebgp and ibgp routes have admin distance of 200 in dut.") + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_admin_distance( + tgen, addr_type, dut, input_dict[addr_type], attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Clear BGP") + for rtr in topo["routers"]: + clear_bgp(tgen, "ipv4", rtr) + clear_bgp(tgen, "ipv6", rtr) + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step("Verify that zebra selects bgp route.") + protocol = "bgp" + + for addr_type in ADDR_TYPES: + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_distance_change/test_bgp_admin_dist_vrf.py b/tests/topotests/bgp_distance_change/test_bgp_admin_dist_vrf.py new file mode 100755 index 0000000..bf30f2e --- /dev/null +++ b/tests/topotests/bgp_distance_change/test_bgp_admin_dist_vrf.py @@ -0,0 +1,887 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +import sys +import time +import pytest +import inspect +import os + +"""Following tests are covered to test bgp admin distance functionality. +TC_5: + Verify bgp admin distance functionality when static route is configured + same as bgp learnt route in user vrf. + +TC_6: Verify bgp admin distance functionality with ECMP in user vrf. + +TC_7: + Verify bgp admin distance functionality when routes are + imported between VRFs. +""" + +################################# +# TOPOLOGY +################################# +""" + + +-------+ + +--------- | R2 | + | +-------+ + |iBGP | + +-------+ | + | R1 | |iBGP + +-------+ | + | | + | iBGP +-------+ eBGP +-------+ + +---------- | R3 |----------| R4 | + +-------+ +-------+ + | + |eBGP + | + +-------+ + | R5 | + +-------+ + + +""" + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +# Required to instantiate the topology builder class. +from lib.common_config import ( + start_topology, + write_test_header, + step, + write_test_footer, + create_static_routes, + verify_rib, + check_address_types, + reset_config_on_routers, + check_router_status, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_best_path_as_per_admin_distance, +) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json +from lib.topolog import logger + +# Global variables +topo = None +bgp_convergence = False +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +NETWORK = { + "ipv4": [ + "192.168.20.1/32", + "192.168.20.2/32", + "192.168.21.1/32", + "192.168.21.2/32", + "192.168.22.1/32", + "192.168.22.2/32", + ], + "ipv6": [ + "fc07:50::1/128", + "fc07:50::2/128", + "fc07:150::1/128", + "fc07:150::2/128", + "fc07:1::1/128", + "fc07:1::2/128", + ], +} +ADDR_TYPES = check_address_types() + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global topo + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_admin_dist_vrf.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global bgp_convergence + global ADDR_TYPES + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format( + bgp_convergence + ) + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """teardown_module. + + Teardown the pytest environment. + * `mod`: module name + """ + logger.info("Running teardown_module to delete topology") + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# Tests starting +##################################################### + + +def test_bgp_admin_distance_ebgp_vrf_p0(): + """ + TC: 5 + Verify bgp admin distance functionality when static route is + configured same as ebgp learnt route + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipping test case because of BGP Convergence failure at setup") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure base config as per the topology") + reset_config_on_routers(tgen) + + step("Configure bgp admin distance 200 with CLI in dut.") + + input_dict_1 = { + "r3": { + "bgp": [ + { + "vrf": "RED", + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200} + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200} + } + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp routes have admin distance of 200 in dut.") + # Verifying best path + dut = "r3" + attribute = "admin_distance" + + input_dict = { + "ipv4": { + "r3": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "admin_distance": 200, + "vrf": "RED", + }, + { + "network": NETWORK["ipv4"][1], + "admin_distance": 200, + "vrf": "RED", + }, + ] + } + }, + "ipv6": { + "r3": { + "static_routes": [ + { + "network": NETWORK["ipv6"][0], + "admin_distance": 200, + "vrf": "RED", + }, + { + "network": NETWORK["ipv6"][1], + "admin_distance": 200, + "vrf": "RED", + }, + ] + } + }, + } + + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_admin_distance( + tgen, addr_type, dut, input_dict[addr_type], attribute, vrf="RED" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Modify the admin distance value to 150.") + + input_dict_1 = { + "r3": { + "bgp": [ + { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 150, "ibgp": 150, "local": 150} + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 150, "ibgp": 150, "local": 150} + } + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp routes have admin distance of 150 in dut.") + # Verifying best path + dut = "r3" + attribute = "admin_distance" + + input_dict = { + "ipv4": { + "r3": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "admin_distance": 150, + "vrf": "RED", + }, + { + "network": NETWORK["ipv4"][1], + "admin_distance": 150, + "vrf": "RED", + }, + ] + } + }, + "ipv6": { + "r3": { + "static_routes": [ + { + "network": NETWORK["ipv6"][0], + "admin_distance": 150, + "vrf": "RED", + }, + { + "network": NETWORK["ipv6"][1], + "admin_distance": 150, + "vrf": "RED", + }, + ] + } + }, + } + + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_admin_distance( + tgen, addr_type, dut, input_dict[addr_type], attribute, vrf="RED" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Un configure the admin distance value on DUT") + + input_dict_1 = { + "r3": { + "bgp": [ + { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "distance": { + "ebgp": 150, + "ibgp": 150, + "local": 150, + "delete": True, + } + } + }, + "ipv6": { + "unicast": { + "distance": { + "ebgp": 150, + "ibgp": 150, + "local": 150, + "delete": True, + } + } + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp routes have default admin distance in dut.") + # Verifying best path + dut = "r3" + attribute = "admin_distance" + + input_dict = { + "ipv4": { + "r3": { + "static_routes": [ + {"network": NETWORK["ipv4"][0], "admin_distance": 20, "vrf": "RED"}, + {"network": NETWORK["ipv4"][1], "admin_distance": 20, "vrf": "RED"}, + ] + } + }, + "ipv6": { + "r3": { + "static_routes": [ + {"network": NETWORK["ipv6"][0], "admin_distance": 20, "vrf": "RED"}, + {"network": NETWORK["ipv6"][1], "admin_distance": 20, "vrf": "RED"}, + ] + } + }, + } + + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_admin_distance( + tgen, addr_type, dut, input_dict[addr_type], attribute, vrf="RED" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure static route Without any admin distance") + + for addr_type in ADDR_TYPES: + # Create Static routes + input_dict = { + "r3": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": "Null0", "vrf": "RED"} + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that zebra selects static route.") + protocol = "static" + # dual stack changes + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": "Null0", "vrf": "RED"} + ] + } + } + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Configure static route with admin distance of 253") + for addr_type in ADDR_TYPES: + # Create Static routes + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": "Null0", + "admin_distance": 253, + "vrf": "RED", + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that zebra selects bgp route.") + protocol = "bgp" + + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": "Null0", + "admin_distance": 253, + "vrf": "RED", + } + ] + } + } + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Configure admin distance of 254 in bgp for route .") + + input_dict_1 = { + "r3": { + "bgp": [ + { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 254, "ibgp": 254, "local": 254} + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 254, "ibgp": 254, "local": 254} + } + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that zebra selects static route.") + protocol = "static" + # dual stack changes + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": "Null0", + "admin_distance": 253, + "vrf": "RED", + } + ] + } + } + + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Configure admin distance of 255 in bgp for route in vrf red") + + input_dict_1 = { + "r3": { + "bgp": [ + { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 255, "ibgp": 255, "local": 255} + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 255, "ibgp": 255, "local": 255} + } + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that zebra selects static route.") + protocol = "static" + # dual stack changes + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": "Null0", + "admin_distance": 253, + "vrf": "RED", + } + ] + } + } + + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Delete the static route.") + for addr_type in ADDR_TYPES: + # Create Static routes + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": "Null0", + "admin_distance": 253, + "delete": True, + "vrf": "RED", + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that zebra selects bgp route.") + protocol = "bgp" + # dual stack changes + for addr_type in ADDR_TYPES: + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + write_test_footer(tc_name) + + +def test_bgp_admin_distance_ebgp_with_imported_rtes_vrf_p0(): + """ + TC: 5 + Verify bgp admin distance functionality when static route is configured + same as bgp learnt route in user vrf. + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipping test case because of BGP Convergence failure at setup") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure base config as per the topology") + reset_config_on_routers(tgen) + step("Configure bgp admin distance 200 with CLI in dut.") + step(" Import route from vrf to default vrf") + input_dict_1 = { + "r3": { + "bgp": [ + { + "vrf": "RED", + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200} + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200} + } + }, + }, + }, + { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200}, + "import": {"vrf": "RED"}, + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200}, + "import": { + "vrf": "RED", + }, + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp routes have admin distance of 200 in dut.") + # Verifying best path + dut = "r3" + attribute = "admin_distance" + + input_dict = { + "ipv4": { + "r3": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "admin_distance": 200, + "vrf": "RED", + }, + { + "network": NETWORK["ipv4"][1], + "admin_distance": 200, + "vrf": "RED", + }, + ] + } + }, + "ipv6": { + "r3": { + "static_routes": [ + { + "network": NETWORK["ipv6"][0], + "admin_distance": 200, + "vrf": "RED", + }, + { + "network": NETWORK["ipv6"][1], + "admin_distance": 200, + "vrf": "RED", + }, + ] + } + }, + } + + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_admin_distance( + tgen, addr_type, dut, input_dict[addr_type], attribute, vrf="RED" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that routes are getting imported without any issues and " + "routes are calculated and installed in rib." + ) + + input_dict = { + "ipv4": { + "r3": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "admin_distance": 200, + }, + { + "network": NETWORK["ipv4"][1], + "admin_distance": 200, + }, + ] + } + }, + "ipv6": { + "r3": { + "static_routes": [ + { + "network": NETWORK["ipv6"][0], + "admin_distance": 200, + }, + { + "network": NETWORK["ipv6"][1], + "admin_distance": 200, + }, + ] + } + }, + } + + step("Verify that zebra selects bgp route.") + protocol = "bgp" + # dual stack changes + for addr_type in ADDR_TYPES: + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step(" Un configure import route vrf red inside default vrf.") + input_dict_1 = { + "r3": { + "bgp": [ + { + "vrf": "RED", + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200} + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200} + } + }, + }, + }, + { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200}, + "import": {"vrf": "RED", "delete": True}, + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200}, + "import": {"vrf": "RED", "delete": True}, + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "ipv4": { + "r3": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "admin_distance": 200, + }, + { + "network": NETWORK["ipv4"][1], + "admin_distance": 200, + }, + ] + } + }, + "ipv6": { + "r3": { + "static_routes": [ + { + "network": NETWORK["ipv6"][0], + "admin_distance": 200, + }, + { + "network": NETWORK["ipv6"][1], + "admin_distance": 200, + }, + ] + } + }, + } + + step("Verify that route withdrawal happens properly.") + protocol = "bgp" + # dual stack changes + for addr_type in ADDR_TYPES: + result4 = verify_rib( + tgen, + addr_type, + dut, + input_dict[addr_type], + protocol=protocol, + expected=False, + ) + assert ( + result4 is not True + ), "Testcase {} : Failed \n Route is not withdrawn. Error: {}".format( + tc_name, result4 + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_distance_change/test_bgp_distance_change.py b/tests/topotests/bgp_distance_change/test_bgp_distance_change.py new file mode 100644 index 0000000..2ca50aa --- /dev/null +++ b/tests/topotests/bgp_distance_change/test_bgp_distance_change.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# bgp_distance_change.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Donatas Abraitis +# + +""" +bgp_distance_change.py: + +Test if works the following commands: +router bgp 65031 + address-family ipv4 unicast + distance bgp 123 123 123 + +Changed distance should reflect to RIB after changes. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_maximum_prefix_invalid(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_distance_change(router): + router.vtysh_cmd( + """ + configure terminal + router bgp 65000 + address-family ipv4 unicast + distance bgp 123 123 123 + """ + ) + + def _bgp_check_distance_change(router): + output = json.loads(router.vtysh_cmd("show ip route 172.16.255.254/32 json")) + expected = {"172.16.255.254/32": [{"protocol": "bgp", "distance": 123}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + + assert result is None, 'Failed to see BGP convergence in "{}"'.format(router) + + _bgp_distance_change(router) + + test_func = functools.partial(_bgp_check_distance_change, router) + success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + + assert result is None, 'Failed to see applied BGP distance in RIB "{}"'.format( + router + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_dont_capability_negotiate/__init__.py b/tests/topotests/bgp_dont_capability_negotiate/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf b/tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf new file mode 100644 index 0000000..2f76d59 --- /dev/null +++ b/tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf @@ -0,0 +1,12 @@ +! +!debug bgp neighbor +! +router bgp 65001 + no bgp ebgp-requires-policy + bgp default show-hostname + bgp default show-nexthop-hostname + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 192.168.1.2 dont-capability-negotiate +! diff --git a/tests/topotests/bgp_dont_capability_negotiate/r1/zebra.conf b/tests/topotests/bgp_dont_capability_negotiate/r1/zebra.conf new file mode 100644 index 0000000..b29940f --- /dev/null +++ b/tests/topotests/bgp_dont_capability_negotiate/r1/zebra.conf @@ -0,0 +1,4 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_dont_capability_negotiate/r2/bgpd.conf b/tests/topotests/bgp_dont_capability_negotiate/r2/bgpd.conf new file mode 100644 index 0000000..06e1e54 --- /dev/null +++ b/tests/topotests/bgp_dont_capability_negotiate/r2/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_dont_capability_negotiate/r2/zebra.conf b/tests/topotests/bgp_dont_capability_negotiate/r2/zebra.conf new file mode 100644 index 0000000..dc15cf7 --- /dev/null +++ b/tests/topotests/bgp_dont_capability_negotiate/r2/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.16.1/32 +! +int r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py b/tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py new file mode 100644 index 0000000..ab71f87 --- /dev/null +++ b/tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2021 by +# Donatas Abraitis +# + +""" +Test if BGP connection is established if at least one peer +sets `dont-capability-negotiate`. +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def bgp_converge(router): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast summary json")) + expected = { + "peers": { + "192.168.1.2": { + "pfxRcd": 2, + "pfxSnt": 2, + "state": "Established", + "peerState": "OK", + } + } + } + return topotest.json_cmp(output, expected) + + +def test_bgp_dont_capability_negotiate(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + test_func = functools.partial(bgp_converge, r1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge with dont-capability-negotiate" + + +def test_bgp_check_fqdn(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_check_fqdn(fqdn=None): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 172.16.16.1/32 json")) + expected = { + "paths": [ + { + "nexthops": [ + { + "hostname": fqdn, + } + ], + "peer": { + "hostname": fqdn, + }, + } + ] + } + return topotest.json_cmp(output, expected) + + step("Enable all capabilities") + r1.vtysh_cmd( + """ + configure terminal + router bgp + address-family ipv4 unicast + no neighbor 192.168.1.2 dont-capability-negotiate + end + clear bgp 192.168.1.2 + """ + ) + + step("Wait to converge") + test_func = functools.partial(bgp_converge, r1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge with dont-capability-negotiate" + + test_func = functools.partial(_bgp_check_fqdn, "r2") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "FQDN capability enabled, but r1 can't see it" + + step("Disable sending any capabilities from r2") + r2.vtysh_cmd( + """ + configure terminal + router bgp + address-family ipv4 unicast + neighbor 192.168.1.1 dont-capability-negotiate + end + clear bgp 192.168.1.1 + """ + ) + + step("Wait to converge") + test_func = functools.partial(bgp_converge, r1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge with dont-capability-negotiate" + + step("Make sure FQDN capability is reset") + test_func = functools.partial(_bgp_check_fqdn) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "FQDN capability disabled, but we still have a hostname" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_dynamic_capability/__init__.py b/tests/topotests/bgp_dynamic_capability/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_dynamic_capability/r1/frr.conf b/tests/topotests/bgp_dynamic_capability/r1/frr.conf new file mode 100644 index 0000000..50280a9 --- /dev/null +++ b/tests/topotests/bgp_dynamic_capability/r1/frr.conf @@ -0,0 +1,15 @@ +! +!debug bgp neighbor +! +int r1-eth0 + ip address 192.168.1.1/24 +! +router bgp 65001 + no bgp ebgp-requires-policy + bgp graceful-restart + bgp long-lived stale-time 10 + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 192.168.1.2 capability dynamic +! diff --git a/tests/topotests/bgp_dynamic_capability/r2/frr.conf b/tests/topotests/bgp_dynamic_capability/r2/frr.conf new file mode 100644 index 0000000..16b83a5 --- /dev/null +++ b/tests/topotests/bgp_dynamic_capability/r2/frr.conf @@ -0,0 +1,22 @@ +! +!debug bgp neighbor +! +int lo + ip address 10.10.10.10/32 +! +int r2-eth0 + ip address 192.168.1.2/24 +! +router bgp 65002 + bgp graceful-restart + bgp long-lived stale-time 20 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + neighbor 192.168.1.1 capability dynamic + ! + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py new file mode 100644 index 0000000..75c712e --- /dev/null +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if BGP graceful restart / long-lived graceful restart capabilities +(restart time, stale time and notification flag) are exchanged dynamically +via BGP dynamic capability. +""" + +import os +import re +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_dynamic_capability_graceful_restart(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "gracefulRestart": "advertisedAndReceived", + "longLivedGracefulRestart": "advertisedAndReceived", + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 2, + } + }, + "gracefulRestartInfo": { + "nBit": True, + "timers": { + "receivedRestartTimer": 120, + "configuredLlgrStaleTime": 10, + }, + "ipv4Unicast": { + "timers": { + "llgrStaleTime": 10, + } + }, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't converge" + + step( + "Change Graceful-Restart restart-time, LLGR stale-time and check if they changed dynamically" + ) + + # Clear message stats to check if we receive a notification or not after we + # change the settings fo LLGR. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r2.vtysh_cmd( + """ + configure terminal + router bgp + bgp graceful-restart restart-time 123 + bgp long-lived-graceful-restart stale-time 5 + """ + ) + + def _bgp_check_if_session_not_reset_after_changing_gr_settings(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "gracefulRestart": "advertisedAndReceived", + "longLivedGracefulRestart": "advertisedAndReceived", + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 2, + } + }, + "gracefulRestartInfo": { + "nBit": True, + "timers": { + "receivedRestartTimer": 123, + "configuredLlgrStaleTime": 10, + }, + "ipv4Unicast": { + "timers": { + "llgrStaleTime": 5, + } + }, + }, + "messageStats": { + "notificationsRecv": 0, + "capabilityRecv": 2, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_session_not_reset_after_changing_gr_settings, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "Session was reset after changing Graceful-Restart restart-time" + + step( + "Disable Graceful-Restart notification support, and check if it's changed dynamically" + ) + + # Clear message stats to check if we receive a notification or not after we + # change the settings fo LLGR. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r2.vtysh_cmd( + """ + configure terminal + router bgp + no bgp graceful-restart notification + """ + ) + + def _bgp_check_if_session_not_reset_after_changing_notification(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "gracefulRestart": "advertisedAndReceived", + "longLivedGracefulRestart": "advertisedAndReceived", + }, + "gracefulRestartInfo": { + "nBit": False, + "timers": { + "receivedRestartTimer": 123, + "configuredLlgrStaleTime": 10, + }, + "ipv4Unicast": { + "timers": { + "llgrStaleTime": 5, + } + }, + }, + "messageStats": { + "notificationsRecv": 0, + "capabilityRecv": 1, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_session_not_reset_after_changing_notification, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "Session was reset after changing Graceful-Restart notification support" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py new file mode 100644 index 0000000..576ef74 --- /dev/null +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if role capability is exchanged dynamically. +""" + +import os +import re +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_dynamic_capability_role(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "localRole": "undefined", + "remoteRole": "undefined", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 2, + } + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't converge" + + step("Set local-role and check if it's exchanged dynamically") + + # Clear message stats to check if we receive a notification or not after we + # change the settings fo LLGR. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + neighbor 192.168.1.2 local-role customer + """ + ) + + r2.vtysh_cmd( + """ + configure terminal + router bgp + neighbor 192.168.1.1 local-role provider + """ + ) + + def _bgp_check_if_session_not_reset(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "localRole": "customer", + "remoteRole": "provider", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "role": "advertisedAndReceived", + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 2, + } + }, + "messageStats": { + "notificationsRecv": 0, + "capabilityRecv": 1, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_session_not_reset, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Session was reset after setting role capability" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py new file mode 100644 index 0000000..002d782 --- /dev/null +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if software version capability is exchanged dynamically. +""" + +import os +import re +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_dynamic_capability_software_version(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "softwareVersion": { + "advertisedSoftwareVersion": None, + "receivedSoftwareVersion": None, + }, + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 2, + } + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't converge" + + step("Enable software version capability and check if it's exchanged dynamically") + + # Clear message stats to check if we receive a notification or not after we + # change the settings fo LLGR. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + neighbor 192.168.1.2 capability software-version + """ + ) + + r2.vtysh_cmd( + """ + configure terminal + router bgp + neighbor 192.168.1.1 capability software-version + """ + ) + + def _bgp_check_if_session_not_reset(): + def _bgp_software_version(): + try: + versions = output["192.168.1.2"]["neighborCapabilities"][ + "softwareVersion" + ] + adv = versions["advertisedSoftwareVersion"] + rcv = versions["receivedSoftwareVersion"] + + if not adv and not rcv: + return "" + + pattern = "^FRRouting/\\d.+" + if re.search(pattern, adv) and re.search(pattern, rcv): + return adv, rcv + except: + return "" + + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + adv, rcv = _bgp_software_version() + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "softwareVersion": { + "advertisedSoftwareVersion": adv, + "receivedSoftwareVersion": rcv, + }, + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 2, + } + }, + "messageStats": { + "notificationsRecv": 0, + "capabilityRecv": 1, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_session_not_reset, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "Session was reset after enabling software version capability" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/__init__.py b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/bgpd.conf b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/bgpd.conf new file mode 100644 index 0000000..c320bb5 --- /dev/null +++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/bgpd.conf @@ -0,0 +1,7 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.101 remote-as external + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/zebra.conf b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/zebra.conf new file mode 100644 index 0000000..1782edc --- /dev/null +++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.1.1/32 +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/bgpd.conf b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/bgpd.conf new file mode 100644 index 0000000..cb712e9 --- /dev/null +++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/bgpd.conf @@ -0,0 +1,3 @@ +router bgp 65103 + no bgp ebgp-requires-policy + neighbor 192.168.1.101 remote-as external diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/zebra.conf b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/zebra.conf new file mode 100644 index 0000000..968171e --- /dev/null +++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/zebra.conf @@ -0,0 +1,4 @@ +! +int r2-eth0 + ip address 192.168.1.103/24 +! diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/bgpd.conf b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/bgpd.conf new file mode 100644 index 0000000..a6e3260 --- /dev/null +++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 65000 + bgp router-id 192.168.1.101 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.103 remote-as external diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/zebra.conf b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/zebra.conf new file mode 100644 index 0000000..ddcf862 --- /dev/null +++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/zebra.conf @@ -0,0 +1,4 @@ +! +int r3-eth0 + ip address 192.168.1.101/24 +! diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py new file mode 100644 index 0000000..34f7dc8 --- /dev/null +++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2021 by +# Donatas Abraitis +# + +""" +https://tools.ietf.org/html/rfc4271 + +Check if NEXT_HOP attribute is not changed if peer X shares a +common subnet with this address. + +- Otherwise, if the route being announced was learned from an + external peer, the speaker can use an IP address of any + adjacent router (known from the received NEXT_HOP attribute) + that the speaker itself uses for local route calculation in + the NEXT_HOP attribute, provided that peer X shares a common + subnet with this address. This is a second form of "third + party" NEXT_HOP attribute. +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = [pytest.mark.bgpd] + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_ebgp_common_subnet_nh_unchanged(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp summary json")) + expected = { + "ipv4Unicast": { + "peers": { + "192.168.1.1": {"state": "Established"}, + "192.168.1.103": {"state": "Established"}, + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, r3) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed bgp convergence in "{}"'.format(r3) + + def _bgp_nh_unchanged(router): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.1.1/32 json")) + expected = {"paths": [{"nexthops": [{"ip": "192.168.1.1"}]}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_nh_unchanged, r2) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Wrong next-hop in "{}"'.format(r2) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ebgp_requires_policy/__init__.py b/tests/topotests/bgp_ebgp_requires_policy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf new file mode 100644 index 0000000..add37ee --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65000 + neighbor 192.168.255.2 remote-as 1000 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 local-as 500 + address-family ipv4 unicast + redistribute connected + neighbor 192.168.255.2 route-map outgoing out +! +ip prefix-list peer-out permit 172.16.255.254/32 +route-map outgoing permit 10 + match ip address prefix-list peer-out +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf new file mode 100644 index 0000000..0a283c0 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf new file mode 100644 index 0000000..802c327 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 1000 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 500 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf new file mode 100644 index 0000000..606c17b --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf new file mode 100644 index 0000000..1280b42 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65000 + neighbor 192.168.255.2 remote-as 1000 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 local-as 500 + address-family ipv4 unicast + redistribute connected diff --git a/tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf new file mode 100644 index 0000000..39499a1 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r3-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf new file mode 100644 index 0000000..802c327 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 1000 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 500 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf new file mode 100644 index 0000000..b859115 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf @@ -0,0 +1,6 @@ +! +interface r4-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf new file mode 100644 index 0000000..60b29f6 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf @@ -0,0 +1,7 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65000 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + redistribute connected +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf new file mode 100644 index 0000000..7ef77a6 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r5-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf new file mode 100644 index 0000000..7ad5d92 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 65000 + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf new file mode 100644 index 0000000..1c617c4 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf @@ -0,0 +1,6 @@ +! +interface r6-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py new file mode 100644 index 0000000..6e3b285 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# bgp_ebgp_requires_policy.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Donatas Abraitis +# + +""" +bgp_ebgp_requires_policy.py: + +Test if eBGP sender without a filter applied to the peer is allowed +to send advertisements. + +Scenario 1: + r1 has a filter applied for outgoing direction, + r2 receives 192.168.255.1/32. + +Scenario 2: + r3 hasn't a filter appied for outgoing direction, + r4 does not receive 192.168.255.1/32. + +Scenario 3: + r5 and r6 establish iBGP session which in turn should ignore + RFC8212. All routes for both directions MUST work. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 7): + tgen.add_router("r{}".format(routern)) + + # Scenario 1. + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # Scenario 2. + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + # Scenario 3. + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r5"]) + switch.add_link(tgen.gears["r6"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_ebgp_requires_policy(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(router): + output = json.loads( + tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json") + ) + expected = {"192.168.255.1": {"bgpState": "Established"}} + return topotest.json_cmp(output, expected) + + def _bgp_has_routes(router): + output = json.loads( + tgen.gears[router].vtysh_cmd( + "show ip bgp neighbor 192.168.255.1 routes json" + ) + ) + expected = {"routes": {"172.16.255.254/32": [{"valid": True}]}} + return topotest.json_cmp(output, expected) + + def _bgp_advertised_routes(router): + output = json.loads( + tgen.gears[router].vtysh_cmd( + "show ip bgp neighbor 192.168.255.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": {}, + "totalPrefixCounter": 0, + "filteredPrefixCounter": 0, + } + return topotest.json_cmp(output, expected) + + # Scenario 1. + logger.info("Scenario 1: r2 receives 192.168.255.1/32 from r1") + test_func = functools.partial(_bgp_converge, "r2") + success, result = topotest.run_and_expect(test_func, None, count=120, wait=0.5) + assert success is True, "Failed bgp convergence (r2)" + + test_func = functools.partial(_bgp_has_routes, "r2") + success, result = topotest.run_and_expect(test_func, None, count=120, wait=0.5) + assert success is True, "r2 does not receive 192.168.255.1/32" + + # Scenario 2. + logger.info("Scenario 2: r3 must not send 192.168.255.1/32 to r4") + test_func = functools.partial(_bgp_converge, "r4") + success, result = topotest.run_and_expect(test_func, None, count=120, wait=0.5) + assert success is True, "Failed bgp convergence (r4)" + + test_func = functools.partial(_bgp_advertised_routes, "r3") + success, result = topotest.run_and_expect(test_func, None, count=120, wait=0.5) + assert success is True, "r3 announced 192.168.255.1/32 to r4" + + # Scenario 3. + logger.info("Scenario 3: r6 receives 192.168.255.1/32 from r5 (iBGP)") + test_func = functools.partial(_bgp_converge, "r6") + success, result = topotest.run_and_expect(test_func, None, count=120, wait=0.5) + assert success is True, "Failed bgp convergence (r6)" + + test_func = functools.partial(_bgp_has_routes, "r6") + success, result = topotest.run_and_expect(test_func, None, count=120, wait=0.5) + assert success is True, "r6 does not receive 192.168.255.1/32" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ecmp_topo1/__init__.py b/tests/topotests/bgp_ecmp_topo1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.dot b/tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.dot new file mode 100644 index 0000000..90295e1 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.dot @@ -0,0 +1,206 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph ospf_ecmp_iBGP_topo1 { + label="bgp ecmp topo1 - eBGP with different AS numbers"; + labelloc="t"; + + # Routers + r1 [ + label="r1\nrtr-id 10.0.255.1/32", + shape=doubleoctagon, + fillcolor="#f08080", + style=filled, + ]; + + # 4 Switches for eBGP Peers + s1 [ + label="s1\n10.0.1.0/24", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + label="s2\n10.0.2.0/24", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + s3 [ + label="s3\n10.0.3.0/24", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + s4 [ + label="s4\n10.0.4.0/24", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + + # 20 ExaBGP Peers AS 101...120 + peer1 [ + label="eBGP peer1\nAS99\nrtr-id 10.0.1.101/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer2 [ + label="eBGP peer2\nAS99\nrtr-id 10.0.1.102/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer3 [ + label="eBGP peer3\nAS99\nrtr-id 10.0.1.103/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer4 [ + label="eBGP peer4\nAS99\nrtr-id 10.0.1.104/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer5 [ + label="eBGP peer5\nAS99\nrtr-id 10.0.1.105/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer6 [ + label="eBGP peer6\nAS99\nrtr-id 10.0.2.106/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer7 [ + label="eBGP peer7\nAS99\nrtr-id 10.0.2.107/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer8 [ + label="eBGP peer8\nAS99\nrtr-id 10.0.2.108/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer9 [ + label="eBGP peer9\nAS99\nrtr-id 10.0.2.109/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer10 [ + label="eBGP peer10\nAS99\nrtr-id 10.0.2.110/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer11 [ + label="eBGP peer11\nAS111\nrtr-id 10.0.3.111/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer12 [ + label="eBGP peer12\nAS112\nrtr-id 10.0.3.112/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer13 [ + label="eBGP peer13\nAS113\nrtr-id 10.0.3.113/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer14 [ + label="eBGP peer14\nAS114\nrtr-id 10.0.3.114/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer15 [ + label="eBGP peer15\nAS115\nrtr-id 10.0.3.115/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer16 [ + label="eBGP peer16\nAS116\nrtr-id 10.0.4.116/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer17 [ + label="eBGP peer17\nAS117\nrtr-id 10.0.4.117/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer18 [ + label="eBGP peer18\nAS118\nrtr-id 10.0.4.118/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer19 [ + label="eBGP peer19\nAS119\nrtr-id 10.0.4.119/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer20 [ + label="eBGP peer20\nAS120\nrtr-id 10.0.4.120/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + + # Connections + r1 -- s1 [label="eth0\n.1"]; + r1 -- s2 [label="eth1\n.1"]; + r1 -- s3 [label="eth2\n.1"]; + r1 -- s4 [label="eth3\n.1"]; + + peer1 -- s1 [label="eth0\n.101"]; + peer2 -- s1 [label="eth0\n.102"]; + peer3 -- s1 [label="eth0\n.103"]; + peer4 -- s1 [label="eth0\n.104"]; + peer5 -- s1 [label="eth0\n.105"]; + peer6 -- s2 [label="eth0\n.106"]; + peer7 -- s2 [label="eth0\n.107"]; + peer8 -- s2 [label="eth0\n.108"]; + peer9 -- s2 [label="eth0\n.109"]; + peer10 -- s2 [label="eth0\n.110"]; + peer11 -- s3 [label="eth0\n.111"]; + peer12 -- s3 [label="eth0\n.112"]; + peer13 -- s3 [label="eth0\n.113"]; + peer14 -- s3 [label="eth0\n.114"]; + peer15 -- s3 [label="eth0\n.115"]; + peer16 -- s4 [label="eth0\n.116"]; + peer17 -- s4 [label="eth0\n.117"]; + peer18 -- s4 [label="eth0\n.118"]; + peer19 -- s4 [label="eth0\n.119"]; + peer20 -- s4 [label="eth0\n.120"]; + + # Arrange network to make cleaner diagram + { rank=same peer1 peer2 peer3 peer4 peer5 } -- s1 -- { rank=same peer6 peer7 peer8 peer9 peer10 } -- s2 + -- { rank=same peer11 peer12 peer13 peer14 peer15 } -- s3 -- { rank=same peer16 peer17 peer18 peer19 peer20 } -- s4 + -- { rank=same r1 } [style=invis] +} diff --git a/tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.pdf b/tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.pdf new file mode 100644 index 0000000..b4d4f6a Binary files /dev/null and b/tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.pdf differ diff --git a/tests/topotests/bgp_ecmp_topo1/exabgp.env b/tests/topotests/bgp_ecmp_topo1/exabgp.env new file mode 100644 index 0000000..a328e04 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/exabgp.env @@ -0,0 +1,54 @@ + +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' +##daemonize = false + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg new file mode 100644 index 0000000..2d0ca89 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 1 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 1"; + receive-routes; + encoder text; + } + + neighbor 10.0.1.1 { + router-id 10.0.1.101; + local-address 10.0.1.101; + local-as 99; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg new file mode 100644 index 0000000..0c842a0 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 10 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 10"; + receive-routes; + encoder text; + } + + neighbor 10.0.2.1 { + router-id 10.0.2.110; + local-address 10.0.2.110; + local-as 99; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg new file mode 100644 index 0000000..936dc57 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 11 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 11"; + receive-routes; + encoder text; + } + + neighbor 10.0.3.1 { + router-id 10.0.3.111; + local-address 10.0.3.111; + local-as 111; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg new file mode 100644 index 0000000..56b33ea --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 12 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 12"; + receive-routes; + encoder text; + } + + neighbor 10.0.3.1 { + router-id 10.0.3.112; + local-address 10.0.3.112; + local-as 112; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg new file mode 100644 index 0000000..b933ffb --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 13 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 13"; + receive-routes; + encoder text; + } + + neighbor 10.0.3.1 { + router-id 10.0.3.113; + local-address 10.0.3.113; + local-as 113; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg new file mode 100644 index 0000000..bcfa41e --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 14 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 14"; + receive-routes; + encoder text; + } + + neighbor 10.0.3.1 { + router-id 10.0.3.114; + local-address 10.0.3.114; + local-as 114; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg new file mode 100644 index 0000000..022e835 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 15 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 15"; + receive-routes; + encoder text; + } + + neighbor 10.0.3.1 { + router-id 10.0.3.115; + local-address 10.0.3.115; + local-as 115; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg new file mode 100644 index 0000000..0649202 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 16 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 16"; + receive-routes; + encoder text; + } + + neighbor 10.0.4.1 { + router-id 10.0.4.116; + local-address 10.0.4.116; + local-as 116; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg new file mode 100644 index 0000000..0aeeed9 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 17 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 17"; + receive-routes; + encoder text; + } + + neighbor 10.0.4.1 { + router-id 10.0.4.117; + local-address 10.0.4.117; + local-as 117; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg new file mode 100644 index 0000000..352c030 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 18 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 18"; + receive-routes; + encoder text; + } + + neighbor 10.0.4.1 { + router-id 10.0.4.118; + local-address 10.0.4.118; + local-as 118; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg new file mode 100644 index 0000000..9913c22 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 19 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 19"; + receive-routes; + encoder text; + } + + neighbor 10.0.4.1 { + router-id 10.0.4.119; + local-address 10.0.4.119; + local-as 119; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg new file mode 100644 index 0000000..46b436d --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 2 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 2"; + receive-routes; + encoder text; + } + + neighbor 10.0.1.1 { + router-id 10.0.1.102; + local-address 10.0.1.102; + local-as 99; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg new file mode 100644 index 0000000..17fb816 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 20 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 20"; + receive-routes; + encoder text; + } + + neighbor 10.0.4.1 { + router-id 10.0.4.120; + local-address 10.0.4.120; + local-as 120; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg new file mode 100644 index 0000000..acd5775 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 3 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 3"; + receive-routes; + encoder text; + } + + neighbor 10.0.1.1 { + router-id 10.0.1.103; + local-address 10.0.1.103; + local-as 99; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg new file mode 100644 index 0000000..4c9a989 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 4 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 4"; + receive-routes; + encoder text; + } + + neighbor 10.0.1.1 { + router-id 10.0.1.104; + local-address 10.0.1.104; + local-as 99; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg new file mode 100644 index 0000000..eba2aae --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 5 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 5"; + receive-routes; + encoder text; + } + + neighbor 10.0.1.1 { + router-id 10.0.1.105; + local-address 10.0.1.105; + local-as 99; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg new file mode 100644 index 0000000..38b6af0 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 6 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 6"; + receive-routes; + encoder text; + } + + neighbor 10.0.2.1 { + router-id 10.0.2.106; + local-address 10.0.2.106; + local-as 99; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg new file mode 100644 index 0000000..7631e43 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 7 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 7"; + receive-routes; + encoder text; + } + + neighbor 10.0.2.1 { + router-id 10.0.2.107; + local-address 10.0.2.107; + local-as 99; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg new file mode 100644 index 0000000..1cd1cd9 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 8 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 8"; + receive-routes; + encoder text; + } + + neighbor 10.0.2.1 { + router-id 10.0.2.108; + local-address 10.0.2.108; + local-as 99; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py new file mode 100755 index 0000000..6bef355 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg new file mode 100644 index 0000000..5771553 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 9 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 9"; + receive-routes; + encoder text; + } + + neighbor 10.0.2.1 { + router-id 10.0.2.109; + local-address 10.0.2.109; + local-as 99; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_ecmp_topo1/r1/bgpd.conf b/tests/topotests/bgp_ecmp_topo1/r1/bgpd.conf new file mode 100644 index 0000000..49981ac --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/r1/bgpd.conf @@ -0,0 +1,51 @@ +! +hostname r1 +log file bgpd.log +! +router bgp 100 + bgp router-id 10.0.255.1 + bgp bestpath as-path multipath-relax + no bgp ebgp-requires-policy + neighbor 10.0.1.101 remote-as 99 + neighbor 10.0.1.101 timers 3 10 + neighbor 10.0.1.102 remote-as 99 + neighbor 10.0.1.102 timers 3 10 + neighbor 10.0.1.103 remote-as 99 + neighbor 10.0.1.103 timers 3 10 + neighbor 10.0.1.104 remote-as 99 + neighbor 10.0.1.104 timers 3 10 + neighbor 10.0.1.105 remote-as 99 + neighbor 10.0.1.105 timers 3 10 + neighbor 10.0.2.106 remote-as 99 + neighbor 10.0.2.106 timers 3 10 + neighbor 10.0.2.107 remote-as 99 + neighbor 10.0.2.107 timers 3 10 + neighbor 10.0.2.108 remote-as 99 + neighbor 10.0.2.108 timers 3 10 + neighbor 10.0.2.109 remote-as 99 + neighbor 10.0.2.109 timers 3 10 + neighbor 10.0.2.110 remote-as 99 + neighbor 10.0.2.110 timers 3 10 + neighbor 10.0.3.111 remote-as 111 + neighbor 10.0.3.111 timers 3 10 + neighbor 10.0.3.112 remote-as 112 + neighbor 10.0.3.112 timers 3 10 + neighbor 10.0.3.113 remote-as 113 + neighbor 10.0.3.113 timers 3 10 + neighbor 10.0.3.114 remote-as 114 + neighbor 10.0.3.114 timers 3 10 + neighbor 10.0.3.115 remote-as 115 + neighbor 10.0.3.115 timers 3 10 + neighbor 10.0.4.116 remote-as 116 + neighbor 10.0.4.116 timers 3 10 + neighbor 10.0.4.117 remote-as 117 + neighbor 10.0.4.117 timers 3 10 + neighbor 10.0.4.118 remote-as 118 + neighbor 10.0.4.118 timers 3 10 + neighbor 10.0.4.119 remote-as 119 + neighbor 10.0.4.119 timers 3 10 + neighbor 10.0.4.120 remote-as 120 + neighbor 10.0.4.120 timers 3 10 + ! +! + diff --git a/tests/topotests/bgp_ecmp_topo1/r1/summary.txt b/tests/topotests/bgp_ecmp_topo1/r1/summary.txt new file mode 100644 index 0000000..68de28a --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/r1/summary.txt @@ -0,0 +1,131 @@ +{ +"ipv4Unicast":{ + "routerId":"10.0.255.1", + "as":100, + "vrfName":"default", + "peerCount":20, + "peers":{ + "10.0.1.101":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.1.102":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.1.103":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.1.104":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.1.105":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.2.106":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.2.107":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.2.108":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.2.109":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.2.110":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.3.111":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.3.112":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.3.113":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.3.114":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.3.115":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.4.116":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.4.117":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.4.118":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.4.119":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.4.120":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + } + }, + "totalPeers":20 +} +} diff --git a/tests/topotests/bgp_ecmp_topo1/r1/summary20.txt b/tests/topotests/bgp_ecmp_topo1/r1/summary20.txt new file mode 100644 index 0000000..4895cdb --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/r1/summary20.txt @@ -0,0 +1,129 @@ +{ + "routerId":"10.0.255.1", + "as":100, + "vrfName":"default", + "peerCount":20, + "peers":{ + "10.0.1.101":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.1.102":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.1.103":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.1.104":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.1.105":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.2.106":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.2.107":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.2.108":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.2.109":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.2.110":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.3.111":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.3.112":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.3.113":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.3.114":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.3.115":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.4.116":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.4.117":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.4.118":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.4.119":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.4.120":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + } + }, + "totalPeers":20 +} diff --git a/tests/topotests/bgp_ecmp_topo1/r1/zebra.conf b/tests/topotests/bgp_ecmp_topo1/r1/zebra.conf new file mode 100644 index 0000000..77c76cd --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/r1/zebra.conf @@ -0,0 +1,16 @@ +! +hostname r1 +log file zebra.log +! +interface r1-eth0 + ip address 10.0.1.1/24 +! +interface r1-eth1 + ip address 10.0.2.1/24 +! +interface r1-eth2 + ip address 10.0.3.1/24 +! +interface r1-eth3 + ip address 10.0.4.1/24 +! diff --git a/tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py b/tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py new file mode 100644 index 0000000..97751ec --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python2 +# SPDX-License-Identifier: ISC + +# +# test_bgp_ecmp_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2017 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_bgp_ecmp_topo1.py: Test BGP topology with ECMP (Equal Cost MultiPath). +""" + +import json +import functools +import os +import sys +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +total_ebgp_peers = 20 + +##################################################### +# +# Network Topology Definition +# +##################################################### + + +def build_topo(tgen): + router = tgen.add_router("r1") + + # Setup Switches - 1 switch per 5 peering routers + for swNum in range(1, (total_ebgp_peers + 4) // 5 + 1): + switch = tgen.add_switch("s{}".format(swNum)) + switch.add_link(router) + + # Add 'total_ebgp_peers' number of eBGP ExaBGP neighbors + for peerNum in range(1, total_ebgp_peers + 1): + swNum = (peerNum - 1) // 5 + 1 + + peer_ip = "10.0.{}.{}".format(swNum, peerNum + 100) + peer_route = "via 10.0.{}.1".format(swNum) + peer = tgen.add_exabgp_peer( + "peer{}".format(peerNum), ip=peer_ip, defaultRoute=peer_route + ) + + switch = tgen.gears["s{}".format(swNum)] + switch.add_link(peer) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def setup_module(module): + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + # Starting Routers + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + router.start() + + # Starting Hosts and init ExaBGP on each of them + topotest.sleep(10, "starting BGP on all {} peers".format(total_ebgp_peers)) + peer_list = tgen.exabgp_peers() + for pname, peer in peer_list.items(): + peer_dir = os.path.join(CWD, pname) + env_file = os.path.join(CWD, "exabgp.env") + peer.start(peer_dir, env_file) + logger.info(pname) + + +def teardown_module(module): + del module + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_convergence(): + "Test for BGP topology convergence" + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Expected result + router = tgen.gears["r1"] + if router.has_version("<", "3.0"): + reffile = os.path.join(CWD, "r1/summary20.txt") + else: + reffile = os.path.join(CWD, "r1/summary.txt") + + expected = json.loads(open(reffile).read()) + + def _output_summary_cmp(router, cmd, data): + """ + Runs `cmd` that returns JSON data (normally the command ends + with 'json') and compare with `data` contents. + """ + output = router.vtysh_cmd(cmd, isjson=True) + return topotest.json_cmp(output, data) + + test_func = functools.partial( + _output_summary_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assertmsg = "BGP router network did not converge" + assert res is None, assertmsg + + +def test_bgp_ecmp(): + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + expect = { + "routerId": "10.0.255.1", + "routes": {}, + } + + for net in range(1, 5): + for subnet in range(0, 10): + netkey = "10.20{}.{}.0/24".format(net, subnet) + expect["routes"][netkey] = [] + for _ in range(0, 10): + peer = {"multipath": True, "valid": True} + expect["routes"][netkey].append(peer) + + test_func = functools.partial( + topotest.router_json_cmp, tgen.gears["r1"], "show ip bgp json", expect + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = 'expected multipath routes in "show ip bgp" output' + assert res is None, assertmsg + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ecmp_topo2/ebgp_ecmp_topo2.json b/tests/topotests/bgp_ecmp_topo2/ebgp_ecmp_topo2.json new file mode 100755 index 0000000..34f11c0 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo2/ebgp_ecmp_topo2.json @@ -0,0 +1,834 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link8": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link9": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link10": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link11": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link12": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link13": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link14": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link15": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link16": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link17": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link18": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link19": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link20": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link21": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link22": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link23": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link24": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link25": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link26": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link27": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link28": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link29": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link30": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link31": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link32": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "next_hop_self": true + }, + "r2-link2": { + "next_hop_self": true + }, + "r2-link3": { + "next_hop_self": true + }, + "r2-link4": { + "next_hop_self": true + }, + "r2-link5": { + "next_hop_self": true + }, + "r2-link6": { + "next_hop_self": true + }, + "r2-link7": { + "next_hop_self": true + }, + "r2-link8": { + "next_hop_self": true + }, + "r2-link9": { + "next_hop_self": true + }, + "r2-link10": { + "next_hop_self": true + }, + "r2-link11": { + "next_hop_self": true + }, + "r2-link12": { + "next_hop_self": true + }, + "r2-link13": { + "next_hop_self": true + }, + "r2-link14": { + "next_hop_self": true + }, + "r2-link15": { + "next_hop_self": true + }, + "r2-link16": { + "next_hop_self": true + }, + "r2-link17": { + "next_hop_self": true + }, + "r2-link18": { + "next_hop_self": true + }, + "r2-link19": { + "next_hop_self": true + }, + "r2-link20": { + "next_hop_self": true + }, + "r2-link21": { + "next_hop_self": true + }, + "r2-link22": { + "next_hop_self": true + }, + "r2-link23": { + "next_hop_self": true + }, + "r2-link24": { + "next_hop_self": true + }, + "r2-link25": { + "next_hop_self": true + }, + "r2-link26": { + "next_hop_self": true + }, + "r2-link27": { + "next_hop_self": true + }, + "r2-link28": { + "next_hop_self": true + }, + "r2-link29": { + "next_hop_self": true + }, + "r2-link30": { + "next_hop_self": true + }, + "r2-link31": { + "next_hop_self": true + }, + "r2-link32": { + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "next_hop_self": true + }, + "r2-link2": { + "next_hop_self": true + }, + "r2-link3": { + "next_hop_self": true + }, + "r2-link4": { + "next_hop_self": true + }, + "r2-link5": { + "next_hop_self": true + }, + "r2-link6": { + "next_hop_self": true + }, + "r2-link7": { + "next_hop_self": true + }, + "r2-link8": { + "next_hop_self": true + }, + "r2-link9": { + "next_hop_self": true + }, + "r2-link10": { + "next_hop_self": true + }, + "r2-link11": { + "next_hop_self": true + }, + "r2-link12": { + "next_hop_self": true + }, + "r2-link13": { + "next_hop_self": true + }, + "r2-link14": { + "next_hop_self": true + }, + "r2-link15": { + "next_hop_self": true + }, + "r2-link16": { + "next_hop_self": true + }, + "r2-link17": { + "next_hop_self": true + }, + "r2-link18": { + "next_hop_self": true + }, + "r2-link19": { + "next_hop_self": true + }, + "r2-link20": { + "next_hop_self": true + }, + "r2-link21": { + "next_hop_self": true + }, + "r2-link22": { + "next_hop_self": true + }, + "r2-link23": { + "next_hop_self": true + }, + "r2-link24": { + "next_hop_self": true + }, + "r2-link25": { + "next_hop_self": true + }, + "r2-link26": { + "next_hop_self": true + }, + "r2-link27": { + "next_hop_self": true + }, + "r2-link28": { + "next_hop_self": true + }, + "r2-link29": { + "next_hop_self": true + }, + "r2-link30": { + "next_hop_self": true + }, + "r2-link31": { + "next_hop_self": true + }, + "r2-link32": { + "next_hop_self": true + } + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link8": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link9": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link10": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link11": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link12": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link13": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link14": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link15": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link16": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link17": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link18": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link19": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link20": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link21": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link22": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link23": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link24": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link25": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link26": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link27": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link28": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link29": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link30": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link31": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link32": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "maximum_paths": { + "ebgp": 32 + }, + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {}, + "r3-link8": {}, + "r3-link9": {}, + "r3-link10": {}, + "r3-link11": {}, + "r3-link12": {}, + "r3-link13": {}, + "r3-link14": {}, + "r3-link15": {}, + "r3-link16": {}, + "r3-link17": {}, + "r3-link18": {}, + "r3-link19": {}, + "r3-link20": {}, + "r3-link21": {}, + "r3-link22": {}, + "r3-link23": {}, + "r3-link24": {}, + "r3-link25": {}, + "r3-link26": {}, + "r3-link27": {}, + "r3-link28": {}, + "r3-link29": {}, + "r3-link30": {}, + "r3-link31": {}, + "r3-link32": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ebgp": 32 + }, + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link5": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link6": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link7": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link8": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link9": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link10": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link11": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link12": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link13": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link14": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link15": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link16": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link17": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link18": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link19": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link20": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link21": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link22": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link23": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link24": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link25": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link26": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link27": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link28": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link29": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link30": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link31": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link32": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_ecmp_topo2/ibgp_ecmp_topo2.json b/tests/topotests/bgp_ecmp_topo2/ibgp_ecmp_topo2.json new file mode 100755 index 0000000..9eea907 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo2/ibgp_ecmp_topo2.json @@ -0,0 +1,844 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link8": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link9": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link10": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link11": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link12": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link13": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link14": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link15": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link16": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link17": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link18": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link19": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link20": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link21": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link22": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link23": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link24": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link25": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link26": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link27": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link28": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link29": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link30": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link31": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link32": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "next_hop_self": true + }, + "r2-link2": { + "next_hop_self": true + }, + "r2-link3": { + "next_hop_self": true + }, + "r2-link4": { + "next_hop_self": true + }, + "r2-link5": { + "next_hop_self": true + }, + "r2-link6": { + "next_hop_self": true + }, + "r2-link7": { + "next_hop_self": true + }, + "r2-link8": { + "next_hop_self": true + }, + "r2-link9": { + "next_hop_self": true + }, + "r2-link10": { + "next_hop_self": true + }, + "r2-link11": { + "next_hop_self": true + }, + "r2-link12": { + "next_hop_self": true + }, + "r2-link13": { + "next_hop_self": true + }, + "r2-link14": { + "next_hop_self": true + }, + "r2-link15": { + "next_hop_self": true + }, + "r2-link16": { + "next_hop_self": true + }, + "r2-link17": { + "next_hop_self": true + }, + "r2-link18": { + "next_hop_self": true + }, + "r2-link19": { + "next_hop_self": true + }, + "r2-link20": { + "next_hop_self": true + }, + "r2-link21": { + "next_hop_self": true + }, + "r2-link22": { + "next_hop_self": true + }, + "r2-link23": { + "next_hop_self": true + }, + "r2-link24": { + "next_hop_self": true + }, + "r2-link25": { + "next_hop_self": true + }, + "r2-link26": { + "next_hop_self": true + }, + "r2-link27": { + "next_hop_self": true + }, + "r2-link28": { + "next_hop_self": true + }, + "r2-link29": { + "next_hop_self": true + }, + "r2-link30": { + "next_hop_self": true + }, + "r2-link31": { + "next_hop_self": true + }, + "r2-link32": { + "next_hop_self": true + } + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "next_hop_self": true + }, + "r2-link2": { + "next_hop_self": true + }, + "r2-link3": { + "next_hop_self": true + }, + "r2-link4": { + "next_hop_self": true + }, + "r2-link5": { + "next_hop_self": true + }, + "r2-link6": { + "next_hop_self": true + }, + "r2-link7": { + "next_hop_self": true + }, + "r2-link8": { + "next_hop_self": true + }, + "r2-link9": { + "next_hop_self": true + }, + "r2-link10": { + "next_hop_self": true + }, + "r2-link11": { + "next_hop_self": true + }, + "r2-link12": { + "next_hop_self": true + }, + "r2-link13": { + "next_hop_self": true + }, + "r2-link14": { + "next_hop_self": true + }, + "r2-link15": { + "next_hop_self": true + }, + "r2-link16": { + "next_hop_self": true + }, + "r2-link17": { + "next_hop_self": true + }, + "r2-link18": { + "next_hop_self": true + }, + "r2-link19": { + "next_hop_self": true + }, + "r2-link20": { + "next_hop_self": true + }, + "r2-link21": { + "next_hop_self": true + }, + "r2-link22": { + "next_hop_self": true + }, + "r2-link23": { + "next_hop_self": true + }, + "r2-link24": { + "next_hop_self": true + }, + "r2-link25": { + "next_hop_self": true + }, + "r2-link26": { + "next_hop_self": true + }, + "r2-link27": { + "next_hop_self": true + }, + "r2-link28": { + "next_hop_self": true + }, + "r2-link29": { + "next_hop_self": true + }, + "r2-link30": { + "next_hop_self": true + }, + "r2-link31": { + "next_hop_self": true + }, + "r2-link32": { + "next_hop_self": true + } + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link8": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link9": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link10": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link11": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link12": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link13": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link14": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link15": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link16": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link17": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link18": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link19": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link20": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link21": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link22": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link23": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link24": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link25": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link26": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link27": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link28": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link29": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link30": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link31": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link32": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "maximum_paths": { + "ibgp": 32 + }, + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {}, + "r3-link8": {}, + "r3-link9": {}, + "r3-link10": {}, + "r3-link11": {}, + "r3-link12": {}, + "r3-link13": {}, + "r3-link14": {}, + "r3-link15": {}, + "r3-link16": {}, + "r3-link17": {}, + "r3-link18": {}, + "r3-link19": {}, + "r3-link20": {}, + "r3-link21": {}, + "r3-link22": {}, + "r3-link23": {}, + "r3-link24": {}, + "r3-link25": {}, + "r3-link26": {}, + "r3-link27": {}, + "r3-link28": {}, + "r3-link29": {}, + "r3-link30": {}, + "r3-link31": {}, + "r3-link32": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ibgp": 32 + }, + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link5": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link6": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link7": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link8": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link9": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link10": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link11": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link12": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link13": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link14": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link15": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link16": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link17": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link18": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link19": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link20": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link21": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link22": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link23": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link24": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link25": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link26": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link27": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link28": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link29": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link30": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link31": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link32": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_ecmp_topo2/test_ebgp_ecmp_topo2.py b/tests/topotests/bgp_ecmp_topo2/test_ebgp_ecmp_topo2.py new file mode 100644 index 0000000..8362776 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo2/test_ebgp_ecmp_topo2.py @@ -0,0 +1,746 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# + + +""" +Following tests are covered to test ecmp functionality on EBGP. +1. Verify routes installed as per maximum-paths configuration (8/16/32) +2. Disable/Shut selected paths nexthops and verify other next are installed in + the RIB of DUT. Enable interfaces and verify RIB count. +3. Verify BGP table and RIB in DUT after clear BGP routes and neighbors. +4. Verify routes are cleared from BGP and RIB table of DUT when + redistribute static configuration is removed. +5. Shut BGP neighbors one by one and verify BGP and routing table updated + accordingly in DUT +6. Delete static routes and verify routers are cleared from BGP table and RIB + of DUT. +7. Verify routes are cleared from BGP and RIB table of DUT when advertise + network configuration is removed. +""" +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + verify_rib, + create_static_routes, + check_address_types, + interface_status, + reset_config_on_routers, + required_linux_kernel_version, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +# Global variables +NEXT_HOPS = {"ipv4": [], "ipv6": []} +INTF_LIST_R3 = [] +INTF_LIST_R2 = [] +NETWORK = {"ipv4": "11.0.20.1/32", "ipv6": "1::/64"} +NEXT_HOP_IP = {"ipv4": "10.0.0.1", "ipv6": "fd00::1"} +BGP_CONVERGENCE = False + + +def setup_module(mod): + """ + Sets up the pytest environment. + + * `mod`: module name + """ + global NEXT_HOPS, INTF_LIST_R3, INTF_LIST_R2, TEST_STATIC + global ADDR_TYPES + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >=4.15") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/ebgp_ecmp_topo2.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # tgen.mininet_cli() + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + link_data = [ + val for links, val in topo["routers"]["r2"]["links"].items() if "r3" in links + ] + for adt in ADDR_TYPES: + NEXT_HOPS[adt] = [val[adt].split("/")[0] for val in link_data] + if adt == "ipv4": + NEXT_HOPS[adt] = sorted(NEXT_HOPS[adt], key=lambda x: int(x.split(".")[2])) + elif adt == "ipv6": + NEXT_HOPS[adt] = sorted( + NEXT_HOPS[adt], key=lambda x: int(x.split(":")[-3], 16) + ) + + INTF_LIST_R2 = [val["interface"].split("/")[0] for val in link_data] + INTF_LIST_R2 = sorted(INTF_LIST_R2, key=lambda x: int(x.split("eth")[1])) + + link_data = [ + val for links, val in topo["routers"]["r3"]["links"].items() if "r2" in links + ] + INTF_LIST_R3 = [val["interface"].split("/")[0] for val in link_data] + INTF_LIST_R3 = sorted(INTF_LIST_R3, key=lambda x: int(x.split("eth")[1])) + + # STATIC_ROUTE = True + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def static_or_nw(tgen, topo, tc_name, test_type, dut): + + if test_type == "redist_static": + input_dict_static = { + dut: { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": NEXT_HOP_IP["ipv4"]}, + {"network": NETWORK["ipv6"], "next_hop": NEXT_HOP_IP["ipv6"]}, + ] + } + } + logger.info("Configuring static route on router %s", dut) + result = create_static_routes(tgen, input_dict_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + dut: { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + } + } + } + } + + logger.info("Configuring redistribute static route on router %s", dut) + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + elif test_type == "advertise_nw": + input_dict_nw = { + dut: { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": NETWORK["ipv4"]}] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [{"network": NETWORK["ipv6"]}] + } + }, + } + } + } + } + + logger.info( + "Advertising networks %s %s from router %s", + NETWORK["ipv4"], + NETWORK["ipv6"], + dut, + ) + result = create_router_bgp(tgen, topo, input_dict_nw) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + +@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"]) +@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) +def test_modify_ecmp_max_paths(request, ecmp_num, test_type): + """ + Verify routes installed as per maximum-paths + configuration (8/16/32). + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + reset_config_on_routers(tgen) + + static_or_nw(tgen, topo, tc_name, test_type, "r2") + + input_dict = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "maximum_paths": { + "ebgp": ecmp_num, + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ebgp": ecmp_num, + } + } + }, + } + } + } + } + + logger.info("Configuring bgp maximum-paths %s on router r3", ecmp_num) + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + + # Only test the count of nexthops; the actual nexthop addresses + # can vary and are not deterministic. + # + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)], + protocol=protocol, + count_only=True, + ) + + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"]) +@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) +def test_ecmp_after_clear_bgp(request, ecmp_num, test_type): + """Verify BGP table and RIB in DUT after clear BGP routes and neighbors""" + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + reset_config_on_routers(tgen) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + static_or_nw(tgen, topo, tc_name, test_type, "r2") + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Clear BGP + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, dut) + + # Verify BGP convergence + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_ecmp_remove_redistribute_static(request): + """Verify routes are cleared from BGP and RIB table of DUT when + redistribute static configuration is removed.""" + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + reset_config_on_routers(tgen) + static_or_nw(tgen, topo, tc_name, "redist_static", "r2") + for addr_type in ADDR_TYPES: + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + "ipv6": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + } + } + } + } + + logger.info("Remove redistribute static") + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3 are deleted", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=[], + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected: Routes still present in {} RIB. Found: {}".format( + tc_name, dut, result + ) + + logger.info("Enable redistribute static") + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) +def test_ecmp_shut_bgp_neighbor(request, test_type): + """Shut BGP neighbors one by one and verify BGP and routing table updated + accordingly in DUT""" + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + logger.info(INTF_LIST_R2) + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + reset_config_on_routers(tgen) + static_or_nw(tgen, topo, tc_name, test_type, "r2") + + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for intf_num in range(len(INTF_LIST_R2) + 1, 16): + intf_val = INTF_LIST_R2[intf_num : intf_num + 16] + + input_dict_1 = {"r2": {"interface_list": [intf_val], "status": "down"}} + logger.info("Shutting down neighbor interface {} on r2".format(intf_val)) + result = interface_status(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + if intf_num + 16 < 32: + check_hops = NEXT_HOPS[addr_type] + else: + check_hops = [] + + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, addr_type, dut, input_dict, next_hop=check_hops, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_1 = {"r2": {"interface_list": INTF_LIST_R2, "status": "up"}} + + logger.info("Enabling all neighbor interface {} on r2") + result = interface_status(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + static_or_nw(tgen, topo, tc_name, test_type, "r2") + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_ecmp_remove_static_route(request): + """ + Delete static routes and verify routers are cleared from BGP table, + and RIB of DUT. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + reset_config_on_routers(tgen) + + static_or_nw(tgen, topo, tc_name, "redist_static", "r2") + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "delete": True, + } + ] + } + } + + logger.info("Remove static routes") + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info("Verifying %s routes on r3 are removed", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_2, + next_hop=[], + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected: Routes still present in {} RIB. Found: {}".format( + tc_name, dut, result + ) + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_4 = { + "r2": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Enable static route") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + +def test_ecmp_remove_nw_advertise(request): + """ + Verify routes are cleared from BGP and RIB table of DUT, + when advertise network configuration is removed + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + reset_config_on_routers(tgen) + static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_3 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": NETWORK["ipv4"], "delete": True} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": NETWORK["ipv6"], "delete": True} + ] + } + }, + } + } + } + } + + logger.info("Withdraw advertised networks") + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=[], + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected: Routes still present in {} RIB. Found: {}".format( + tc_name, dut, result + ) + + static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ecmp_topo2/test_ibgp_ecmp_topo2.py b/tests/topotests/bgp_ecmp_topo2/test_ibgp_ecmp_topo2.py new file mode 100644 index 0000000..b347042 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo2/test_ibgp_ecmp_topo2.py @@ -0,0 +1,747 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# + + +""" +Following tests are covered to test ecmp functionality on EBGP. +1. Verify routes installed as per maximum-paths configuration (8/16/32) +2. Disable/Shut selected paths nexthops and verify other next are installed in + the RIB of DUT. Enable interfaces and verify RIB count. +3. Verify BGP table and RIB in DUT after clear BGP routes and neighbors. +4. Verify routes are cleared from BGP and RIB table of DUT when + redistribute static configuration is removed. +5. Shut BGP neighbors one by one and verify BGP and routing table updated + accordingly in DUT +6. Delete static routes and verify routers are cleared from BGP table and RIB + of DUT. +7. Verify routes are cleared from BGP and RIB table of DUT when advertise + network configuration is removed. +""" +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + verify_rib, + create_static_routes, + check_address_types, + interface_status, + reset_config_on_routers, + required_linux_kernel_version, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +# Global variables +NEXT_HOPS = {"ipv4": [], "ipv6": []} +INTF_LIST_R3 = [] +INTF_LIST_R2 = [] +NETWORK = {"ipv4": "11.0.20.1/32", "ipv6": "1::/64"} +NEXT_HOP_IP = {"ipv4": "10.0.0.1", "ipv6": "fd00::1"} +BGP_CONVERGENCE = False + + +def setup_module(mod): + """ + Sets up the pytest environment. + + * `mod`: module name + """ + global NEXT_HOPS, INTF_LIST_R3, INTF_LIST_R2, TEST_STATIC + global ADDR_TYPES + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >=4.15") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/ibgp_ecmp_topo2.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # tgen.mininet_cli() + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + for addr_type in ADDR_TYPES: + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + link_data = [ + val for links, val in topo["routers"]["r2"]["links"].items() if "r3" in links + ] + for adt in ADDR_TYPES: + NEXT_HOPS[adt] = [val[adt].split("/")[0] for val in link_data] + if adt == "ipv4": + NEXT_HOPS[adt] = sorted(NEXT_HOPS[adt], key=lambda x: int(x.split(".")[2])) + elif adt == "ipv6": + NEXT_HOPS[adt] = sorted( + NEXT_HOPS[adt], key=lambda x: int(x.split(":")[-3], 16) + ) + + INTF_LIST_R2 = [val["interface"].split("/")[0] for val in link_data] + INTF_LIST_R2 = sorted(INTF_LIST_R2, key=lambda x: int(x.split("eth")[1])) + + link_data = [ + val for links, val in topo["routers"]["r3"]["links"].items() if "r2" in links + ] + INTF_LIST_R3 = [val["interface"].split("/")[0] for val in link_data] + INTF_LIST_R3 = sorted(INTF_LIST_R3, key=lambda x: int(x.split("eth")[1])) + + # STATIC_ROUTE = True + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def static_or_nw(tgen, topo, tc_name, test_type, dut): + + if test_type == "redist_static": + input_dict_static = { + dut: { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": NEXT_HOP_IP["ipv4"]}, + {"network": NETWORK["ipv6"], "next_hop": NEXT_HOP_IP["ipv6"]}, + ] + } + } + logger.info("Configuring static route on router %s", dut) + result = create_static_routes(tgen, input_dict_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + dut: { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + } + } + } + } + + logger.info("Configuring redistribute static route on router %s", dut) + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + elif test_type == "advertise_nw": + input_dict_nw = { + dut: { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": NETWORK["ipv4"]}] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [{"network": NETWORK["ipv6"]}] + } + }, + } + } + } + } + + logger.info( + "Advertising networks %s %s from router %s", + NETWORK["ipv4"], + NETWORK["ipv6"], + dut, + ) + result = create_router_bgp(tgen, topo, input_dict_nw) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + +@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"]) +@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) +def test_modify_ecmp_max_paths(request, ecmp_num, test_type): + """ + Verify routes installed as per maximum-paths + configuration (8/16/32). + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + reset_config_on_routers(tgen) + + static_or_nw(tgen, topo, tc_name, test_type, "r2") + + input_dict = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "maximum_paths": { + "ibgp": ecmp_num, + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ibgp": ecmp_num, + } + } + }, + } + } + } + } + + logger.info("Configuring bgp maximum-paths %s on router r3", ecmp_num) + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + + # Test only the count of nexthops, not the specific nexthop addresses - + # they're not deterministic + # + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)], + protocol=protocol, + count_only=True, + ) + + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"]) +@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) +def test_ecmp_after_clear_bgp(request, ecmp_num, test_type): + """Verify BGP table and RIB in DUT after clear BGP routes and neighbors""" + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + reset_config_on_routers(tgen) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + static_or_nw(tgen, topo, tc_name, test_type, "r2") + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Clear BGP + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, dut) + + # Verify BGP convergence + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_ecmp_remove_redistribute_static(request): + """Verify routes are cleared from BGP and RIB table of DUT when + redistribute static configuration is removed.""" + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + reset_config_on_routers(tgen) + static_or_nw(tgen, topo, tc_name, "redist_static", "r2") + for addr_type in ADDR_TYPES: + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + "ipv6": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + } + } + } + } + + logger.info("Remove redistribute static") + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3 are deleted", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=[], + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected: Routes still present in {} RIB. Found: {}".format( + tc_name, dut, result + ) + + logger.info("Enable redistribute static") + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) +def test_ecmp_shut_bgp_neighbor(request, test_type): + """Shut BGP neighbors one by one and verify BGP and routing table updated + accordingly in DUT""" + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + logger.info(INTF_LIST_R2) + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + reset_config_on_routers(tgen) + static_or_nw(tgen, topo, tc_name, test_type, "r2") + + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for intf_num in range(len(INTF_LIST_R2) + 1, 16): + intf_val = INTF_LIST_R2[intf_num : intf_num + 16] + + input_dict_1 = {"r2": {"interface_list": [intf_val], "status": "down"}} + logger.info("Shutting down neighbor interface {} on r2".format(intf_val)) + result = interface_status(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + if intf_num + 16 < 32: + check_hops = NEXT_HOPS[addr_type] + else: + check_hops = [] + + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, addr_type, dut, input_dict, next_hop=check_hops, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_1 = {"r2": {"interface_list": INTF_LIST_R2, "status": "up"}} + + logger.info("Enabling all neighbor interface {} on r2") + result = interface_status(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + static_or_nw(tgen, topo, tc_name, test_type, "r2") + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_ecmp_remove_static_route(request): + """ + Delete static routes and verify routers are cleared from BGP table, + and RIB of DUT. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + reset_config_on_routers(tgen) + + static_or_nw(tgen, topo, tc_name, "redist_static", "r2") + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "delete": True, + } + ] + } + } + + logger.info("Remove static routes") + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info("Verifying %s routes on r3 are removed", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_2, + next_hop=[], + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected: Routes still present in {} RIB. Found: {}".format( + tc_name, dut, result + ) + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_4 = { + "r2": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Enable static route") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_ecmp_remove_nw_advertise(request): + """ + Verify routes are cleared from BGP and RIB table of DUT, + when advertise network configuration is removed + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + reset_config_on_routers(tgen) + static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_3 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": NETWORK["ipv4"], "delete": True} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": NETWORK["ipv6"], "delete": True} + ] + } + }, + } + } + } + } + + logger.info("Withdraw advertised networks") + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=[], + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected: Routes still present in {} RIB. Found: {}".format( + tc_name, dut, result + ) + + static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ecmp_topo3/ibgp_ecmp_topo3.json b/tests/topotests/bgp_ecmp_topo3/ibgp_ecmp_topo3.json new file mode 100644 index 0000000..b01f902 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo3/ibgp_ecmp_topo3.json @@ -0,0 +1,232 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 60, + "holddowntimer": 180, + "next_hop_self": true + }, + "r2-link2": { + "keepalivetimer": 60, + "holddowntimer": 180, + "next_hop_self": true + } + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 60, + "holddowntimer": 180, + "next_hop_self": true + }, + "r2-link2": { + "keepalivetimer": 60, + "holddowntimer": 180, + "next_hop_self": true + } + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "maximum_paths": { + "ibgp": 2 + }, + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 60, + "holddowntimer": 180 + }, + "r3-link2": { + "keepalivetimer": 60, + "holddowntimer": 180 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ibgp": 2 + }, + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 60, + "holddowntimer": 180, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link2": { + "keepalivetimer": 60, + "holddowntimer": 180, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py b/tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py new file mode 100644 index 0000000..366cf3d --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# + + +""" +Following tests are covered to test ecmp functionality on iBGP. +1. Verify bgp fast-convergence functionality +""" +import os +import sys +import time +import pytest +import re +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + write_test_header, + write_test_footer, + verify_rib, + create_static_routes, + check_address_types, + reset_config_on_routers, + shutdown_bringup_interface, + apply_raw_config, + start_topology, +) +from lib.topolog import logger +from lib.topojson import build_config_from_json +from lib.bgp import create_router_bgp, verify_bgp_convergence + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +# Global variables +NEXT_HOPS = {"ipv4": [], "ipv6": []} +NETWORK = {"ipv4": "192.168.1.10/32", "ipv6": "fd00:0:0:1::10/128"} +NEXT_HOP_IP = {"ipv4": "10.0.0.1", "ipv6": "fd00::1"} +BGP_CONVERGENCE = False + + +def setup_module(mod): + """ + Sets up the pytest environment. + + * `mod`: module name + """ + global ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + # This function initiates the topology build with Topogen... + json_file = "{}/ibgp_ecmp_topo3.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + # STATIC_ROUTE = True + logger.info("Running setup_module() done") + + +def teardown_module(): + get_topogen().stop_topology() + + +def static_or_nw(tgen, topo, tc_name, test_type, dut): + + if test_type == "redist_static": + input_dict_static = { + dut: { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": NEXT_HOP_IP["ipv4"]}, + {"network": NETWORK["ipv6"], "next_hop": NEXT_HOP_IP["ipv6"]}, + ] + } + } + logger.info("Configuring static route on router %s", dut) + result = create_static_routes(tgen, input_dict_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + dut: { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + } + } + } + } + + logger.info("Configuring redistribute static route on router %s", dut) + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + elif test_type == "advertise_nw": + input_dict_nw = { + dut: { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": NETWORK["ipv4"]}] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [{"network": NETWORK["ipv6"]}] + } + }, + } + } + } + } + + logger.info( + "Advertising networks %s %s from router %s", + NETWORK["ipv4"], + NETWORK["ipv6"], + dut, + ) + result = create_router_bgp(tgen, topo, input_dict_nw) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + +@pytest.mark.parametrize("test_type", ["redist_static"]) +def test_ecmp_fast_convergence(request, test_type, tgen, topo): + """This test is to verify bgp fast-convergence cli functionality""" + + tc_name = request.node.name + write_test_header(tc_name) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + reset_config_on_routers(tgen) + static_or_nw(tgen, topo, tc_name, test_type, "r2") + + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + intf1 = topo["routers"]["r2"]["links"]["r3-link1"]["interface"] + intf2 = topo["routers"]["r2"]["links"]["r3-link2"]["interface"] + + logger.info("Shutdown one of the link b/w r2 and r3") + shutdown_bringup_interface(tgen, "r2", intf1, False) + + logger.info("Verify bgp neighbors are still up") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + logger.info("Shutdown another link b/w r2 and r3") + shutdown_bringup_interface(tgen, "r2", intf2, False) + + logger.info("Wait for 10 sec and make sure bgp neighbors are still up") + sleep(10) + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + logger.info("No shut links b/w r2 and r3") + shutdown_bringup_interface(tgen, "r2", intf1, True) + shutdown_bringup_interface(tgen, "r2", intf2, True) + + logger.info("Ensure that the links are still up") + result = verify_bgp_convergence(tgen, topo) + + logger.info("Enable bgp fast-convergence cli") + raw_config = { + "r2": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r2"]["bgp"]["local_as"]), + "bgp fast-convergence", + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + logger.info("Ensure BGP has processed the cli") + r2 = tgen.gears["r2"] + output = r2.vtysh_cmd("show run") + verify = re.search(r"fast-convergence", output) + assert verify is not None, "r2 does not have the fast convergence command yet" + + logger.info("Shutdown one link b/w r2 and r3") + shutdown_bringup_interface(tgen, "r2", intf1, False) + + logger.info("Verify bgp neighbors goes down immediately") + result = verify_bgp_convergence(tgen, topo, dut="r2", expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: BGP should not be converged for {} \n " + "Found: {}".format(tc_name, "r2", result) + ) + + logger.info("Shutdown second link b/w r2 and r3") + shutdown_bringup_interface(tgen, "r2", intf2, False) + + logger.info("Verify bgp neighbors goes down immediately") + result = verify_bgp_convergence(tgen, topo, dut="r2", expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: BGP should not be converged for {} \n " + "Found: {}".format(tc_name, "r2", result) + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_evpn_mh/evpn-mh-topo-tests.pdf b/tests/topotests/bgp_evpn_mh/evpn-mh-topo-tests.pdf new file mode 100644 index 0000000..8858e21 Binary files /dev/null and b/tests/topotests/bgp_evpn_mh/evpn-mh-topo-tests.pdf differ diff --git a/tests/topotests/bgp_evpn_mh/hostd11/evpn.conf b/tests/topotests/bgp_evpn_mh/hostd11/evpn.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_mh/hostd11/pim.conf b/tests/topotests/bgp_evpn_mh/hostd11/pim.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_mh/hostd11/zebra.conf b/tests/topotests/bgp_evpn_mh/hostd11/zebra.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_mh/hostd12/evpn.conf b/tests/topotests/bgp_evpn_mh/hostd12/evpn.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_mh/hostd12/pim.conf b/tests/topotests/bgp_evpn_mh/hostd12/pim.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_mh/hostd12/zebra.conf b/tests/topotests/bgp_evpn_mh/hostd12/zebra.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_mh/hostd21/evpn.conf b/tests/topotests/bgp_evpn_mh/hostd21/evpn.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_mh/hostd21/pim.conf b/tests/topotests/bgp_evpn_mh/hostd21/pim.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_mh/hostd21/zebra.conf b/tests/topotests/bgp_evpn_mh/hostd21/zebra.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_mh/hostd22/evpn.conf b/tests/topotests/bgp_evpn_mh/hostd22/evpn.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_mh/hostd22/pim.conf b/tests/topotests/bgp_evpn_mh/hostd22/pim.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_mh/hostd22/zebra.conf b/tests/topotests/bgp_evpn_mh/hostd22/zebra.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_mh/leaf1/evpn.conf b/tests/topotests/bgp_evpn_mh/leaf1/evpn.conf new file mode 100644 index 0000000..33b6d08 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/leaf1/evpn.conf @@ -0,0 +1,21 @@ +frr defaults datacenter +! +router bgp 65101 + bgp router-id 192.168.100.13 + no bgp ebgp-requires-policy + neighbor 192.168.50.1 remote-as external + neighbor 192.168.51.1 remote-as external + neighbor 192.168.1.2 remote-as external + neighbor 192.168.2.2 remote-as external + neighbor 192.168.3.2 remote-as external + neighbor 192.168.4.2 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.50.1 activate + neighbor 192.168.51.1 activate + neighbor 192.168.1.2 activate + neighbor 192.168.2.2 activate + neighbor 192.168.3.2 activate + neighbor 192.168.4.2 activate + exit-address-family +! diff --git a/tests/topotests/bgp_evpn_mh/leaf1/pim.conf b/tests/topotests/bgp_evpn_mh/leaf1/pim.conf new file mode 100644 index 0000000..a4de64d --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/leaf1/pim.conf @@ -0,0 +1,26 @@ +#debug pim packet +#debug pim packet register +#debug pim event +#debug pim trace +ip pim ecmp +ip pim rp 192.168.100.13 +ip pim spt-switchover infinity-and-beyond +! +int leaf1-eth0 + ip pim +! +int leaf1-eth1 + ip pim +! +int leaf1-eth2 + ip pim +! +int leaf1-eth3 + ip pim +! +int leaf1-eth4 + ip pim +! +int leaf1-eth5 + ip pim +! diff --git a/tests/topotests/bgp_evpn_mh/leaf1/zebra.conf b/tests/topotests/bgp_evpn_mh/leaf1/zebra.conf new file mode 100644 index 0000000..f666f98 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/leaf1/zebra.conf @@ -0,0 +1,30 @@ +# spine 1 connection +int leaf1-eth0 + description spine1 connection + ip addr 192.168.50.2/24 + +#spine 2 connection +int leaf1-eth1 + description spine2 connection + ip addr 192.168.51.2/24 + +#torm11 connection +int leaf1-eth2 + description torm11 connection + ip addr 192.168.1.1/24 +! +#torm12 connection +int leaf1-eth3 + description torm12 connection + ip addr 192.168.2.1/24 +! +#torm21 connection +int leaf1-eth4 + description torm21 connection + ip addr 192.168.3.1/24 +! +#torm22 connection +int leaf1-eth5 + descriptoin torm22 connection + ip addr 192.168.4.1/24 +! diff --git a/tests/topotests/bgp_evpn_mh/leaf2/evpn.conf b/tests/topotests/bgp_evpn_mh/leaf2/evpn.conf new file mode 100644 index 0000000..428998b --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/leaf2/evpn.conf @@ -0,0 +1,21 @@ +frr defaults datacenter +! +router bgp 65101 + bgp router-id 192.168.100.14 + no bgp ebgp-requires-policy + neighbor 192.168.61.1 remote-as external + neighbor 192.168.51.1 remote-as external + neighbor 192.168.5.2 remote-as external + neighbor 192.168.6.2 remote-as external + neighbor 192.168.7.2 remote-as external + neighbor 192.168.8.2 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.61.1 activate + neighbor 192.168.51.1 activate + neighbor 192.168.5.2 activate + neighbor 192.168.6.2 activate + neighbor 192.168.7.2 activate + neighbor 192.168.8.2 activate + exit-address-family +! diff --git a/tests/topotests/bgp_evpn_mh/leaf2/pim.conf b/tests/topotests/bgp_evpn_mh/leaf2/pim.conf new file mode 100644 index 0000000..3abd969 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/leaf2/pim.conf @@ -0,0 +1,20 @@ +#debug pim packet register +#debug pim packet +#debug pim event +#debug pim trace +ip pim ecmp +ip pim rp 192.168.100.13 +ip pim spt-switchover infinity-and-beyond +! +int leaf2-eth0 + ip pim +! +int leaf2-eth1 + ip pim +! +int leaf2-eth2 + ip pim +! +int leaf2-eth3 + ip pim +! diff --git a/tests/topotests/bgp_evpn_mh/leaf2/zebra.conf b/tests/topotests/bgp_evpn_mh/leaf2/zebra.conf new file mode 100644 index 0000000..ff680cf --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/leaf2/zebra.conf @@ -0,0 +1,24 @@ +# spine1 connection +int leaf2-eth0 + ip addr 192.168.51.2/24 +! +# spine2 connection +int leaf2-eth1 + ip addr 192.168.61.2/24 +! +#torm11 connection +int leaf2-eth2 + ip addr 192.168.5.1/24 +! +#torm12 connection +int leaf2-eth3 + ip addr 192.168.6.1/24 +! +#torm21 connection +int leaf2-eth4 + ip addr 192.168.7.1/24 +! +#torm22 connection +int leaf2-eth5 + ip addr 192.168.8.1/24 +! diff --git a/tests/topotests/bgp_evpn_mh/spine1/evpn.conf b/tests/topotests/bgp_evpn_mh/spine1/evpn.conf new file mode 100644 index 0000000..b9fce46 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/spine1/evpn.conf @@ -0,0 +1,13 @@ +frr defaults datacenter +! +router bgp 65001 + bgp router-id 192.168.100.13 + no bgp ebgp-requires-policy + neighbor 192.168.50.2 remote-as external + neighbor 192.168.51.2 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.50.2 activate + neighbor 192.168.51.2 activate + exit-address-family +! diff --git a/tests/topotests/bgp_evpn_mh/spine1/pim.conf b/tests/topotests/bgp_evpn_mh/spine1/pim.conf new file mode 100644 index 0000000..018d244 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/spine1/pim.conf @@ -0,0 +1,18 @@ +ip pim ecmp +ip pim rp 192.168.100.13 +#debug pim packets +#debug pim packet register +#debug pim event +#debug pim trace +#debug pim vxlan +ip pim spt-switchover infinity-and-beyond +! +int lo + ip pim +! +int spine1-eth0 + ip pim +! +int spine1-eth1 + ip pim +! diff --git a/tests/topotests/bgp_evpn_mh/spine1/zebra.conf b/tests/topotests/bgp_evpn_mh/spine1/zebra.conf new file mode 100644 index 0000000..607a5e8 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/spine1/zebra.conf @@ -0,0 +1,11 @@ +# leaf1 connection +int spine1-eth0 + ip addr 192.168.50.1/24 +! +# leaf2 connection +int spine1-eth1 + ip addr 192.168.51.1/24 +! +int lo + ip addr 192.168.100.13/32 + ip addr 192.168.100.100/32 diff --git a/tests/topotests/bgp_evpn_mh/spine2/evpn.conf b/tests/topotests/bgp_evpn_mh/spine2/evpn.conf new file mode 100644 index 0000000..1430e10 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/spine2/evpn.conf @@ -0,0 +1,13 @@ +frr defaults datacenter +! +router bgp 65001 + bgp router-id 192.168.100.14 + no bgp ebgp-requires-policy + neighbor 192.168.60.2 remote-as external + neighbor 192.168.61.2 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.60.2 activate + neighbor 192.168.61.2 activate + exit-address-family +! diff --git a/tests/topotests/bgp_evpn_mh/spine2/pim.conf b/tests/topotests/bgp_evpn_mh/spine2/pim.conf new file mode 100644 index 0000000..0ece2ff --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/spine2/pim.conf @@ -0,0 +1,13 @@ +ip pim ecmp +ip pim rp 192.168.100.13 +ip pim spt-switchover infinity-and-beyond +! +int lo + ip pim +! +int spine2-eth0 + ip pim +! +int spine2-eth1 + ip pim +! diff --git a/tests/topotests/bgp_evpn_mh/spine2/zebra.conf b/tests/topotests/bgp_evpn_mh/spine2/zebra.conf new file mode 100644 index 0000000..c8cec14 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/spine2/zebra.conf @@ -0,0 +1,13 @@ +# leaf1 connection +int spine2-eth0 + description leaf1 connection + ip addr 192.168.60.1/24 +! +# leaf2 connection +int spine2-eth1 + description leaf2 connection + ip addr 192.168.61.1/24 +! +int lo + ip addr 192.168.100.14/32 + ip addr 192.168.100.100/32 diff --git a/tests/topotests/bgp_evpn_mh/test_evpn_mh.py b/tests/topotests/bgp_evpn_mh/test_evpn_mh.py new file mode 100644 index 0000000..ec52278 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/test_evpn_mh.py @@ -0,0 +1,819 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_evpn_mh.py +# +# Copyright (c) 2020 by +# Cumulus Networks, Inc. +# Anuradha Karuppiah +# + +""" +test_evpn_mh.py: Testing EVPN multihoming + +""" + +import os +import sys +import subprocess +from functools import partial + +import pytest +import json +import platform +from functools import partial + +pytestmark = [pytest.mark.bgpd, pytest.mark.pimd] + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest + +# Required to instantiate the topology builder class. +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd, pytest.mark.pimd] + +##################################################### +## +## Network Topology Definition +## +## See topology picture at evpn-mh-topo-tests.pdf +##################################################### + + +def build_topo(tgen): + """ + EVPN Multihoming Topology - + 1. Two level CLOS + 2. Two spine switches - spine1, spine2 + 3. Two racks with Top-of-Rack switches per rack - tormx1, tormx2 + 4. Two dual attached hosts per-rack - hostdx1, hostdx2 + """ + + tgen.add_router("spine1") + tgen.add_router("spine2") + tgen.add_router("leaf1") + tgen.add_router("leaf2") + tgen.add_router("torm11") + tgen.add_router("torm12") + tgen.add_router("torm21") + tgen.add_router("torm22") + tgen.add_router("hostd11") + tgen.add_router("hostd12") + tgen.add_router("hostd21") + tgen.add_router("hostd22") + + # On main router + # First switch is for a dummy interface (for local network) + + ##################### spine1 ######################## + # spine1-eth0 is connected to leaf1-eth0 + switch = tgen.add_switch("sw1") + switch.add_link(tgen.gears["spine1"]) + switch.add_link(tgen.gears["leaf1"]) + + # spine1-eth1 is connected to leaf2-eth0 + switch = tgen.add_switch("sw2") + switch.add_link(tgen.gears["spine1"]) + switch.add_link(tgen.gears["leaf2"]) + + # spine2-eth0 is connected to leaf1-eth1 + switch = tgen.add_switch("sw3") + switch.add_link(tgen.gears["spine2"]) + switch.add_link(tgen.gears["leaf1"]) + + # spine2-eth1 is connected to leaf2-eth1 + switch = tgen.add_switch("sw4") + switch.add_link(tgen.gears["spine2"]) + switch.add_link(tgen.gears["leaf2"]) + + ################## leaf1 ########################## + # leaf1-eth2 is connected to torm11-eth0 + switch = tgen.add_switch("sw5") + switch.add_link(tgen.gears["leaf1"]) + switch.add_link(tgen.gears["torm11"]) + + # leaf1-eth3 is connected to torm12-eth0 + switch = tgen.add_switch("sw6") + switch.add_link(tgen.gears["leaf1"]) + switch.add_link(tgen.gears["torm12"]) + + # leaf1-eth4 is connected to torm21-eth0 + switch = tgen.add_switch("sw7") + switch.add_link(tgen.gears["leaf1"]) + switch.add_link(tgen.gears["torm21"]) + + # leaf1-eth5 is connected to torm22-eth0 + switch = tgen.add_switch("sw8") + switch.add_link(tgen.gears["leaf1"]) + switch.add_link(tgen.gears["torm22"]) + + ##################### leaf2 ######################## + # leaf2-eth2 is connected to torm11-eth1 + switch = tgen.add_switch("sw9") + switch.add_link(tgen.gears["leaf2"]) + switch.add_link(tgen.gears["torm11"]) + + # leaf2-eth3 is connected to torm12-eth1 + switch = tgen.add_switch("sw10") + switch.add_link(tgen.gears["leaf2"]) + switch.add_link(tgen.gears["torm12"]) + + # leaf2-eth4 is connected to torm21-eth1 + switch = tgen.add_switch("sw11") + switch.add_link(tgen.gears["leaf2"]) + switch.add_link(tgen.gears["torm21"]) + + # leaf2-eth5 is connected to torm22-eth1 + switch = tgen.add_switch("sw12") + switch.add_link(tgen.gears["leaf2"]) + switch.add_link(tgen.gears["torm22"]) + + ##################### torm11 ######################## + # torm11-eth2 is connected to hostd11-eth0 + switch = tgen.add_switch("sw13") + switch.add_link(tgen.gears["torm11"]) + switch.add_link(tgen.gears["hostd11"]) + + # torm11-eth3 is connected to hostd12-eth0 + switch = tgen.add_switch("sw14") + switch.add_link(tgen.gears["torm11"]) + switch.add_link(tgen.gears["hostd12"]) + + ##################### torm12 ######################## + # torm12-eth2 is connected to hostd11-eth1 + switch = tgen.add_switch("sw15") + switch.add_link(tgen.gears["torm12"]) + switch.add_link(tgen.gears["hostd11"]) + + # torm12-eth3 is connected to hostd12-eth1 + switch = tgen.add_switch("sw16") + switch.add_link(tgen.gears["torm12"]) + switch.add_link(tgen.gears["hostd12"]) + + ##################### torm21 ######################## + # torm21-eth2 is connected to hostd21-eth0 + switch = tgen.add_switch("sw17") + switch.add_link(tgen.gears["torm21"]) + switch.add_link(tgen.gears["hostd21"]) + + # torm21-eth3 is connected to hostd22-eth0 + switch = tgen.add_switch("sw18") + switch.add_link(tgen.gears["torm21"]) + switch.add_link(tgen.gears["hostd22"]) + + ##################### torm22 ######################## + # torm22-eth2 is connected to hostd21-eth1 + switch = tgen.add_switch("sw19") + switch.add_link(tgen.gears["torm22"]) + switch.add_link(tgen.gears["hostd21"]) + + # torm22-eth3 is connected to hostd22-eth1 + switch = tgen.add_switch("sw20") + switch.add_link(tgen.gears["torm22"]) + switch.add_link(tgen.gears["hostd22"]) + + +##################################################### +## +## Tests starting +## +##################################################### + +tor_ips = { + "torm11": "192.168.100.15", + "torm12": "192.168.100.16", + "torm21": "192.168.100.17", + "torm22": "192.168.100.18", +} + +svi_ips = { + "torm11": "45.0.0.2", + "torm12": "45.0.0.3", + "torm21": "45.0.0.4", + "torm22": "45.0.0.5", +} + +tor_ips_rack_1 = {"torm11": "192.168.100.15", "torm12": "192.168.100.16"} + +tor_ips_rack_2 = {"torm21": "192.168.100.17", "torm22": "192.168.100.18"} + +host_es_map = { + "hostd11": "03:44:38:39:ff:ff:01:00:00:01", + "hostd12": "03:44:38:39:ff:ff:01:00:00:02", + "hostd21": "03:44:38:39:ff:ff:02:00:00:01", + "hostd22": "03:44:38:39:ff:ff:02:00:00:02", +} + + +def config_bond(node, bond_name, bond_members, bond_ad_sys_mac, br): + """ + Used to setup bonds on the TORs and hosts for MH + """ + node.run("ip link add dev %s type bond mode 802.3ad" % bond_name) + node.run("ip link set dev %s type bond lacp_rate 1" % bond_name) + node.run("ip link set dev %s type bond miimon 100" % bond_name) + node.run("ip link set dev %s type bond xmit_hash_policy layer3+4" % bond_name) + node.run("ip link set dev %s type bond min_links 1" % bond_name) + node.run( + "ip link set dev %s type bond ad_actor_system %s" % (bond_name, bond_ad_sys_mac) + ) + + for bond_member in bond_members: + node.run("ip link set dev %s down" % bond_member) + node.run("ip link set dev %s master %s" % (bond_member, bond_name)) + node.run("ip link set dev %s up" % bond_member) + + node.run("ip link set dev %s up" % bond_name) + + # if bridge is specified add the bond as a bridge member + if br: + node.run(" ip link set dev %s master bridge" % bond_name) + node.run("/sbin/bridge link set dev %s priority 8" % bond_name) + node.run("/sbin/bridge vlan del vid 1 dev %s" % bond_name) + node.run("/sbin/bridge vlan del vid 1 untagged pvid dev %s" % bond_name) + node.run("/sbin/bridge vlan add vid 1000 dev %s" % bond_name) + node.run("/sbin/bridge vlan add vid 1000 untagged pvid dev %s" % bond_name) + + +def config_mcast_tunnel_termination_device(node): + """ + The kernel requires a device to terminate VxLAN multicast tunnels + when EVPN-PIM is used for flooded traffic + """ + node.run("ip link add dev ipmr-lo type dummy") + node.run("ip link set dev ipmr-lo mtu 16000") + node.run("ip link set dev ipmr-lo mode dormant") + node.run("ip link set dev ipmr-lo up") + + +def config_bridge(node): + """ + Create a VLAN aware bridge + """ + node.run("ip link add dev bridge type bridge stp_state 0") + node.run("ip link set dev bridge type bridge vlan_filtering 1") + node.run("ip link set dev bridge mtu 9216") + node.run("ip link set dev bridge type bridge ageing_time 1800") + node.run("ip link set dev bridge type bridge mcast_snooping 0") + node.run("ip link set dev bridge type bridge vlan_stats_enabled 1") + node.run("ip link set dev bridge up") + node.run("/sbin/bridge vlan add vid 1000 dev bridge") + + +def config_vxlan(node, node_ip): + """ + Create a VxLAN device for VNI 1000 and add it to the bridge. + VLAN-1000 is mapped to VNI-1000. + """ + node.run("ip link add dev vx-1000 type vxlan id 1000 dstport 4789") + node.run("ip link set dev vx-1000 type vxlan nolearning") + node.run("ip link set dev vx-1000 type vxlan local %s" % node_ip) + node.run("ip link set dev vx-1000 type vxlan ttl 64") + node.run("ip link set dev vx-1000 mtu 9152") + node.run("ip link set dev vx-1000 type vxlan dev ipmr-lo group 239.1.1.100") + node.run("ip link set dev vx-1000 up") + + # bridge attrs + node.run("ip link set dev vx-1000 master bridge") + node.run("/sbin/bridge link set dev vx-1000 neigh_suppress on") + node.run("/sbin/bridge link set dev vx-1000 learning off") + node.run("/sbin/bridge link set dev vx-1000 priority 8") + node.run("/sbin/bridge vlan del vid 1 dev vx-1000") + node.run("/sbin/bridge vlan del vid 1 untagged pvid dev vx-1000") + node.run("/sbin/bridge vlan add vid 1000 dev vx-1000") + node.run("/sbin/bridge vlan add vid 1000 untagged pvid dev vx-1000") + + +def config_svi(node, svi_pip): + """ + Create an SVI for VLAN 1000 + """ + node.run("ip link add link bridge name vlan1000 type vlan id 1000 protocol 802.1q") + node.run("ip addr add %s/24 dev vlan1000" % svi_pip) + node.run("ip link set dev vlan1000 up") + node.run("/sbin/sysctl net.ipv4.conf.vlan1000.arp_accept=1") + node.run("ip link add link vlan1000 name vlan1000-v0 type macvlan mode private") + node.run("/sbin/sysctl net.ipv6.conf.vlan1000-v0.accept_dad=0") + node.run("/sbin/sysctl net.ipv6.conf.vlan1000-v0.dad_transmits") + node.run("/sbin/sysctl net.ipv6.conf.vlan1000-v0.dad_transmits=0") + node.run("ip link set dev vlan1000-v0 address 00:00:5e:00:01:01") + node.run("ip link set dev vlan1000-v0 up") + # metric 1024 is not working + node.run("ip addr add 45.0.0.1/24 dev vlan1000-v0") + + +def config_tor(tor_name, tor, tor_ip, svi_pip): + """ + Create the bond/vxlan-bridge on the TOR which acts as VTEP and EPN-PE + """ + # create a device for terminating VxLAN multicast tunnels + config_mcast_tunnel_termination_device(tor) + + # create a vlan aware bridge + config_bridge(tor) + + # create vxlan device and add it to bridge + config_vxlan(tor, tor_ip) + + # create hostbonds and add them to the bridge + if "torm1" in tor_name: + sys_mac = "44:38:39:ff:ff:01" + else: + sys_mac = "44:38:39:ff:ff:02" + bond_member = tor_name + "-eth2" + config_bond(tor, "hostbond1", [bond_member], sys_mac, "bridge") + + bond_member = tor_name + "-eth3" + config_bond(tor, "hostbond2", [bond_member], sys_mac, "bridge") + + # create SVI + config_svi(tor, svi_pip) + + +def config_tors(tgen, tors): + for tor_name in tors: + tor = tgen.gears[tor_name] + config_tor(tor_name, tor, tor_ips.get(tor_name), svi_ips.get(tor_name)) + + +def compute_host_ip_mac(host_name): + host_id = host_name.split("hostd")[1] + host_ip = "45.0.0." + host_id + "/24" + host_mac = "00:00:00:00:00:" + host_id + + return host_ip, host_mac + + +def config_host(host_name, host): + """ + Create the dual-attached bond on host nodes for MH + """ + bond_members = [] + bond_members.append(host_name + "-eth0") + bond_members.append(host_name + "-eth1") + bond_name = "torbond" + config_bond(host, bond_name, bond_members, "00:00:00:00:00:00", None) + + host_ip, host_mac = compute_host_ip_mac(host_name) + host.run("ip addr add %s dev %s" % (host_ip, bond_name)) + host.run("ip link set dev %s address %s" % (bond_name, host_mac)) + + +def config_hosts(tgen, hosts): + for host_name in hosts: + host = tgen.gears[host_name] + config_host(host_name, host) + + +def setup_module(module): + "Setup topology" + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + krel = platform.release() + if topotest.version_cmp(krel, "4.19") < 0: + tgen.errors = "kernel 4.19 needed for multihoming tests" + pytest.skip(tgen.errors) + + tors = [] + tors.append("torm11") + tors.append("torm12") + tors.append("torm21") + tors.append("torm22") + config_tors(tgen, tors) + + hosts = [] + hosts.append("hostd11") + hosts.append("hostd12") + hosts.append("hostd21") + hosts.append("hostd22") + config_hosts(tgen, hosts) + + # tgen.mininet_cli() + # This is a sample of configuration loading. + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_PIM, os.path.join(CWD, "{}/pim.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/evpn.conf".format(rname)) + ) + tgen.start_router() + # tgen.mininet_cli() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def check_local_es(esi, vtep_ips, dut_name, down_vteps): + """ + Check if ES peers are setup correctly on local ESs + """ + peer_ips = [] + if "torm1" in dut_name: + tor_ips_rack = tor_ips_rack_1 + else: + tor_ips_rack = tor_ips_rack_2 + + for tor_name, tor_ip in tor_ips_rack.items(): + if dut_name not in tor_name: + peer_ips.append(tor_ip) + + # remove down VTEPs from the peer check list + peer_set = set(peer_ips) + down_vtep_set = set(down_vteps) + peer_set = peer_set - down_vtep_set + + vtep_set = set(vtep_ips) + diff = peer_set.symmetric_difference(vtep_set) + + return (esi, diff) if diff else None + + +def check_remote_es(esi, vtep_ips, dut_name, down_vteps): + """ + Verify list of PEs associated with a remote ES + """ + remote_ips = [] + + if "torm1" in dut_name: + tor_ips_rack = tor_ips_rack_2 + else: + tor_ips_rack = tor_ips_rack_1 + + for tor_name, tor_ip in tor_ips_rack.items(): + remote_ips.append(tor_ip) + + # remove down VTEPs from the remote check list + remote_set = set(remote_ips) + down_vtep_set = set(down_vteps) + remote_set = remote_set - down_vtep_set + + vtep_set = set(vtep_ips) + diff = remote_set.symmetric_difference(vtep_set) + + return (esi, diff) if diff else None + + +def check_es(dut): + """ + Verify list of PEs associated all ESs, local and remote + """ + bgp_es = dut.vtysh_cmd("show bgp l2vp evpn es json") + bgp_es_json = json.loads(bgp_es) + + result = None + + expected_es_set = set([v for k, v in host_es_map.items()]) + curr_es_set = [] + + # check is ES content is correct + for es in bgp_es_json: + esi = es["esi"] + curr_es_set.append(esi) + types = es["type"] + vtep_ips = [] + for vtep in es.get("vteps", []): + vtep_ips.append(vtep["vtep_ip"]) + + if "local" in types: + result = check_local_es(esi, vtep_ips, dut.name, []) + else: + result = check_remote_es(esi, vtep_ips, dut.name, []) + + if result: + return result + + # check if all ESs are present + curr_es_set = set(curr_es_set) + result = curr_es_set.symmetric_difference(expected_es_set) + + return result if result else None + + +def check_one_es(dut, esi, down_vteps): + """ + Verify list of PEs associated all ESs, local and remote + """ + bgp_es = dut.vtysh_cmd("show bgp l2vp evpn es %s json" % esi) + es = json.loads(bgp_es) + + if not es: + return "esi %s not found" % esi + + esi = es["esi"] + types = es["type"] + vtep_ips = [] + for vtep in es.get("vteps", []): + vtep_ips.append(vtep["vtep_ip"]) + + if "local" in types: + result = check_local_es(esi, vtep_ips, dut.name, down_vteps) + else: + result = check_remote_es(esi, vtep_ips, dut.name, down_vteps) + + return result + + +def test_evpn_es(): + """ + Two ES are setup on each rack. This test checks if - + 1. ES peer has been added to the local ES (via Type-1/EAD route) + 2. The remote ESs are setup with the right list of PEs (via Type-1) + """ + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + dut_name = "torm11" + dut = tgen.gears[dut_name] + test_fn = partial(check_es, dut) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + + assertmsg = '"{}" ES content incorrect'.format(dut_name) + assert result is None, assertmsg + # tgen.mininet_cli() + + +def test_evpn_ead_update(): + """ + Flap a host link one the remote rack and check if the EAD updates + are sent/processed for the corresponding ESI + """ + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # dut on rack1 and host link flap on rack2 + dut_name = "torm11" + dut = tgen.gears[dut_name] + + remote_tor_name = "torm21" + remote_tor = tgen.gears[remote_tor_name] + + host_name = "hostd21" + host = tgen.gears[host_name] + esi = host_es_map.get(host_name) + + # check if the VTEP list is right to start with + down_vteps = [] + test_fn = partial(check_one_es, dut, esi, down_vteps) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + assertmsg = '"{}" ES content incorrect'.format(dut_name) + assert result is None, assertmsg + + # down a remote host link and check if the EAD withdraw is rxed + # Note: LACP is not working as expected so I am temporarily shutting + # down the link on the remote TOR instead of the remote host + remote_tor.run("ip link set dev %s-%s down" % (remote_tor_name, "eth2")) + down_vteps.append(tor_ips.get(remote_tor_name)) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + assertmsg = '"{}" ES incorrect after remote link down'.format(dut_name) + assert result is None, assertmsg + + # bring up remote host link and check if the EAD update is rxed + down_vteps.remove(tor_ips.get(remote_tor_name)) + remote_tor.run("ip link set dev %s-%s up" % (remote_tor_name, "eth2")) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + assertmsg = '"{}" ES incorrect after remote link flap'.format(dut_name) + assert result is None, assertmsg + + # tgen.mininet_cli() + + +def ping_anycast_gw(tgen): + # ping the anycast gw from the local and remote hosts to populate + # the mac address on the PEs + python3_path = tgen.net.get_exec_path(["python3", "python"]) + script_path = os.path.abspath(os.path.join(CWD, "../lib/scapy_sendpkt.py")) + intf = "torbond" + ipaddr = "45.0.0.1" + ping_cmd = [ + python3_path, + script_path, + "--imports=Ether,ARP", + "--interface=" + intf, + 'Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="{}")'.format(ipaddr), + ] + for name in ("hostd11", "hostd21", "hostd12", "hostd22"): + host = tgen.net.hosts[name] + _, stdout, _ = host.cmd_status(ping_cmd, warn=False, stderr=subprocess.STDOUT) + stdout = stdout.strip() + if stdout: + host.logger.debug( + "%s: arping on %s for %s returned: %s", name, intf, ipaddr, stdout + ) + + +def check_mac(dut, vni, mac, m_type, esi, intf, ping_gw=False, tgen=None): + """ + checks if mac is present and if desination matches the one provided + """ + + if ping_gw: + ping_anycast_gw(tgen) + + out = dut.vtysh_cmd("show evpn mac vni %d mac %s json" % (vni, mac)) + + mac_js = json.loads(out) + for mac, info in mac_js.items(): + tmp_esi = info.get("esi", "") + tmp_m_type = info.get("type", "") + tmp_intf = info.get("intf", "") if tmp_m_type == "local" else "" + if tmp_esi == esi and tmp_m_type == m_type and intf == intf: + return None + + return "invalid vni %d mac %s out %s" % (vni, mac, mac_js) + + +def test_evpn_mac(): + """ + 1. Add a MAC on hostd11 and check if the MAC is synced between + torm11 and torm12. And installed as a local MAC. + 2. Add a MAC on hostd21 and check if the MAC is installed as a + remote MAC on torm11 and torm12 + """ + + tgen = get_topogen() + + local_host = tgen.gears["hostd11"] + remote_host = tgen.gears["hostd21"] + tors = [] + tors.append(tgen.gears["torm11"]) + tors.append(tgen.gears["torm12"]) + + vni = 1000 + + # check if the rack-1 host MAC is present on all rack-1 PEs + # and points to local access port + m_type = "local" + _, mac = compute_host_ip_mac(local_host.name) + esi = host_es_map.get(local_host.name) + intf = "hostbond1" + + for tor in tors: + test_fn = partial(check_mac, tor, vni, mac, m_type, esi, intf, True, tgen) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + assertmsg = '"{}" local MAC content incorrect'.format(tor.name) + assert result is None, assertmsg + + # check if the rack-2 host MAC is present on all rack-1 PEs + # and points to the remote ES destination + m_type = "remote" + _, mac = compute_host_ip_mac(remote_host.name) + esi = host_es_map.get(remote_host.name) + intf = "" + + for tor in tors: + test_fn = partial(check_mac, tor, vni, mac, m_type, esi, intf) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + assertmsg = '"{}" remote MAC content incorrect'.format(tor.name) + assert result is None, assertmsg + + +def check_df_role(dut, esi, role): + """ + Return error string if the df role on the dut is different + """ + es_json = dut.vtysh_cmd("show evpn es %s json" % esi) + es = json.loads(es_json) + + if not es: + return "esi %s not found" % esi + + flags = es.get("flags", []) + curr_role = "nonDF" if "nonDF" in flags else "DF" + + if curr_role != role: + return "%s is %s for %s" % (dut.name, curr_role, esi) + + return None + + +def test_evpn_df(): + """ + 1. Check the DF role on all the PEs on rack-1. + 2. Increase the DF preference on the non-DF and check if it becomes + the DF winner. + """ + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # We will run the tests on just one ES + esi = host_es_map.get("hostd11") + intf = "hostbond1" + + tors = [] + tors.append(tgen.gears["torm11"]) + tors.append(tgen.gears["torm12"]) + df_node = "torm11" + + # check roles on rack-1 + for tor in tors: + role = "DF" if tor.name == df_node else "nonDF" + test_fn = partial(check_df_role, tor, esi, role) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + assertmsg = '"{}" DF role incorrect'.format(tor.name) + assert result is None, assertmsg + + # change df preference on the nonDF to make it the df + torm12 = tgen.gears["torm12"] + torm12.vtysh_cmd("conf\ninterface %s\nevpn mh es-df-pref %d" % (intf, 60000)) + df_node = "torm12" + + # re-check roles on rack-1; we should have a new winner + for tor in tors: + role = "DF" if tor.name == df_node else "nonDF" + test_fn = partial(check_df_role, tor, esi, role) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + assertmsg = '"{}" DF role incorrect'.format(tor.name) + assert result is None, assertmsg + + # tgen.mininet_cli() + + +def check_protodown_rc(dut, protodown_rc): + """ + check if specified protodown reason code is set + """ + + out = dut.vtysh_cmd("show evpn json") + + evpn_js = json.loads(out) + tmp_rc = evpn_js.get("protodownReasons", []) + + if protodown_rc: + if protodown_rc not in tmp_rc: + return "protodown %s missing in %s" % (protodown_rc, tmp_rc) + else: + if tmp_rc: + return "unexpected protodown rc %s" % (tmp_rc) + + return None + + +def test_evpn_uplink_tracking(): + """ + 1. Wait for access ports to come out of startup-delay + 2. disable uplinks and check if access ports have been protodowned + 3. enable uplinks and check if access ports have been moved out + of protodown + """ + + tgen = get_topogen() + + dut_name = "torm11" + dut = tgen.gears[dut_name] + + # wait for protodown rc to clear after startup + test_fn = partial(check_protodown_rc, dut, None) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + assertmsg = '"{}" protodown rc incorrect'.format(dut_name) + assert result is None, assertmsg + + # disable the uplinks + dut.run("ip link set %s-eth0 down" % dut_name) + dut.run("ip link set %s-eth1 down" % dut_name) + + # check if the access ports have been protodowned + test_fn = partial(check_protodown_rc, dut, "uplinkDown") + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + assertmsg = '"{}" protodown rc incorrect'.format(dut_name) + assert result is None, assertmsg + + # enable the uplinks + dut.run("ip link set %s-eth0 up" % dut_name) + dut.run("ip link set %s-eth1 up" % dut_name) + + # check if the access ports have been moved out of protodown + test_fn = partial(check_protodown_rc, dut, None) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + assertmsg = '"{}" protodown rc incorrect'.format(dut_name) + assert result is None, assertmsg + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_evpn_mh/torm11/evpn.conf b/tests/topotests/bgp_evpn_mh/torm11/evpn.conf new file mode 100644 index 0000000..2c1c695 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/torm11/evpn.conf @@ -0,0 +1,21 @@ +! +frr defaults datacenter +! +! debug bgp evpn mh es +! debug bgp evpn mh route +! debug bgp zebra +! +! +router bgp 65002 + bgp router-id 192.168.100.15 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.5.1 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.1.1 activate + neighbor 192.168.5.1 activate + advertise-all-vni + advertise-svi-ip + exit-address-family +! diff --git a/tests/topotests/bgp_evpn_mh/torm11/pim.conf b/tests/topotests/bgp_evpn_mh/torm11/pim.conf new file mode 100644 index 0000000..a5d45da --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/torm11/pim.conf @@ -0,0 +1,19 @@ +! +#debug pim packet +#debug pim packet register +#debug pim trace +#debug pim event +#debug pim vxlan +ip pim ecmp +ip pim rp 192.168.100.13 239.1.1.0/24 +ip pim spt-switchover infinity-and-beyond +! +interface lo + ip igmp + ip pim +! +interface torm11-eth0 + ip pim +! +interface torm11-eth1 + ip pim diff --git a/tests/topotests/bgp_evpn_mh/torm11/zebra.conf b/tests/topotests/bgp_evpn_mh/torm11/zebra.conf new file mode 100644 index 0000000..a88370d --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/torm11/zebra.conf @@ -0,0 +1,27 @@ +! debug zebra evpn mh es +! debug zebra evpn mh mac +! debug zebra evpn mh neigh +! debug zebra evpn mh nh +! debug zebra vxlan +! +evpn mh startup-delay 1 +! +int torm11-eth0 + ip addr 192.168.1.2/24 + evpn mh uplink +! +int torm11-eth1 + ip addr 192.168.5.2/24 + evpn mh uplink +! +int lo + ip addr 192.168.100.15/32 +! +interface hostbond1 + evpn mh es-id 1 + evpn mh es-sys-mac 44:38:39:ff:ff:01 +! +interface hostbond2 + evpn mh es-id 2 + evpn mh es-sys-mac 44:38:39:ff:ff:01 +! diff --git a/tests/topotests/bgp_evpn_mh/torm12/evpn.conf b/tests/topotests/bgp_evpn_mh/torm12/evpn.conf new file mode 100644 index 0000000..8b0ce1d --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/torm12/evpn.conf @@ -0,0 +1,21 @@ +! +frr defaults datacenter +! +! debug bgp evpn mh es +! debug bgp evpn mh route +! debug bgp zebra +! +! +router bgp 65003 + bgp router-id 192.168.100.16 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + neighbor 192.168.6.1 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.2.1 activate + neighbor 192.168.6.1 activate + advertise-all-vni + advertise-svi-ip + exit-address-family +! diff --git a/tests/topotests/bgp_evpn_mh/torm12/pim.conf b/tests/topotests/bgp_evpn_mh/torm12/pim.conf new file mode 100644 index 0000000..7e09ba7 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/torm12/pim.conf @@ -0,0 +1,19 @@ +#debug pim packet +#debug pim packet register +#debug pim trace +#debug pim event +#debug pim vxlan +! +ip pim ecmp +ip pim rp 192.168.100.13 239.1.1.0/24 +ip pim spt-switchover infinity-and-beyond +! +interface lo + ip igmp + ip pim +! +interface torm12-eth0 + ip pim +! +interface torm12-eth1 + ip pim diff --git a/tests/topotests/bgp_evpn_mh/torm12/zebra.conf b/tests/topotests/bgp_evpn_mh/torm12/zebra.conf new file mode 100644 index 0000000..9532762 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/torm12/zebra.conf @@ -0,0 +1,28 @@ +! debug zebra evpn mh es +! debug zebra evpn mh mac +! debug zebra evpn mh neigh +! debug zebra evpn mh nh +! debug zebra vxlan +! +evpn mh startup-delay 1 +! +int torm12-eth0 + ip addr 192.168.2.2/24 + evpn mh uplink +! +int torm12-eth1 + ip addr 192.168.6.2/24 + evpn mh uplink +! +! +int lo + ip addr 192.168.100.16/32 +! +interface hostbond1 + evpn mh es-id 1 + evpn mh es-sys-mac 44:38:39:ff:ff:01 +! +interface hostbond2 + evpn mh es-id 2 + evpn mh es-sys-mac 44:38:39:ff:ff:01 +! diff --git a/tests/topotests/bgp_evpn_mh/torm21/evpn.conf b/tests/topotests/bgp_evpn_mh/torm21/evpn.conf new file mode 100644 index 0000000..5247dc1 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/torm21/evpn.conf @@ -0,0 +1,21 @@ +! +frr defaults datacenter +! +! debug bgp evpn mh es +! debug bgp evpn mh route +! debug bgp zebra +! +! +router bgp 65004 + bgp router-id 192.168.100.17 + no bgp ebgp-requires-policy + neighbor 192.168.3.1 remote-as external + neighbor 192.168.7.1 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.3.1 activate + neighbor 192.168.7.1 activate + advertise-all-vni + advertise-svi-ip + exit-address-family +! diff --git a/tests/topotests/bgp_evpn_mh/torm21/pim.conf b/tests/topotests/bgp_evpn_mh/torm21/pim.conf new file mode 100644 index 0000000..6996d74 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/torm21/pim.conf @@ -0,0 +1,19 @@ +#debug pim packet +#debug pim packet register +#debug pim trace +#debug pim event +#debug pim vxlan +! +ip pim ecmp +ip pim rp 192.168.100.13 239.1.1.0/24 +ip pim spt-switchover infinity-and-beyond +! +interface lo + ip igmp + ip pim +! +interface torm21-eth0 + ip pim +! +interface torm21-eth1 + ip pim diff --git a/tests/topotests/bgp_evpn_mh/torm21/zebra.conf b/tests/topotests/bgp_evpn_mh/torm21/zebra.conf new file mode 100644 index 0000000..6c75df7 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/torm21/zebra.conf @@ -0,0 +1,29 @@ +! debug zebra evpn mh es +! debug zebra evpn mh mac +! debug zebra evpn mh neigh +! debug zebra evpn mh nh +! debug zebra vxlan +! +evpn mh startup-delay 1 +! +int torm21-eth0 + ip addr 192.168.3.2/24 + evpn mh uplink +! +! +int torm21-eth1 + ip addr 192.168.7.2/24 + evpn mh uplink +! +! +int lo + ip addr 192.168.100.17/32 +! +interface hostbond1 + evpn mh es-id 1 + evpn mh es-sys-mac 44:38:39:ff:ff:02 +! +interface hostbond2 + evpn mh es-id 2 + evpn mh es-sys-mac 44:38:39:ff:ff:02 +! diff --git a/tests/topotests/bgp_evpn_mh/torm22/evpn.conf b/tests/topotests/bgp_evpn_mh/torm22/evpn.conf new file mode 100644 index 0000000..ec56360 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/torm22/evpn.conf @@ -0,0 +1,20 @@ +! +frr defaults datacenter +! +! debug bgp evpn mh es +! debug bgp evpn mh route +! debug bgp zebra +! +router bgp 65005 + bgp router-id 192.168.100.18 + no bgp ebgp-requires-policy + neighbor 192.168.4.1 remote-as external + neighbor 192.168.8.1 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.4.1 activate + neighbor 192.168.8.1 activate + advertise-all-vni + advertise-svi-ip + exit-address-family +! diff --git a/tests/topotests/bgp_evpn_mh/torm22/pim.conf b/tests/topotests/bgp_evpn_mh/torm22/pim.conf new file mode 100644 index 0000000..6256e0e --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/torm22/pim.conf @@ -0,0 +1,19 @@ +#debug pim packet +#debug pim packet register +#debug pim trace +#debug pim event +#debug pim vxlan +! +ip pim ecmp +ip pim rp 192.168.100.13 239.1.1.0/24 +ip pim spt-switchover infinity-and-beyond +! +interface lo + ip igmp + ip pim +! +interface torm22-eth0 + ip pim +! +interface torm22-eth1 + ip pim diff --git a/tests/topotests/bgp_evpn_mh/torm22/zebra.conf b/tests/topotests/bgp_evpn_mh/torm22/zebra.conf new file mode 100644 index 0000000..4c94966 --- /dev/null +++ b/tests/topotests/bgp_evpn_mh/torm22/zebra.conf @@ -0,0 +1,29 @@ +! debug zebra evpn mh es +! debug zebra evpn mh mac +! debug zebra evpn mh neigh +! debug zebra evpn mh nh +! debug zebra vxlan +! +evpn mh startup-delay 1 +! +int torm22-eth0 + ip addr 192.168.4.2/24 + evpn mh uplink +! +! +int torm22-eth1 + ip addr 192.168.8.2/24 + evpn mh uplink +! +! +int lo + ip addr 192.168.100.18/32 +! +interface hostbond1 + evpn mh es-id 1 + evpn mh es-sys-mac 44:38:39:ff:ff:02 +! +interface hostbond2 + evpn mh es-id 2 + evpn mh es-sys-mac 44:38:39:ff:ff:02 +! diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_base.json new file mode 100644 index 0000000..2eeebad --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_base.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt2.json new file mode 100644 index 0000000..419bcc3 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt2.json @@ -0,0 +1,8 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]": null, + "[3]:[0]:[32]:[10.100.0.1]": null, + "[3]:[0]:[32]:[10.100.0.2]": null +} \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt5.json new file mode 100644 index 0000000..2eeebad --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt5.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_base.json new file mode 100644 index 0000000..833f986 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_base.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"50.0.1.11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } } \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt2.json new file mode 100644 index 0000000..833f986 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt2.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"50.0.1.11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } } \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt5.json new file mode 100644 index 0000000..4a292bd --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": null } } diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_base.json new file mode 100644 index 0000000..3dc3fcf --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_base.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"50:0:1::11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global" + } + ] + } +] } } diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt2.json new file mode 100644 index 0000000..3dc3fcf --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt2.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"50:0:1::11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global" + } + ] + } +] } } diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt5.json new file mode 100644 index 0000000..6c11d89 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": null } } \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgpd.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgpd.conf new file mode 100644 index 0000000..63aa99a --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgpd.conf @@ -0,0 +1,30 @@ +router bgp 101 + bgp router-id 10.100.0.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.1.2 remote-as 102 + ! + address-family l2vpn evpn + neighbor 10.0.1.2 activate + advertise-all-vni + exit-address-family +! +router bgp 101 vrf vrf-blue + bgp router-id 10.100.0.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 50.0.1.11 remote-as 111 + neighbor 50:0:1::11 remote-as 111 + ! + address-family ipv4 unicast + no neighbor 50:0:1::11 activate + exit-address-family + ! + address-family ipv6 unicast + neighbor 50:0:1::11 activate + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast gateway-ip + advertise ipv6 unicast gateway-ip + exit-address-family \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra.conf new file mode 100644 index 0000000..99a2e89 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra.conf @@ -0,0 +1,14 @@ +! +log file zebra.log +! +ip route 10.100.0.2/32 10.0.1.2 +! +vrf vrf-blue + vni 1000 prefix-routes-only + exit-vrf +! +interface lo + ip address 10.100.0.1/32 +interface PE1-eth0 + ip address 10.0.1.1/24 +! diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_base.json new file mode 100644 index 0000000..2dcf35d --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_base.json @@ -0,0 +1,56 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32":[ + { + "prefix":"100.0.0.21\/32", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50.0.1.11", + "afi":"ipv4", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt2.json new file mode 100644 index 0000000..2dcf35d --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt2.json @@ -0,0 +1,56 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32":[ + { + "prefix":"100.0.0.21\/32", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50.0.1.11", + "afi":"ipv4", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt5.json new file mode 100644 index 0000000..9c3091d --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32": null +} diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_base.json new file mode 100644 index 0000000..229c927 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_base.json @@ -0,0 +1,55 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128":[ + { + "prefix":"100::21\/128", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt2.json new file mode 100644 index 0000000..229c927 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt2.json @@ -0,0 +1,55 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128":[ + { + "prefix":"100::21\/128", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt5.json new file mode 100644 index 0000000..94f82e6 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128": null +} \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_base.json new file mode 100644 index 0000000..7b8d38e --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_base.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt2.json new file mode 100644 index 0000000..6273b3e --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt2.json @@ -0,0 +1,68 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]": null, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt5.json new file mode 100644 index 0000000..7b8d38e --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt5.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_base.json new file mode 100644 index 0000000..c03d701 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_base.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } } \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt2.json new file mode 100644 index 0000000..7f1b8d2 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt2.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":null, + "bestpath":null, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt5.json new file mode 100644 index 0000000..52e4311 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": null } } \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_base.json new file mode 100644 index 0000000..1d90c9c --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_base.json @@ -0,0 +1,28 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global", + "used":true + } + ] + } +] } } \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt2.json new file mode 100644 index 0000000..a0e63c6 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt2.json @@ -0,0 +1,28 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":null, + "bestpath":null, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt5.json new file mode 100644 index 0000000..789fe69 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": null } } \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgpd.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgpd.conf new file mode 100644 index 0000000..59fee15 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 102 + bgp router-id 10.100.0.2 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.1.1 remote-as 101 + ! + address-family l2vpn evpn + neighbor 10.0.1.1 activate + advertise-all-vni + enable-resolve-overlay-index + exit-address-family +! +router bgp 101 vrf vrf-blue + bgp router-id 10.100.0.2 diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra.conf new file mode 100644 index 0000000..b78cdcc --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra.conf @@ -0,0 +1,14 @@ +! +log file zebra.log +! +ip route 10.100.0.1/32 10.0.1.1 +! +vrf vrf-blue + vni 1000 prefix-routes-only + exit-vrf +! +interface lo + ip address 10.100.0.2/32 +interface PE2-eth0 + ip address 10.0.1.2/24 +! diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_base.json new file mode 100644 index 0000000..b3a3640 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_base.json @@ -0,0 +1,56 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32":[ + { + "prefix":"100.0.0.21\/32", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":40, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50.0.1.11", + "afi":"ipv4", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt2.json new file mode 100644 index 0000000..996fe52 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt2.json @@ -0,0 +1,29 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32": null +} \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt5.json new file mode 100644 index 0000000..996fe52 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32": null +} \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_base.json new file mode 100644 index 0000000..d5be22a --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_base.json @@ -0,0 +1,56 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128":[ + { + "prefix":"100::21\/128", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":40, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50:0:1::11", + "afi":"ipv6", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt2.json new file mode 100644 index 0000000..94f82e6 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt2.json @@ -0,0 +1,29 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128": null +} \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt5.json new file mode 100644 index 0000000..94f82e6 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128": null +} \ No newline at end of file diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/__init__.py b/tests/topotests/bgp_evpn_overlay_index_gateway/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/host1/bgpd.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/host1/bgpd.conf new file mode 100644 index 0000000..7608ec9 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/host1/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 111 + bgp router-id 10.100.0.11 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 50.0.1.1 remote-as 101 + neighbor 50:0:1::1 remote-as 101 + ! + address-family ipv4 unicast + network 100.0.0.21/32 + no neighbor 50:0:1::1 activate + exit-address-family + ! + address-family ipv6 unicast + network 100::21/128 + neighbor 50:0:1::1 activate + exit-address-family + + diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/host1/zebra.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/host1/zebra.conf new file mode 100644 index 0000000..c8c832e --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/host1/zebra.conf @@ -0,0 +1,4 @@ +! +int host1-eth0 + ip address 50.0.1.11/24 + ipv6 address 50:0:1::11/48 diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/host2/bgpd.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/bgpd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf new file mode 100644 index 0000000..b9f80f1 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf @@ -0,0 +1,4 @@ +! +int host2-eth0 + ip address 50.0.1.21/24 + ipv6 address 50:0:1::21/48 diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/test_bgp_evpn_overlay_index_gateway.py b/tests/topotests/bgp_evpn_overlay_index_gateway/test_bgp_evpn_overlay_index_gateway.py new file mode 100755 index 0000000..1066269 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/test_bgp_evpn_overlay_index_gateway.py @@ -0,0 +1,401 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# + +""" +test_bgp_evpn_overlay_index_gateway.py: Test EVPN gateway IP overlay index functionality +Following functionality is covered: + + +--------+ BGP +--------+ BGP +--------+ +--------+ + SN1 | | IPv4/v6 | | EVPN | | | | + ======+ Host1 +---------+ PE1 +------+ PE2 +------+ Host2 + + | | | | | | | | + +--------+ +--------+ +--------+ +--------+ + + Host1 is connected to PE1 and host2 is connected to PE2 + Host1 and PE1 have IPv4/v6 BGP sessions. + PE1 and PE2 gave EVPN session. + Host1 advertises IPv4/v6 prefixes to PE1. + PE1 advertises these prefixes to PE2 as EVPN type-5 routes. + Gateway IP for these EVPN type-5 routes is host1 IP. + Host1 MAC/IP is advertised by PE1 as EVPN type-2 route + +Following testcases are covered: +TC_1: +Check BGP and zebra states for above topology at PE1 and PE2. + +TC_2: +Stop advertising prefixes from host1. It should withdraw type-5 routes. Check states at PE1 and PE2 +Advertise the prefixes again. Check states. + +TC_3: +Shut down VxLAN interface at PE1. This should withdraw type-2 routes. Check states at PE1 and PE2. +Enable VxLAN interface again. Check states. +""" + +import os +import sys +import json +from functools import partial +import pytest +import time +import platform + +# Current Working Directory +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import ( + step, + write_test_header, + write_test_footer, + generate_support_bundle, +) + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.bgpd] + + +# Global variables +PES = ["PE1", "PE2"] +HOSTS = ["host1", "host2"] +PE_SUFFIX = {"PE1": "1", "PE2": "2"} +HOST_SUFFIX = {"host1": "1", "host2": "2"} +TRIGGERS = ["base", "no_rt5", "no_rt2"] + + +def build_topo(tgen): + # This function only purpose is to define allocation and relationship + # between routers and add links. + + # Create routers + for pe in PES: + tgen.add_router(pe) + for host in HOSTS: + tgen.add_router(host) + + krel = platform.release() + logger.info("Kernel version " + krel) + + # Add links + tgen.add_link(tgen.gears["PE1"], tgen.gears["PE2"], "PE1-eth0", "PE2-eth0") + tgen.add_link(tgen.gears["PE1"], tgen.gears["host1"], "PE1-eth1", "host1-eth0") + tgen.add_link(tgen.gears["PE2"], tgen.gears["host2"], "PE2-eth1", "host2-eth0") + + +def setup_module(mod): + "Sets up the pytest environment" + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info( + "For EVPN, kernel version should be minimum 4.15. Kernel present {}".format( + kernelv + ) + ) + return + + if topotest.version_cmp(kernelv, "4.15") == 0: + l3mdev_accept = 1 + logger.info("setting net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept)) + else: + l3mdev_accept = 0 + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + tgen.start_topology() + + # Configure MAC address for hosts as these MACs are advertised with EVPN type-2 routes + for name in tgen.gears: + if name not in HOSTS: + continue + host = tgen.net[name] + + host_mac = "1a:2b:3c:4d:5e:6{}".format(HOST_SUFFIX[name]) + host.cmd_raises("ip link set dev {}-eth0 down".format(name)) + host.cmd_raises("ip link set dev {0}-eth0 address {1}".format(name, host_mac)) + host.cmd_raises("ip link set dev {}-eth0 up".format(name)) + + # Configure PE VxLAN and Bridge interfaces + for name in tgen.gears: + if name not in PES: + continue + pe = tgen.net[name] + + vtep_ip = "10.100.0.{}".format(PE_SUFFIX[name]) + bridge_ip = "50.0.1.{}/24".format(PE_SUFFIX[name]) + bridge_ipv6 = "50:0:1::{}/48".format(PE_SUFFIX[name]) + + pe.cmd_raises("ip link add vrf-blue type vrf table 10") + pe.cmd_raises("ip link set dev vrf-blue up") + pe.cmd_raises( + "ip link add vxlan100 type vxlan id 100 dstport 4789 local {}".format( + vtep_ip + ) + ) + pe.cmd_raises("ip link add name br100 type bridge stp_state 0") + pe.cmd_raises("ip link set dev vxlan100 master br100") + pe.cmd_raises("ip link set dev {}-eth1 master br100".format(name)) + pe.cmd_raises("ip addr add {} dev br100".format(bridge_ip)) + pe.cmd_raises("ip link set up dev br100") + pe.cmd_raises("ip link set up dev vxlan100") + pe.cmd_raises("ip link set up dev {}-eth1".format(name)) + pe.cmd_raises("ip link set dev br100 master vrf-blue") + pe.cmd_raises("ip -6 addr add {} dev br100".format(bridge_ipv6)) + + pe.cmd_raises( + "ip link add vxlan1000 type vxlan id 1000 dstport 4789 local {}".format( + vtep_ip + ) + ) + pe.cmd_raises("ip link add name br1000 type bridge stp_state 0") + pe.cmd_raises("ip link set dev vxlan1000 master br100") + pe.cmd_raises("ip link set up dev br1000") + pe.cmd_raises("ip link set up dev vxlan1000") + pe.cmd_raises("ip link set dev br1000 master vrf-blue") + + pe.cmd_raises("sysctl -w net.ipv4.ip_forward=1") + pe.cmd_raises("sysctl -w net.ipv6.conf.all.forwarding=1") + pe.cmd_raises("sysctl -w net.ipv4.udp_l3mdev_accept={}".format(l3mdev_accept)) + pe.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept)) + + # For all registered routers, load the zebra configuration file + for (name, router) in tgen.routers().items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(name)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(name)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + logger.info("Running setup_module() done") + + time.sleep(10) + + +def teardown_module(mod): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def evpn_gateway_ip_show_op_check(trigger=" "): + """ + This function checks CLI O/P for commands mentioned in show_commands for a given trigger + :param trigger: Should be a trigger present in TRIGGERS + :return: Returns a tuple (result: None for success, retmsg: Log message to be printed on failure) + """ + tgen = get_topogen() + + if trigger not in TRIGGERS: + return "Unexpected trigger", "Unexpected trigger {}".format(trigger) + + show_commands = { + "bgp_vni_routes": "show bgp l2vpn evpn route vni 100 json", + "bgp_vrf_ipv4": "show bgp vrf vrf-blue ipv4 json", + "bgp_vrf_ipv6": "show bgp vrf vrf-blue ipv6 json", + "zebra_vrf_ipv4": "show ip route vrf vrf-blue json", + "zebra_vrf_ipv6": "show ipv6 route vrf vrf-blue json", + } + + for (name, pe) in tgen.gears.items(): + if name not in PES: + continue + + for (cmd_key, command) in show_commands.items(): + expected_op_file = "{0}/{1}/{2}_{3}.json".format( + CWD, name, cmd_key, trigger + ) + expected_op = json.loads(open(expected_op_file).read()) + + test_func = partial(topotest.router_json_cmp, pe, command, expected_op) + ret, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{0}" JSON output mismatch for {1}'.format(name, command) + if result is not None: + return result, assertmsg + + return None, "Pass" + + +def test_evpn_gateway_ip_basic_topo(request): + """ + Tets EVPN overlay index gateway IP functionality. VErify show O/Ps on PE1 and PE2 + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Temporarily Disabled + tgen.set_error( + "%s: Failing under new micronet framework, please debug and re-enable", tc_name + ) + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info("For EVPN, kernel version should be minimum 4.15") + write_test_footer(tc_name) + return + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Check O/Ps for EVPN gateway IP overlay Index functionality at PE1 and PE2") + + result, assertmsg = evpn_gateway_ip_show_op_check("base") + + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + write_test_footer(tc_name) + + +def test_evpn_gateway_ip_flap_rt5(request): + """ + Withdraw EVPN type-5 routes and check O/Ps at PE1 and PE2 + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info("For EVPN, kernel version should be minimum 4.15") + write_test_footer(tc_name) + return + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + h1 = tgen.gears["host1"] + + step("Withdraw type-5 routes") + + h1.run( + 'vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv4" \ + -c "no network 100.0.0.21/32"' + ) + h1.run( + 'vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv6" \ + -c "no network 100::21/128"' + ) + + result, assertmsg = evpn_gateway_ip_show_op_check("no_rt5") + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + step("Advertise type-5 routes again") + + h1.run( + 'vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv4" \ + -c "network 100.0.0.21/32"' + ) + h1.run( + 'vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv6" \ + -c "network 100::21/128"' + ) + + result, assertmsg = evpn_gateway_ip_show_op_check("base") + if result is not None: + generate_support_bundle() + + assert result is None, assertmsg + + write_test_footer(tc_name) + + +def test_evpn_gateway_ip_flap_rt2(request): + """ + Withdraw EVPN type-2 routes and check O/Ps at PE1 and PE2 + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info("For EVPN, kernel version should be minimum 4.15") + write_test_footer(tc_name) + return + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Shut down VxLAN interface at PE1 which results in withdraw of type-2 routes") + + pe1 = tgen.net["PE1"] + + pe1.cmd_raises("ip link set dev vxlan100 down") + + result, assertmsg = evpn_gateway_ip_show_op_check("no_rt2") + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + step("Bring up VxLAN interface at PE1 and advertise type-2 routes again") + + pe1.cmd_raises("ip link set dev vxlan100 up") + + result, assertmsg = evpn_gateway_ip_show_op_check("base") + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + write_test_footer(tc_name) + + +def test_memory_leak(): + """Run the memory leak test and report results""" + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_evpn_rt5/__init__.py b/tests/topotests/bgp_evpn_rt5/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf b/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf new file mode 100644 index 0000000..ccbeae6 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf @@ -0,0 +1,26 @@ +! debug bgp neighbor-events +! debug bgp updates +! debug bgp zebra +router bgp 65000 + bgp router-id 192.168.100.21 + bgp log-neighbor-changes + no bgp default ipv4-unicast + neighbor 192.168.100.41 remote-as 65000 + neighbor 192.168.100.41 capability extended-nexthop + ! + address-family l2vpn evpn + neighbor 192.168.100.41 activate + advertise-all-vni + exit-address-family +! +router bgp 65000 vrf r1-vrf-101 + bgp router-id 192.168.102.21 + bgp log-neighbor-changes + no bgp network import-check + address-family ipv4 unicast + network 192.168.102.21/32 + exit-address-family + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family + ! diff --git a/tests/topotests/bgp_evpn_rt5/r1/zebra.conf b/tests/topotests/bgp_evpn_rt5/r1/zebra.conf new file mode 100644 index 0000000..4f1804c --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/zebra.conf @@ -0,0 +1,22 @@ +log stdout + +hostname r1 +password zebra + +! debug zebra vxlan +! debug zebra kernel +! debug zebra dplane +! debug zebra rib +log stdout +vrf r1-vrf-101 + vni 101 + exit-vrf +! +interface r1-eth0 + ip address 192.168.100.21/24 +! +interface loop101 vrf r1-vrf-101 + ip address 192.168.102.21/32 +! + + diff --git a/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf b/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf new file mode 100644 index 0000000..744c259 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf @@ -0,0 +1,27 @@ +! debug bgp neighbor-events +! debug bgp updates +! debug bgp zebra +router bgp 65000 + bgp router-id 192.168.100.41 + bgp log-neighbor-changes + no bgp default ipv4-unicast + neighbor 192.168.100.21 peer-group + neighbor 192.168.100.21 remote-as 65000 + neighbor 192.168.100.21 capability extended-nexthop + ! + address-family l2vpn evpn + neighbor 192.168.100.21 activate + advertise-all-vni + exit-address-family +! +router bgp 65000 vrf r2-vrf-101 + bgp router-id 192.168.101.41 + bgp log-neighbor-changes + no bgp network import-check + address-family ipv4 unicast + network 192.168.101.41/32 + exit-address-family + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family + ! diff --git a/tests/topotests/bgp_evpn_rt5/r2/zebra.conf b/tests/topotests/bgp_evpn_rt5/r2/zebra.conf new file mode 100644 index 0000000..7d19a5b --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r2/zebra.conf @@ -0,0 +1,18 @@ +log stdout + +hostname r2 +password zebra + +! debug zebra vxlan + +vrf r2-vrf-101 + vni 101 + exit-vrf +! +interface loop101 vrf r2-vrf-101 + ip address 192.168.101.41/32 +! +interface r2-eth0 + ip address 192.168.100.41/24 +! + diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py new file mode 100644 index 0000000..7e4bcc8 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_evpn.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# + +""" + test_bgp_evpn.py: Test the FRR BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + tgen.add_router("r1") + tgen.add_router("r2") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + krel = platform.release() + if topotest.version_cmp(krel, "4.18") < 0: + logger.info( + 'BGP EVPN RT5 NETNS tests will not run (have kernel "{}", but it requires 4.18)'.format( + krel + ) + ) + return pytest.skip("Skipping BGP EVPN RT5 NETNS Test. Kernel not supported") + + # create VRF vrf-101 on R1 and R2 + # create loop101 + cmds_vrflite = [ + "ip link add {}-vrf-101 type vrf table 101", + "ip ru add oif {}-vrf-101 table 101", + "ip ru add iif {}-vrf-101 table 101", + "ip link set dev {}-vrf-101 up", + "ip link add loop101 type dummy", + "ip link set dev loop101 master {}-vrf-101", + "ip link set dev loop101 up", + ] + + cmds_r2 = [ # config routing 101 + "ip link add name bridge-101 up type bridge stp_state 0", + "ip link set bridge-101 master {}-vrf-101", + "ip link set dev bridge-101 up", + "ip link add name vxlan-101 type vxlan id 101 dstport 4789 dev r2-eth0 local 192.168.100.41", + "ip link set dev vxlan-101 master bridge-101", + "ip link set vxlan-101 up type bridge_slave learning off flood off mcast_flood off", + ] + + # cmds_r1_netns_method3 = [ + # "ip link add name vxlan-{1} type vxlan id {1} dstport 4789 dev {0}-eth0 local 192.168.100.21", + # "ip link set dev vxlan-{1} netns {0}-vrf-{1}", + # "ip netns exec {0}-vrf-{1} ip li set dev lo up", + # "ip netns exec {0}-vrf-{1} ip link add name bridge-{1} up type bridge stp_state 0", + # "ip netns exec {0}-vrf-{1} ip link set dev vxlan-{1} master bridge-{1}", + # "ip netns exec {0}-vrf-{1} ip link set bridge-{1} up", + # "ip netns exec {0}-vrf-{1} ip link set vxlan-{1} up", + # ] + + router = tgen.gears["r1"] + + ns = "r1-vrf-101" + tgen.net["r1"].add_netns(ns) + tgen.net["r1"].cmd_raises("ip link add loop101 type dummy") + tgen.net["r1"].set_intf_netns("loop101", ns, up=True) + + router = tgen.gears["r2"] + for cmd in cmds_vrflite: + logger.info("cmd to r2: " + cmd.format("r2")) + output = router.cmd_raises(cmd.format("r2")) + logger.info("result: " + output) + + for cmd in cmds_r2: + logger.info("cmd to r2: " + cmd.format("r2")) + output = router.cmd_raises(cmd.format("r2")) + logger.info("result: " + output) + + tgen.net["r1"].cmd_raises( + "ip link add name vxlan-101 type vxlan id 101 dstport 4789 dev r1-eth0 local 192.168.100.21" + ) + tgen.net["r1"].set_intf_netns("vxlan-101", "r1-vrf-101", up=True) + tgen.net["r1"].cmd_raises("ip -n r1-vrf-101 link set lo up") + tgen.net["r1"].cmd_raises( + "ip -n r1-vrf-101 link add name bridge-101 up type bridge stp_state 0" + ) + tgen.net["r1"].cmd_raises( + "ip -n r1-vrf-101 link set dev vxlan-101 master bridge-101" + ) + tgen.net["r1"].cmd_raises("ip -n r1-vrf-101 link set bridge-101 up") + tgen.net["r1"].cmd_raises("ip -n r1-vrf-101 link set vxlan-101 up") + + for rname, router in router_list.items(): + if rname == "r1": + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, "{}/zebra.conf".format(rname)), + "--vrfwnetns", + ) + else: + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.net["r1"].delete_netns("r1-vrf-101") + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + topotest.sleep(4, "waiting 4 seconds for bgp convergence") + # Check IPv4/IPv6 routing tables. + output = tgen.gears["r1"].vtysh_cmd("show bgp l2vpn evpn", isjson=False) + logger.info("==== result from show bgp l2vpn evpn") + logger.info(output) + output = tgen.gears["r1"].vtysh_cmd( + "show bgp l2vpn evpn route detail", isjson=False + ) + logger.info("==== result from show bgp l2vpn evpn route detail") + logger.info(output) + output = tgen.gears["r1"].vtysh_cmd("show bgp vrf r1-vrf-101 ipv4", isjson=False) + logger.info("==== result from show bgp vrf r1-vrf-101 ipv4") + logger.info(output) + output = tgen.gears["r1"].vtysh_cmd("show bgp vrf r1-vrf-101", isjson=False) + logger.info("==== result from show bgp vrf r1-vrf-101 ") + logger.info(output) + output = tgen.gears["r1"].vtysh_cmd("show ip route vrf r1-vrf-101", isjson=False) + logger.info("==== result from show ip route vrf r1-vrf-101") + logger.info(output) + output = tgen.gears["r1"].vtysh_cmd("show evpn vni detail", isjson=False) + logger.info("==== result from show evpn vni detail") + logger.info(output) + output = tgen.gears["r1"].vtysh_cmd("show evpn next-hops vni all", isjson=False) + logger.info("==== result from show evpn next-hops vni all") + logger.info(output) + output = tgen.gears["r1"].vtysh_cmd("show evpn rmac vni all", isjson=False) + logger.info("==== result from show evpn next-hops vni all") + logger.info(output) + # Check IPv4 and IPv6 connectivity between r1 and r2 ( routing vxlan evpn) + pingrouter = tgen.gears["r1"] + logger.info( + "Check Ping IPv4 from R1(r1-vrf-101) to R2(r2-vrf-101 = 192.168.101.41)" + ) + output = pingrouter.run("ip netns exec r1-vrf-101 ping 192.168.101.41 -f -c 1000") + logger.info(output) + if "1000 packets transmitted, 1000 received" not in output: + assertmsg = ( + "expected ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) should be ok" + ) + assert 0, assertmsg + else: + logger.info("Check Ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) OK") + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/bgpd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/ospfd.conf new file mode 100644 index 0000000..2db7edb --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/ospfd.conf @@ -0,0 +1,13 @@ +! +router ospf + network 10.20.0.0/16 area 0 + network 10.20.20.20/32 area 0 +! +int P1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int P1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/zebra.conf new file mode 100644 index 0000000..95b5da8 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/zebra.conf @@ -0,0 +1,7 @@ +! +interface lo + ip address 10.20.20.20/32 +interface P1-eth0 + ip address 10.20.1.2/24 +interface P1-eth1 + ip address 10.20.2.2/24 diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgp.l2vpn.evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgp.l2vpn.evpn.vni.json new file mode 100644 index 0000000..9f93635 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgp.l2vpn.evpn.vni.json @@ -0,0 +1,19 @@ +{ + "vni":101, + "type":"L2", + "inKernel":"True", + "rd":"10.10.10.10:101", + "originatorIp":"10.10.10.10", + "mcastGroup":"0.0.0.0", + "siteOfOrigin":"65000:0", + "advertiseGatewayMacip":"Disabled", + "advertiseSviMacIp":"Active", + "sviInterface":"br101", + "importRts":[ + "65000:101" + ], + "exportRts":[ + "65000:101" + ] +} + diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgpd.conf new file mode 100644 index 0000000..e4d20b9 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65000 + timers bgp 3 9 + bgp router-id 10.10.10.10 + no bgp default ipv4-unicast + neighbor 10.30.30.30 remote-as 65000 + neighbor 10.30.30.30 update-source lo + neighbor 10.30.30.30 timers 3 10 + ! + address-family l2vpn evpn + neighbor 10.30.30.30 activate + advertise-all-vni + advertise-svi-ip + vni 101 + rd 10.10.10.10:101 + route-target import 65000:101 + route-target export 65000:101 + exit-vni + advertise-svi-ip diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/evpn.vni.json new file mode 100644 index 0000000..4bea8b3 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/evpn.vni.json @@ -0,0 +1,17 @@ +{ + "vni":101, + "type":"L2", + "tenantVrf":"VRF-A", + "vxlanInterface":"vxlan101", + "vtepIp":"10.10.10.10", + "mcastGroup":"0.0.0.0", + "advertiseGatewayMacip":"No", + "numRemoteVteps":1, + "remoteVteps":[ + { + "ip":"10.30.30.30", + "flood":"HER" + } + ] +} + diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/ospfd.conf new file mode 100644 index 0000000..f1c2b42 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/ospfd.conf @@ -0,0 +1,9 @@ +! +router ospf + network 10.20.0.0/16 area 0 + network 10.10.10.10/32 area 0 +! +int PE1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/zebra.conf new file mode 100644 index 0000000..e269947 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/zebra.conf @@ -0,0 +1,8 @@ +! +log file zebra.log +! +interface lo + ip address 10.10.10.10/32 +interface PE1-eth1 + ip address 10.20.1.1/24 +! diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgp.l2vpn.evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgp.l2vpn.evpn.vni.json new file mode 100644 index 0000000..63ac730 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgp.l2vpn.evpn.vni.json @@ -0,0 +1,19 @@ +{ + "vni":101, + "type":"L2", + "inKernel":"True", + "rd":"10.30.30.30:101", + "originatorIp":"10.30.30.30", + "mcastGroup":"0.0.0.0", + "siteOfOrigin":"65000:0", + "advertiseGatewayMacip":"Disabled", + "advertiseSviMacIp":"Active", + "sviInterface":"br101", + "importRts":[ + "65000:101" + ], + "exportRts":[ + "65000:101" + ] +} + diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgpd.conf new file mode 100644 index 0000000..9a0830d --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65000 + timers bgp 3 9 + bgp router-id 10.30.30.30 + no bgp default ipv4-unicast + neighbor 10.10.10.10 remote-as 65000 + neighbor 10.10.10.10 update-source lo + neighbor 10.10.10.10 timers 3 10 + ! + address-family l2vpn evpn + neighbor 10.10.10.10 activate + advertise-all-vni + advertise-svi-ip + vni 101 + rd 10.30.30.30:101 + route-target import 65000:101 + route-target export 65000:101 + exit-vni + advertise-svi-ip diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/evpn.vni.json new file mode 100644 index 0000000..5566fff --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/evpn.vni.json @@ -0,0 +1,16 @@ +{ + "vni":101, + "type":"L2", + "tenantVrf":"VRF-A", + "vxlanInterface":"vxlan101", + "vtepIp":"10.30.30.30", + "mcastGroup":"0.0.0.0", + "advertiseGatewayMacip":"No", + "numRemoteVteps":1, + "remoteVteps":[ + { + "ip":"10.10.10.10", + "flood":"HER" + } + ] +} diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/ospfd.conf new file mode 100644 index 0000000..065c993 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/ospfd.conf @@ -0,0 +1,9 @@ +! +router ospf + network 10.20.0.0/16 area 0 + network 10.30.30.30/32 area 0 +! +int PE2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/zebra.conf new file mode 100644 index 0000000..9738916 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/zebra.conf @@ -0,0 +1,6 @@ +! +interface lo + ip address 10.30.30.30/32 +interface PE2-eth0 + ip address 10.20.2.3/24 +! diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/bgpd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/ospfd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/ospfd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/zebra.conf new file mode 100644 index 0000000..91fae9e --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/zebra.conf @@ -0,0 +1,3 @@ +! +int host1-eth0 + ip address 10.10.1.55/24 diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/bgpd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/ospfd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/ospfd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/zebra.conf new file mode 100644 index 0000000..df9adeb --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/zebra.conf @@ -0,0 +1,3 @@ +! +interface host2-eth0 + ip address 10.10.1.56/24 diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/test_bgp_evpn_vxlan_macvrf_soo.py b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/test_bgp_evpn_vxlan_macvrf_soo.py new file mode 100755 index 0000000..558f737 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/test_bgp_evpn_vxlan_macvrf_soo.py @@ -0,0 +1,839 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: GPL-2.0-or-later +# +# test_bgp_evpn_vxlan_macvrf_soo.py +# +# May 10 2023, Trey Aspelund +# +# Copyright (C) 2023 NVIDIA Corporation +# +# Test MAC-VRF Site-of-Origin feature. +# Ensure: +# - routes received with SoO are installed w/o "mac-vrf soo" config +# - invalid "mac-vrf soo" config is rejected +# - valid "mac-vrf soo" config is applied to local VNIs +# - valid "mac-vrf soo" is set for locally originated type-2/3 routes +# - routes received with SoO are unimported/uninstalled from L2VNI/zebra +# - routes received with SoO are unimported/uninstalled from L3VNI/RIB +# - routes received with SoO are still present in global EVPN loc-rib +# + +import os +import sys +import json +from functools import partial +from time import sleep +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd] + + +def build_topo(tgen): + "Build function" + + # Create routers + tgen.add_router("P1") + tgen.add_router("PE1") + tgen.add_router("PE2") + tgen.add_router("host1") + tgen.add_router("host2") + + # Host1-PE1 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["host1"]) + switch.add_link(tgen.gears["PE1"]) + + # PE1-P1 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["PE1"]) + switch.add_link(tgen.gears["P1"]) + + # P1-PE2 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["P1"]) + switch.add_link(tgen.gears["PE2"]) + + # PE2-host2 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["PE2"]) + switch.add_link(tgen.gears["host2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + p1 = tgen.gears["P1"] + host1 = tgen.gears["host1"] + host2 = tgen.gears["host2"] + + # Setup PEs with: + # - vrf: VRF-A + # - l3vni 404: vxlan404 / br404 + # - l2vni 101: vxlan101 / br101 + + ## Setup VRF + # pe1 + pe1.run("ip link add VRF-A type vrf table 4000") + pe1.run("ip link set VRF-A up") + # pe2 + pe2.run("ip link add VRF-A type vrf table 4000") + pe2.run("ip link set VRF-A up") + + ## Setup L3VNI bridge/vxlan + # pe1 + pe1.run("ip link add name br404 type bridge stp_state 0") + pe1.run("ip link set dev br404 addr aa:bb:cc:00:11:ff") + pe1.run("ip link set dev br404 master VRF-A addrgenmode none") + pe1.run("ip link set dev br404 up") + pe1.run( + "ip link add vxlan404 type vxlan id 404 dstport 4789 local 10.10.10.10 nolearning" + ) + pe1.run("ip link set dev vxlan404 master br404 addrgenmode none") + pe1.run("ip link set dev vxlan404 type bridge_slave neigh_suppress on learning off") + pe1.run("ip link set dev vxlan404 up") + # pe2 + pe2.run("ip link add name br404 type bridge stp_state 0") + pe2.run("ip link set dev br404 addr aa:bb:cc:00:22:ff") + pe2.run("ip link set dev br404 master VRF-A addrgenmode none") + pe2.run("ip link set dev br404 up") + pe2.run( + "ip link add vxlan404 type vxlan id 404 dstport 4789 local 10.30.30.30 nolearning" + ) + pe2.run("ip link set dev vxlan404 master br404 addrgenmode none") + pe2.run("ip link set dev vxlan404 type bridge_slave neigh_suppress on learning off") + pe2.run("ip link set dev vxlan404 up") + + ## Setup L2VNI bridge/vxlan + L2 PE/CE link + # pe1 + pe1.run("ip link add name br101 type bridge stp_state 0") + pe1.run("ip addr add 10.10.1.1/24 dev br101") + pe1.run("ip link set dev br101 addr aa:bb:cc:00:11:aa") + pe1.run("ip link set dev br101 master VRF-A") + pe1.run("ip link set dev br101 up") + pe1.run( + "ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.10.10.10 nolearning" + ) + pe1.run("ip link set dev vxlan101 master br101") + pe1.run("ip link set dev vxlan101 type bridge_slave neigh_suppress on learning off") + pe1.run("ip link set dev vxlan101 up") + pe1.run("ip link set dev PE1-eth0 master br101") + pe1.run("ip link set dev PE1-eth0 up") + # pe2 + pe2.run("ip link add name br101 type bridge stp_state 0") + pe2.run("ip addr add 10.10.1.3/24 dev br101") + pe2.run("ip link set dev br101 addr aa:bb:cc:00:22:ff") + pe2.run("ip link set dev br101 master VRF-A") + pe2.run("ip link set dev br101 up") + pe2.run( + "ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.30.30.30 nolearning" + ) + pe2.run("ip link set dev vxlan101 master br101") + pe2.run("ip link set dev vxlan101 type bridge_slave neigh_suppress on learning off") + pe2.run("ip link set dev vxlan101 up") + pe2.run("ip link set dev PE2-eth1 master br101") + pe2.run("ip link set dev PE2-eth1 up") + + ## Enable IPv4 Routing + p1.run("sysctl -w net.ipv4.ip_forward=1") + pe1.run("sysctl -w net.ipv4.ip_forward=1") + pe2.run("sysctl -w net.ipv4.ip_forward=1") + + ## tell hosts to send GARP upon IPv4 addr assignment + host1.run("sysctl -w net.ipv4.conf.host1-eth0.arp_announce=1") + host2.run("sysctl -w net.ipv4.conf.host2-eth0.arp_announce=1") + + ## Load FRR config on all nodes and start topo + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def show_vni_json_elide_ifindex(pe, vni, expected): + output_json = pe.vtysh_cmd("show evpn vni {} json".format(vni), isjson=True) + if "ifindex" in output_json: + output_json.pop("ifindex") + + return topotest.json_cmp(output_json, expected) + + +def check_vni_macs_present(tgen, router, vni, maclist): + result = router.vtysh_cmd("show evpn mac vni {} json".format(vni), isjson=True) + for rname, ifname in maclist: + m = tgen.net.macs[(rname, ifname)] + if m not in result["macs"]: + return "MAC ({}) for interface {} on {} missing on {} from {}".format( + m, ifname, rname, router.name, json.dumps(result, indent=4) + ) + return None + + +def test_pe1_converge_evpn(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + json_file = "{}/{}/evpn.vni.json".format(CWD, pe1.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(show_vni_json_elide_ifindex, pe1, 101, expected) + _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + + # Let's ensure that the hosts have actually tried talking to + # each other. Otherwise under certain startup conditions + # they may not actually do any l2 arp'ing and as such + # the bridges won't know about the hosts on their networks + host1 = tgen.gears["host1"] + host1.run("ping -c 1 10.10.1.56") + host2 = tgen.gears["host2"] + host2.run("ping -c 1 10.10.1.55") + + test_func = partial( + check_vni_macs_present, + tgen, + pe1, + 101, + (("host1", "host1-eth0"), ("host2", "host2-eth0")), + ) + + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + if result: + logger.warning("%s", result) + assert None, '"{}" missing expected MACs'.format(pe1.name) + + +def test_pe2_converge_evpn(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe2 = tgen.gears["PE2"] + json_file = "{}/{}/evpn.vni.json".format(CWD, pe2.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(show_vni_json_elide_ifindex, pe2, 101, expected) + _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe2.name) + assert result is None, assertmsg + + test_func = partial( + check_vni_macs_present, + tgen, + pe2, + 101, + (("host1", "host1-eth0"), ("host2", "host2-eth0")), + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + if result: + logger.warning("%s", result) + assert None, '"{}" missing expected MACs'.format(pe2.name) + + +def mac_learn_test(host, local): + "check the host MAC gets learned by the VNI" + + host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name)) + int_lines = host_output.splitlines() + for line in int_lines: + line_items = line.split(": ") + if "HWaddr" in line_items[0]: + mac = line_items[1] + break + + mac_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac)) + mac_output_json = json.loads(mac_output) + assertmsg = "Local MAC output does not match interface mac {}".format(mac) + assert mac_output_json[mac]["type"] == "local", assertmsg + + +def mac_test_local_remote(local, remote): + "test MAC transfer between local and remote" + + local_output = local.vtysh_cmd("show evpn mac vni all json") + remote_output = remote.vtysh_cmd("show evpn mac vni all json") + local_output_vni = local.vtysh_cmd("show evpn vni detail json") + local_output_json = json.loads(local_output) + remote_output_json = json.loads(remote_output) + local_output_vni_json = json.loads(local_output_vni) + + for vni in local_output_json: + mac_list = local_output_json[vni]["macs"] + for mac in mac_list: + if mac_list[mac]["type"] == "local" and mac_list[mac]["intf"] != "br101": + assertmsg = "JSON output mismatches local: {} remote: {}".format( + local_output_vni_json[0]["vtepIp"], + remote_output_json[vni]["macs"][mac]["remoteVtep"], + ) + assert ( + remote_output_json[vni]["macs"][mac]["remoteVtep"] + == local_output_vni_json[0]["vtepIp"] + ), assertmsg + + +def test_learning_pe1(): + "test MAC learning on PE1" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host1 = tgen.gears["host1"] + pe1 = tgen.gears["PE1"] + mac_learn_test(host1, pe1) + + +def test_learning_pe2(): + "test MAC learning on PE2" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host2 = tgen.gears["host2"] + pe2 = tgen.gears["PE2"] + mac_learn_test(host2, pe2) + + +def test_local_remote_mac_pe1(): + "Test MAC transfer PE1 local and PE2 remote" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + mac_test_local_remote(pe1, pe2) + + +def test_local_remote_mac_pe2(): + "Test MAC transfer PE2 local and PE1 remote" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + mac_test_local_remote(pe2, pe1) + + +def ip_learn_test(tgen, host, local, remote, ip_addr): + "check the host IP gets learned by the VNI" + host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name)) + int_lines = host_output.splitlines() + for line in int_lines: + line_items = line.split(": ") + if "HWaddr" in line_items[0]: + mac = line_items[1] + break + print(host_output) + + # check we have a local association between the MAC and IP + local_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac)) + print(local_output) + local_output_json = json.loads(local_output) + mac_type = local_output_json[mac]["type"] + assertmsg = "Failed to learn local IP address on host {}".format(host.name) + assert local_output_json[mac]["neighbors"] != "none", assertmsg + learned_ip = local_output_json[mac]["neighbors"]["active"][0] + + assertmsg = "local learned mac wrong type: {} ".format(mac_type) + assert mac_type == "local", assertmsg + + assertmsg = ( + "learned address mismatch with configured address host: {} learned: {}".format( + ip_addr, learned_ip + ) + ) + assert ip_addr == learned_ip, assertmsg + + # now lets check the remote + count = 0 + converged = False + while count < 30: + remote_output = remote.vtysh_cmd( + "show evpn mac vni 101 mac {} json".format(mac) + ) + print(remote_output) + remote_output_json = json.loads(remote_output) + type = remote_output_json[mac]["type"] + if not remote_output_json[mac]["neighbors"] == "none": + # due to a kernel quirk, learned IPs can be inactive + if ( + remote_output_json[mac]["neighbors"]["active"] + or remote_output_json[mac]["neighbors"]["inactive"] + ): + converged = True + break + count += 1 + sleep(1) + + print("tries: {}".format(count)) + assertmsg = "{} remote learned mac no address: {} ".format(host.name, mac) + # some debug for this failure + if not converged == True: + log_output = remote.run("cat zebra.log") + print(log_output) + + assert converged == True, assertmsg + if remote_output_json[mac]["neighbors"]["active"]: + learned_ip = remote_output_json[mac]["neighbors"]["active"][0] + else: + learned_ip = remote_output_json[mac]["neighbors"]["inactive"][0] + assertmsg = "remote learned mac wrong type: {} ".format(type) + assert type == "remote", assertmsg + + assertmsg = "remote learned address mismatch with configured address host: {} learned: {}".format( + ip_addr, learned_ip + ) + assert ip_addr == learned_ip, assertmsg + + +def test_ip_pe1_learn(): + "run the IP learn test for PE1" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host1 = tgen.gears["host1"] + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + # pe2.vtysh_cmd("debug zebra vxlan") + # pe2.vtysh_cmd("debug zebra kernel") + # lets populate that arp cache + host1.run("ping -c1 10.10.1.1") + ip_learn_test(tgen, host1, pe1, pe2, "10.10.1.55") + # tgen.mininet_cli() + + +def test_ip_pe2_learn(): + "run the IP learn test for PE2" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host2 = tgen.gears["host2"] + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + # pe1.vtysh_cmd("debug zebra vxlan") + # pe1.vtysh_cmd("debug zebra kernel") + # lets populate that arp cache + host2.run("ping -c1 10.10.1.3") + ip_learn_test(tgen, host2, pe2, pe1, "10.10.1.56") + # tgen.mininet_cli() + + +def is_installed(json_paths, soo): + """ + check if any path has been selected as best. + optionally check for matching SoO on bestpath. + """ + best = False + soo_present = False + for path in json_paths: + path = path[0] + # sometimes "bestpath" is a bool, other times it's a dict + # either way, the key isn't present when the bool is false... + # so we may as well just check for the key's existence + best = "bestpath" in path + path_keys = path.keys() + if best: + if soo: + soo_present = soo in path["extendedCommunity"]["string"] + break + return (best and soo_present) if soo else best + + +def change_soo(pe, soo, vni): + soo_cmd_str = "mac-vrf soo " + if soo: + soo_cmd_str += soo + else: + soo_cmd_str = "no " + soo_cmd_str + pe.vtysh_cmd( + """ + configure terminal + router bgp 65000 + address-family l2vpn evpn + {} + """.format( + soo_cmd_str + ) + ) + bgp_l2vni = get_bgp_l2vni_fields(pe, vni) + l2vni_soo = bgp_l2vni[2] + return l2vni_soo == soo + + +def get_evpn_rt_json_str(vni, rd, oip=None, mac=None, ip=None): + "convert evpn route fields into a route string + global/l2vni cli syntax" + # type-3 + if oip: + rt_str = "[3]:[0]:[32]:[{}]".format(oip) + global_rt_cmd = "show bgp l2vpn evpn route rd {} type 3 json".format(rd) + l2vni_rt_cmd = "show bgp vni {} type 3 vtep {} json".format(vni, oip) + # type-2 + else: + rt_str = "[2]:[0]:[48]:[{}]".format(mac) + global_rt_cmd = "show bgp l2vpn evpn route rd {} type 2".format(rd) + l2vni_rt_cmd = "show bgp vni {} type 2 mac {}".format(vni, mac) + if ip: + ip_len = 128 if ":" in ip else 32 + rt_str += ":[{}]:[{}]".format(ip_len, ip) + l2vni_rt_cmd = "show bgp vni {} type 2 ip {}".format(vni, ip) + global_rt_cmd += " json" + l2vni_rt_cmd += " json" + return [rt_str, global_rt_cmd, l2vni_rt_cmd] + + +def get_evpn_rt_json(pe, vni, rd, oip=None, mac=None, ip=None): + "get json global/l2vni json blobs for the corresponding evpn route" + rt = get_evpn_rt_json_str(vni, rd, oip, mac, ip) + rt_str = rt.pop(0) + global_rt_cmd = rt.pop(0) + l2vni_rt_cmd = rt.pop(0) + logger.info( + "collecting global/l2vni evpn routes for pfx {} on {}".format(rt_str, pe.name) + ) + global_rt_json = pe.vtysh_cmd(global_rt_cmd, isjson=True) + logger.info("global evpn route for pfx {} on {}".format(rt_str, pe.name)) + logger.info(global_rt_json) + l2vni_rt_json = pe.vtysh_cmd(l2vni_rt_cmd, isjson=True) + logger.info("l2vni evpn route for pfx {} on {}".format(rt_str, pe.name)) + logger.info(l2vni_rt_json) + return [rt_str, global_rt_json, l2vni_rt_json] + + +def get_bgp_l2vni_fields(pe, vni): + bgp_vni_output = pe.vtysh_cmd( + "show bgp l2vpn evpn vni {} json".format(vni), isjson=True + ) + rd = bgp_vni_output["rd"] + oip = bgp_vni_output["originatorIp"] + soo = bgp_vni_output["siteOfOrigin"] + return [rd, oip, soo] + + +def rt_test(pe, vni, rd, oip, mac, ip, soo): + """ + Check installation status of a given route. + @pe = router where bgp routes are collected from + @vni = l2vni + @rd = rd of the route + @oip = originator-ip, set only for type-3 route + @mac = nlri mac, set only for type-2 + @ip = nlri ip, optionally set for type-2 + @soo = MAC-VRF SoO string, set if SoO needs to be + on the rt to be considered installed. + """ + rt = get_evpn_rt_json(pe, vni, rd, oip, mac, ip) + rt_str = rt.pop(0) + rt_global_json = rt.pop(0) + rt_l2vni_json = rt.pop(0) + + if ( + not rt_global_json + or rd not in rt_global_json + or rt_str not in rt_global_json[rd] + ): + global_installed = False + else: + global_json_paths = rt_global_json[rd][rt_str]["paths"] + global_installed = is_installed(global_json_paths, soo) + if not rt_l2vni_json: + l2vni_installed = False + else: + if not oip: + # json for RT2s in l2vni don't key by route string + l2vni_json_paths = rt_l2vni_json["paths"] + l2vni_installed = is_installed(l2vni_json_paths, soo) + elif rt_str in rt_l2vni_json and "paths" in rt_l2vni_json[rt_str]: + l2vni_json_paths = rt_l2vni_json[rt_str]["paths"] + l2vni_installed = is_installed(l2vni_json_paths, soo) + else: + l2vni_installed = False + return [global_installed, l2vni_installed] + + +def test_macvrf_soo(): + "Test MAC-VRF Site-of-Origin on pe1" + l2vni = 101 + l3vni = 404 + soo = "65000:0" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host1 = tgen.gears["host1"] + host2 = tgen.gears["host2"] + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + + # Collect pe2 RD/Originator-IP + pe2_bgp_vni = get_bgp_l2vni_fields(pe2, l2vni) + pe2_rd = pe2_bgp_vni[0] + pe2_oip = pe2_bgp_vni[1] + # Collect local addrs + h2_mac = host2.run("ip -br link show host2-eth0").split()[2] + h2_ip = host2.run("ip -4 -br addr show host2-eth0").split()[2].split("/")[0] + pe2_mac = pe2.run("ip -br link show br101").split()[2] + pe2_ip = pe2.run("ip -4 -br addr show br101").split()[2].split("/")[0] + # Route fields + pe2_svi_parms = [l2vni, pe2_rd, None, pe2_mac, pe2_ip] + pe2_imet_parms = [l2vni, pe2_rd, pe2_oip, None, None] + host2_mac_parms = [l2vni, pe2_rd, None, h2_mac, None] + host2_neigh_parms = [l2vni, pe2_rd, None, h2_mac, h2_ip] + # Route strings + pe2_svi_rt_str, _, _ = get_evpn_rt_json_str(*pe2_svi_parms) + pe2_imet_rt_str, _, _ = get_evpn_rt_json_str(*pe2_imet_parms) + host2_mac_rt_str, _, _ = get_evpn_rt_json_str(*host2_mac_parms) + host2_neigh_rt_str, _, _ = get_evpn_rt_json_str(*host2_neigh_parms) + + ## trigger mac/arp learn + host1.run("ping -c1 10.10.1.1") + host2.run("ping -c1 10.10.1.3") + + step("Test pe2/host2 routes are installed on pe1 (global/l2vni)") + + # expected state: + # - global table: present w/o soo + # - l2vni table: present w/o soo + assertmsg = "{} missing on {} in {}{} evpn table(s)" + global_parms = [pe2.name, "global", ""] + l2vni_parms = [pe2.name, "l2vni", l2vni] + # pe2's type-2 for l2vni 101 svi mac/ip + test_f = partial(rt_test, pe2, *pe2_svi_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_svi_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_svi_rt_str, *l2vni_parms) + # pe2's type-3 for l2vni 101 + test_f = partial(rt_test, pe2, *pe2_imet_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_imet_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_imet_rt_str, *l2vni_parms) + # mac-only type-2 for host2 + test_f = partial(rt_test, pe1, *host2_mac_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_mac_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_mac_rt_str, *l2vni_parms) + # mac+ip type-2 for host2 + test_f = partial(rt_test, pe1, *host2_neigh_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_neigh_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_neigh_rt_str, *l2vni_parms) + + step("Add valid SoO config to pe2") + test_f = partial(change_soo, pe2, soo, l2vni) + _, res = topotest.run_and_expect(test_f, True, count=10, wait=1) + assertmsg = "soo '{}' not properly applied on {}".format(soo, pe2.name) + assert res == True, assertmsg + + step("Test valid config applied to L2VNI on pe2") + ## expected state: + ## - global table: present w/ soo + ## - l2vni table: present w/ soo + assertmsg = "{} not originated with soo {} by {} in {}{} evpn table(s)" + global_parms = [soo, pe2.name, "global", ""] + l2vni_parms = [soo, pe2.name, "l2vni", l2vni] + # type-2 for l2vni 101 svi mac/ip + test_f = partial(rt_test, pe2, *pe2_svi_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_svi_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_svi_rt_str, *l2vni_parms) + # type-3 for l2vni 101 + test_f = partial(rt_test, pe2, *pe2_imet_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_imet_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_imet_rt_str, *l2vni_parms) + + step("Test invalid SoO config on pe2") + test_f = partial(change_soo, pe2, "1:1:1", l2vni) + _, res = topotest.run_and_expect(test_f, False, count=10, wait=1) + assertmsg = "soo '1:1:1' should not have been allowed on {}".format(pe2.name) + assert res == False, assertmsg + + step("Test valid SoO applied to host2 routes (mac-only + mac/ip) on pe2") + + ## expected state: + ## - global table: present w/ soo + ## - l2vni table: present w/ soo + assertmsg = "{} not originated with soo {} by {} in {}{} evpn table(s)" + global_parms = [soo, pe1.name, "global", ""] + l2vni_parms = [soo, pe1.name, "l2vni", l2vni] + # mac-only type-2 for host2 + test_f = partial(rt_test, pe2, *host2_mac_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_mac_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_mac_rt_str, *l2vni_parms) + # mac+ip type-2 for host2 + test_f = partial(rt_test, pe2, *host2_neigh_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_neigh_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_neigh_rt_str, *l2vni_parms) + + step("Add valid SoO to pe1") + test_f = partial(change_soo, pe1, soo, l2vni) + _, res = topotest.run_and_expect(test_f, True, count=10, wait=1) + assertmsg = "soo '{}' not properly applied on {}".format(soo, pe1.name) + assert res == True, assertmsg + + step("Test pe2's routes are filtered from l2vni on pe1.") + ## expected state: + ## - global table: present w/ soo + ## - l2vni table: not present + global_assertmsg = "{} with soo {} from {} missing from global evpn table" + l2vni_assertmsg = "{} with soo {} from {} not filtered from {}{} evpn table" + global_parms = [soo, pe1.name, "global", ""] + l2vni_parms = [soo, pe1.name, "l2vni", l2vni] + # pe2's svi route + test_f = partial(rt_test, pe1, *pe2_svi_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, False], count=30, wait=1) + assert res[0] == True, global_assertmsg.format(pe2_svi_rt_str, *global_parms) + assert res[1] == False, l2vni_assertmsg.format(pe2_svi_rt_str, *l2vni_parms) + # pe2's imet route + test_f = partial(rt_test, pe1, *pe2_imet_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, False], count=30, wait=1) + assert res[0] == True, global_assertmsg.format(pe2_imet_rt_str, *global_parms) + assert res[1] == False, l2vni_assertmsg.format(pe2_imet_rt_str, *l2vni_parms) + # mac-only type-2 for host2 + test_f = partial(rt_test, pe1, *host2_mac_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, False], count=30, wait=1) + assert res[0] == True, global_assertmsg.format(host2_mac_rt_str, *global_parms) + assert res[1] == False, l2vni_assertmsg.format(host2_mac_rt_str, *l2vni_parms) + # mac+ip type-2 for host2 + test_f = partial(rt_test, pe1, *host2_neigh_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, False], count=30, wait=1) + assert res[0] == True, global_assertmsg.format(host2_neigh_rt_str, *global_parms) + assert res[1] == False, l2vni_assertmsg.format(host2_neigh_rt_str, *l2vni_parms) + + step("Remove SoO from pe1") + test_f = partial(change_soo, pe1, "", l2vni) + _, res = topotest.run_and_expect(test_f, True, count=10, wait=1) + assertmsg = "soo '{}' not properly removed from {}".format(soo, pe1.name) + assert res == True, assertmsg + + step("Test pe2/host2 routes are installed on pe1 (global/l2vni)") + ## expected state: + ## - global table: present w/ soo + ## - l2vni table: present w/ soo + assertmsg = "{} with soo {} missing on {} in {}{} evpn table" + global_parms = [soo, pe1.name, "global", ""] + l2vni_parms = [soo, pe1.name, "l2vni", l2vni] + # pe2's type-2 for l2vni 101 svi mac/ip + test_f = partial(rt_test, pe1, *pe2_svi_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_svi_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_svi_rt_str, *l2vni_parms) + # pe2's type-3 for l2vni 101 + test_f = partial(rt_test, pe1, *pe2_imet_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_imet_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_imet_rt_str, *l2vni_parms) + # mac-only type-2 for host2 + test_f = partial(rt_test, pe1, *host2_mac_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_mac_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_mac_rt_str, *l2vni_parms) + # mac+ip type-2 for host2 + test_f = partial(rt_test, pe1, *host2_neigh_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_neigh_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_neigh_rt_str, *l2vni_parms) + + step("Remove SoO from pe2") + test_f = partial(change_soo, pe2, "", l2vni) + _, res = topotest.run_and_expect(test_f, True, count=10, wait=1) + assertmsg = "soo '{}' not properly removed from {}".format(soo, pe2.name) + assert res == True, assertmsg + + step("Test pe2's 'self' routes are installed on pe1 (global/l2vni)") + ## expected state: + ## - global table: present w/o soo + ## - l2vni table: present w/o soo + assertmsg = "{} missing on {} in {}{} evpn table(s)" + global_parms = [pe1.name, "global", ""] + l2vni_parms = [pe1.name, "l2vni", l2vni] + # pe2's type-2 for l2vni 101 svi mac/ip + test_f = partial(rt_test, pe1, *pe2_svi_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_svi_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_svi_rt_str, *l2vni_parms) + # pe2's type-3 for l2vni 101 + test_f = partial(rt_test, pe1, *pe2_imet_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_imet_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_imet_rt_str, *l2vni_parms) + # mac-only type-2 for host2 + test_f = partial(rt_test, pe1, *host2_mac_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_mac_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_mac_rt_str, *l2vni_parms) + # mac+ip type-2 for host2 + test_f = partial(rt_test, pe1, *host2_neigh_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_neigh_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_neigh_rt_str, *l2vni_parms) + + # tgen.mininet_cli() + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/bgpd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/ospfd.conf new file mode 100644 index 0000000..2db7edb --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/ospfd.conf @@ -0,0 +1,13 @@ +! +router ospf + network 10.20.0.0/16 area 0 + network 10.20.20.20/32 area 0 +! +int P1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int P1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/zebra.conf new file mode 100644 index 0000000..95b5da8 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/zebra.conf @@ -0,0 +1,7 @@ +! +interface lo + ip address 10.20.20.20/32 +interface P1-eth0 + ip address 10.20.1.2/24 +interface P1-eth1 + ip address 10.20.2.2/24 diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf new file mode 100644 index 0000000..9fb2bd6 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf @@ -0,0 +1,19 @@ +router bgp 65000 + timers bgp 3 9 + bgp router-id 10.10.10.10 + no bgp default ipv4-unicast + neighbor 10.30.30.30 remote-as 65000 + neighbor 10.30.30.30 update-source lo + neighbor 10.30.30.30 timers 3 10 + address-family l2vpn evpn + neighbor 10.30.30.30 activate + advertise-all-vni + advertise-svi-ip +! +router bgp 65000 vrf vrf-red + ! + address-family l2vpn evpn + route-target import *:300 + route-target import auto + exit-address-family +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/evpn.vni.json new file mode 100644 index 0000000..98ae92c --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/evpn.vni.json @@ -0,0 +1,16 @@ +{ + "vni":101, + "type":"L2", + "vrf":"default", + "vxlanInterface":"vxlan0", + "vtepIp":"10.10.10.10", + "mcastGroup":"0.0.0.0", + "advertiseGatewayMacip":"No", + "remoteVteps":[ + { + "ip":"10.30.30.30", + "flood":"HER" + } + ] +} + diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/ospfd.conf new file mode 100644 index 0000000..f1c2b42 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/ospfd.conf @@ -0,0 +1,9 @@ +! +router ospf + network 10.20.0.0/16 area 0 + network 10.10.10.10/32 area 0 +! +int PE1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf new file mode 100644 index 0000000..8c6cf3e --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf @@ -0,0 +1,13 @@ +! +log file zebra.log +! +vrf vrf-red + vni 100 + exit-vrf +! +! +interface lo + ip address 10.10.10.10/32 +interface PE1-eth1 + ip address 10.20.1.1/24 +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/bgpd.conf new file mode 100644 index 0000000..10809da --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/bgpd.conf @@ -0,0 +1,24 @@ +! +router bgp 65000 + timers bgp 3 9 + bgp router-id 10.30.30.30 + no bgp default ipv4-unicast + neighbor 10.10.10.10 remote-as 65000 + neighbor 10.10.10.10 update-source lo + neighbor 10.10.10.10 timers 3 10 + ! + address-family l2vpn evpn + neighbor 10.10.10.10 activate + advertise-all-vni + advertise-svi-ip +! +router bgp 65000 vrf vrf-blue + ! + address-family ipv4 unicast + redistribute static + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/evpn.vni.json new file mode 100644 index 0000000..5c05978 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/evpn.vni.json @@ -0,0 +1,15 @@ +{ + "vni":101, + "type":"L2", + "vrf":"default", + "vxlanInterface":"vxlan0", + "vtepIp":"10.30.30.30", + "mcastGroup":"0.0.0.0", + "advertiseGatewayMacip":"No", + "remoteVteps":[ + { + "ip":"10.10.10.10", + "flood":"HER" + } + ] +} diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/ospfd.conf new file mode 100644 index 0000000..065c993 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/ospfd.conf @@ -0,0 +1,9 @@ +! +router ospf + network 10.20.0.0/16 area 0 + network 10.30.30.30/32 area 0 +! +int PE2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/zebra.conf new file mode 100644 index 0000000..cee4355 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/zebra.conf @@ -0,0 +1,17 @@ +vrf vrf-blue + vni 300 + exit-vrf +! +vrf vrf-red + vni 100 + exit-vrf +! +interface lo + ip address 10.30.30.30/32 +interface PE2-eth0 + ip address 10.20.2.3/24 +! +interface vrf-blue + ip address 30.0.0.3/24 +! +ip route 4.4.4.1/32 30.0.0.100 vrf vrf-blue diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/__init__.py b/tests/topotests/bgp_evpn_vxlan_svd_topo1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/bgpd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/ospfd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/ospfd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/zebra.conf new file mode 100644 index 0000000..91fae9e --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/zebra.conf @@ -0,0 +1,3 @@ +! +int host1-eth0 + ip address 10.10.1.55/24 diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/bgpd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/ospfd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/ospfd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/zebra.conf new file mode 100644 index 0000000..df9adeb --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/zebra.conf @@ -0,0 +1,3 @@ +! +interface host2-eth0 + ip address 10.10.1.56/24 diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py b/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py new file mode 100755 index 0000000..65c0c35 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py @@ -0,0 +1,556 @@ +#!/usr/bin/env python + +# +# test_bgp_evpn_vxlan_svd.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 NVIDIA Corporation +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_evpn_vxlan.py: Test VXLAN EVPN MAC and route signalling over BGP +using Single Vxlan Device Configurtion +""" + +import os +import sys +import json +from functools import partial +from time import sleep +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import required_linux_kernel_version + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd] + + +def build_topo(tgen): + "Build function" + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # + # Create routers + tgen.add_router("P1") + tgen.add_router("PE1") + tgen.add_router("PE2") + tgen.add_router("host1") + tgen.add_router("host2") + + # Host1-PE1 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["host1"]) + switch.add_link(tgen.gears["PE1"]) + + # PE1-P1 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["PE1"]) + switch.add_link(tgen.gears["P1"]) + + # P1-PE2 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["P1"]) + switch.add_link(tgen.gears["PE2"]) + + # PE2-host2 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["PE2"]) + switch.add_link(tgen.gears["host2"]) + + +def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): + pe = tgen.gears[pe_name] + + # configure vlan aware bridge + pe.run("ip link add name bridge type bridge stp_state 0") + pe.run("ip link set dev bridge type bridge vlan_filtering 1") + pe.run("bridge vlan add vid 1 dev bridge self") + pe.run("ip link set dev bridge up") + + # setup svi + pe.run("ip link add link bridge name vlan1 type vlan id 1 protocol 802.1q") + pe.run("ip link set dev vlan1 up") + pe.run("ip addr add {0} dev vlan1".format(svi_ip)) + pe.run("/sbin/sysctl net.ipv4.conf.vlan1.arp_accept=1") + + # setup single vxlan device + pe.run( + "ip link add dev vxlan0 type vxlan dstport 4789 local {0} nolearning external".format( + tunnel_local_ip + ) + ) + pe.run("ip link set dev vxlan0 master bridge") + pe.run("bridge link set dev vxlan0 vlan_tunnel on") + pe.run("bridge link set dev vxlan0 neigh_suppress on") + pe.run("bridge link set dev vxlan0 learning off") + pe.run("bridge vlan add dev vxlan0 vid 1") + pe.run("bridge vlan add dev vxlan0 vid 1 tunnel_info id 101") + pe.run("ip link set up dev vxlan0") + + # setup PE interface + pe.run("ip link set dev {0}-{1} master bridge".format(pe_name, intf)) + pe.run("bridge vlan del vid 1 dev {0}-{1}".format(pe_name, intf)) + pe.run("bridge vlan del vid 1 untagged pvid dev {0}-{1}".format(pe_name, intf)) + pe.run("bridge vlan add vid 1 dev {0}-{1}".format(pe_name, intf)) + pe.run("bridge vlan add vid 1 pvid untagged dev {0}-{1}".format(pe_name, intf)) + + # l3vni 100 + pe.run("ip link add vrf-red type vrf table 1400") + pe.run("ip link add link bridge name vlan100 type vlan id 100 protocol 802.1q") + pe.run("ip link set dev vlan100 master vrf-blue") + pe.run("ip link set dev vlan100 up") + pe.run("bridge vlan add vid 100 dev bridge self") + pe.run("bridge vlan add dev vxlan0 vid 100") + pe.run("bridge vlan add dev vxlan0 vid 100 tunnel_info id 100") + + # add a vrf for testing DVNI + if pe_name == "PE2": + pe.run("ip link add vrf-blue type vrf table 2400") + pe.run("ip link add link bridge name vlan300 type vlan id 300 protocol 802.1q") + pe.run("ip link set dev vlan300 master vrf-blue") + pe.run("ip link set dev vlan300 up") + pe.run("bridge vlan add vid 300 dev bridge self") + pe.run("bridge vlan add dev vxlan0 vid 300") + pe.run("bridge vlan add dev vxlan0 vid 300 tunnel_info id 300") + + +def setup_p_router(tgen, p_name): + p1 = tgen.gears[p_name] + p1.run("sysctl -w net.ipv4.ip_forward=1") + + +def setup_module(mod): + "Sets up the pytest environment" + + result = required_linux_kernel_version("5.7") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >= 5.7") + + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + setup_pe_router(tgen, "PE1", "10.10.10.10", "10.10.1.1/24", "eth0") + setup_pe_router(tgen, "PE2", "10.30.30.30", "10.10.1.3/24", "eth1") + setup_p_router(tgen, "P1") + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # For all registred routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # tgen.mininet_cli() + # This function tears down the whole topology. + tgen.stop_topology() + + +def show_vni_json_elide_ifindex(pe, vni, expected): + output_json = pe.vtysh_cmd("show evpn vni {} json".format(vni), isjson=True) + + if "ifindex" in output_json: + output_json.pop("ifindex") + + return topotest.json_cmp(output_json, expected) + + +def check_vni_macs_present(tgen, router, vni, maclist): + result = router.vtysh_cmd("show evpn mac vni {} json".format(vni), isjson=True) + for rname, ifname in maclist: + m = tgen.net.macs[(rname, ifname)] + if m not in result["macs"]: + return "MAC ({}) for interface {} on {} missing on {} from {}".format( + m, ifname, rname, router.name, json.dumps(result, indent=4) + ) + return None + + +def check_flood_entry_present(pe, vni, vtep): + if not topotest.iproute2_is_fdb_get_capable(): + return None + + output = pe.run( + "bridge fdb get 00:00:00:00:00:00 dev vxlan0 vni {} self".format(vni) + ) + + if str(vtep) not in output: + return output + + return None + + +def test_pe1_converge_evpn(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + json_file = "{}/{}/evpn.vni.json".format(CWD, pe1.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(show_vni_json_elide_ifindex, pe1, 101, expected) + _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + + # Let's ensure that the hosts have actually tried talking to + # each other. Otherwise under certain startup conditions + # they may not actually do any l2 arp'ing and as such + # the bridges won't know about the hosts on their networks + host1 = tgen.gears["host1"] + host1.run("ping -c 1 10.10.1.56") + host2 = tgen.gears["host2"] + host2.run("ping -c 1 10.10.1.55") + + test_func = partial( + check_vni_macs_present, + tgen, + pe1, + 101, + (("host1", "host1-eth0"), ("host2", "host2-eth0")), + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + if result: + logger.warning("%s", result) + assert None, '"{}" missing expected MACs'.format(pe1.name) + + vtep = "10.30.30.30" + test_func = partial(check_flood_entry_present, pe1, 101, vtep) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" Flood FDB Entry for VTEP {} not found'.format(pe1.name, vtep) + assert result is None, assertmsg + + +def test_pe2_converge_evpn(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe2 = tgen.gears["PE2"] + json_file = "{}/{}/evpn.vni.json".format(CWD, pe2.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(show_vni_json_elide_ifindex, pe2, 101, expected) + _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe2.name) + assert result is None, assertmsg + + test_func = partial( + check_vni_macs_present, + tgen, + pe2, + 101, + (("host1", "host1-eth0"), ("host2", "host2-eth0")), + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + if result: + logger.warning("%s", result) + assert None, '"{}" missing expected MACs'.format(pe2.name) + + vtep = "10.10.10.10" + test_func = partial(check_flood_entry_present, pe2, 101, vtep) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" Flood FDB Entry for VTEP {} not found'.format(pe2.name, vtep) + assert result is None, assertmsg + + +def mac_learn_test(host, local): + "check the host MAC gets learned by the VNI" + + host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name)) + int_lines = host_output.splitlines() + for line in int_lines: + line_items = line.split(": ") + if "HWaddr" in line_items[0]: + mac = line_items[1] + break + + mac_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac)) + mac_output_json = json.loads(mac_output) + assertmsg = "Local MAC output does not match interface mac {}".format(mac) + assert mac_output_json[mac]["type"] == "local", assertmsg + + +def mac_test_local_remote(local, remote): + "test MAC transfer between local and remote" + + local_output = local.vtysh_cmd("show evpn mac vni all json") + remote_output = remote.vtysh_cmd("show evpn mac vni all json") + local_output_vni = local.vtysh_cmd("show evpn vni detail json") + local_output_json = json.loads(local_output) + remote_output_json = json.loads(remote_output) + local_output_vni_json = json.loads(local_output_vni) + + for vni in local_output_json: + if vni not in remote_output_json: + continue + + mac_list = local_output_json[vni]["macs"] + for mac in mac_list: + if mac_list[mac]["type"] == "local" and mac_list[mac]["intf"] != "br101": + assertmsg = "JSON output mismatches local: {} remote: {}".format( + local_output_vni_json[0]["vtepIp"], + remote_output_json[vni]["macs"][mac]["remoteVtep"], + ) + assert ( + remote_output_json[vni]["macs"][mac]["remoteVtep"] + == local_output_vni_json[0]["vtepIp"] + ), assertmsg + + +def test_learning_pe1(): + "test MAC learning on PE1" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host1 = tgen.gears["host1"] + pe1 = tgen.gears["PE1"] + mac_learn_test(host1, pe1) + + +def test_learning_pe2(): + "test MAC learning on PE2" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host2 = tgen.gears["host2"] + pe2 = tgen.gears["PE2"] + mac_learn_test(host2, pe2) + + +def test_local_remote_mac_pe1(): + "Test MAC transfer PE1 local and PE2 remote" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + mac_test_local_remote(pe1, pe2) + + +def test_local_remote_mac_pe2(): + "Test MAC transfer PE2 local and PE1 remote" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + mac_test_local_remote(pe2, pe1) + + +def ip_learn_test(tgen, host, local, remote, ip_addr): + "check the host IP gets learned by the VNI" + host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name)) + int_lines = host_output.splitlines() + for line in int_lines: + line_items = line.split(": ") + if "HWaddr" in line_items[0]: + mac = line_items[1] + break + # print(host_output) + + # check we have a local association between the MAC and IP + local_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac)) + # print(local_output) + local_output_json = json.loads(local_output) + mac_type = local_output_json[mac]["type"] + assertmsg = "Failed to learn local IP address on host {}".format(host.name) + assert local_output_json[mac]["neighbors"] != "none", assertmsg + learned_ip = local_output_json[mac]["neighbors"]["active"][0] + + assertmsg = "local learned mac wrong type: {} ".format(mac_type) + assert mac_type == "local", assertmsg + + assertmsg = ( + "learned address mismatch with configured address host: {} learned: {}".format( + ip_addr, learned_ip + ) + ) + assert ip_addr == learned_ip, assertmsg + + # now lets check the remote + count = 0 + converged = False + while count < 30: + remote_output = remote.vtysh_cmd( + "show evpn mac vni 101 mac {} json".format(mac) + ) + # print(remote_output) + remote_output_json = json.loads(remote_output) + type = remote_output_json[mac]["type"] + if not remote_output_json[mac]["neighbors"] == "none": + # due to a kernel quirk, learned IPs can be inactive + if ( + remote_output_json[mac]["neighbors"]["active"] + or remote_output_json[mac]["neighbors"]["inactive"] + ): + converged = True + break + count += 1 + sleep(1) + + # print("tries: {}".format(count)) + assertmsg = "{} remote learned mac no address: {} ".format(host.name, mac) + # some debug for this failure + if not converged == True: + log_output = remote.run("cat zebra.log") + # print(log_output) + + assert converged == True, assertmsg + if remote_output_json[mac]["neighbors"]["active"]: + learned_ip = remote_output_json[mac]["neighbors"]["active"][0] + else: + learned_ip = remote_output_json[mac]["neighbors"]["inactive"][0] + assertmsg = "remote learned mac wrong type: {} ".format(type) + assert type == "remote", assertmsg + + assertmsg = "remote learned address mismatch with configured address host: {} learned: {}".format( + ip_addr, learned_ip + ) + assert ip_addr == learned_ip, assertmsg + + +def test_ip_pe1_learn(): + "run the IP learn test for PE1" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host1 = tgen.gears["host1"] + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + # pe2.vtysh_cmd("debug zebra vxlan") + # pe2.vtysh_cmd("debug zebra kernel") + # lets populate that arp cache + host1.run("ping -c1 10.10.1.1") + ip_learn_test(tgen, host1, pe1, pe2, "10.10.1.55") + # tgen.mininet_cli() + + +def test_ip_pe2_learn(): + "run the IP learn test for PE2" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host2 = tgen.gears["host2"] + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + # pe1.vtysh_cmd("debug zebra vxlan") + # pe1.vtysh_cmd("debug zebra kernel") + # lets populate that arp cache + host2.run("ping -c1 10.10.1.3") + ip_learn_test(tgen, host2, pe2, pe1, "10.10.1.56") + # tgen.mininet_cli() + + +def show_dvni_route(pe, vni, prefix, vrf): + output = pe.vtysh_cmd("show ip route vrf {} {}".format(vrf, prefix)) + + if str(vni) not in output: + return output + + output = pe.run("ip route show vrf {} {}".format(vrf, prefix)) + + if str(vni) not in output: + return output + + return None + + +def test_dvni(): + "test Downstream VNI works as expected importing into PE1" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + + prefix = "4.4.4.1/32" + test_func = partial(show_dvni_route, pe1, 300, prefix, "vrf-red") + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" DVNI route {} not found'.format(pe1.name, prefix) + assert result is None, assertmsg + # tgen.mininet_cli() + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/P1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/P1/bgpd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/P1/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/P1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/P1/ospfd.conf new file mode 100644 index 0000000..2db7edb --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/P1/ospfd.conf @@ -0,0 +1,13 @@ +! +router ospf + network 10.20.0.0/16 area 0 + network 10.20.20.20/32 area 0 +! +int P1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int P1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/P1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_topo1/P1/zebra.conf new file mode 100644 index 0000000..95b5da8 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/P1/zebra.conf @@ -0,0 +1,7 @@ +! +interface lo + ip address 10.20.20.20/32 +interface P1-eth0 + ip address 10.20.1.2/24 +interface P1-eth1 + ip address 10.20.2.2/24 diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/bgpd.conf new file mode 100644 index 0000000..dbbfc82 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 65000 + timers bgp 3 9 + bgp router-id 10.10.10.10 + no bgp default ipv4-unicast + neighbor 10.30.30.30 remote-as 65000 + neighbor 10.30.30.30 update-source lo + neighbor 10.30.30.30 timers 3 10 + address-family l2vpn evpn + neighbor 10.30.30.30 activate + advertise-all-vni + advertise-svi-ip diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE1/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/evpn.vni.json new file mode 100644 index 0000000..eb8668b --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/evpn.vni.json @@ -0,0 +1,17 @@ +{ + "vni":101, + "type":"L2", + "tenantVrf":"default", + "vxlanInterface":"vxlan101", + "vtepIp":"10.10.10.10", + "mcastGroup":"0.0.0.0", + "advertiseGatewayMacip":"No", + "numRemoteVteps":1, + "remoteVteps":[ + { + "ip":"10.30.30.30", + "flood":"HER" + } + ] +} + diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/ospfd.conf new file mode 100644 index 0000000..f1c2b42 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/ospfd.conf @@ -0,0 +1,9 @@ +! +router ospf + network 10.20.0.0/16 area 0 + network 10.10.10.10/32 area 0 +! +int PE1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/zebra.conf new file mode 100644 index 0000000..e269947 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/zebra.conf @@ -0,0 +1,8 @@ +! +log file zebra.log +! +interface lo + ip address 10.10.10.10/32 +interface PE1-eth1 + ip address 10.20.1.1/24 +! diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/bgpd.conf new file mode 100644 index 0000000..52f8687 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65000 + timers bgp 3 9 + bgp router-id 10.30.30.30 + no bgp default ipv4-unicast + neighbor 10.10.10.10 remote-as 65000 + neighbor 10.10.10.10 update-source lo + neighbor 10.10.10.10 timers 3 10 + ! + address-family l2vpn evpn + neighbor 10.10.10.10 activate + advertise-all-vni + advertise-svi-ip diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE2/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/evpn.vni.json new file mode 100644 index 0000000..befb416 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/evpn.vni.json @@ -0,0 +1,16 @@ +{ + "vni":101, + "type":"L2", + "tenantVrf":"default", + "vxlanInterface":"vxlan101", + "vtepIp":"10.30.30.30", + "mcastGroup":"0.0.0.0", + "advertiseGatewayMacip":"No", + "numRemoteVteps":1, + "remoteVteps":[ + { + "ip":"10.10.10.10", + "flood":"HER" + } + ] +} diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/ospfd.conf new file mode 100644 index 0000000..065c993 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/ospfd.conf @@ -0,0 +1,9 @@ +! +router ospf + network 10.20.0.0/16 area 0 + network 10.30.30.30/32 area 0 +! +int PE2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/zebra.conf new file mode 100644 index 0000000..9738916 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/zebra.conf @@ -0,0 +1,6 @@ +! +interface lo + ip address 10.30.30.30/32 +interface PE2-eth0 + ip address 10.20.2.3/24 +! diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/__init__.py b/tests/topotests/bgp_evpn_vxlan_topo1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/host1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/host1/bgpd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/host1/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/host1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/host1/ospfd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/host1/ospfd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/host1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_topo1/host1/zebra.conf new file mode 100644 index 0000000..91fae9e --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/host1/zebra.conf @@ -0,0 +1,3 @@ +! +int host1-eth0 + ip address 10.10.1.55/24 diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/host2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/host2/bgpd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/host2/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/host2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/host2/ospfd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/host2/ospfd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/host2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_topo1/host2/zebra.conf new file mode 100644 index 0000000..df9adeb --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/host2/zebra.conf @@ -0,0 +1,3 @@ +! +interface host2-eth0 + ip address 10.10.1.56/24 diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py b/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py new file mode 100755 index 0000000..2884043 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py @@ -0,0 +1,436 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_evpn_vxlan.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# + +""" +test_bgp_evpn_vxlan.py: Test VXLAN EVPN MAC a route signalling over BGP. +""" + +import os +import sys +import json +from functools import partial +from time import sleep +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd] + + +def build_topo(tgen): + "Build function" + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # + # Create routers + tgen.add_router("P1") + tgen.add_router("PE1") + tgen.add_router("PE2") + tgen.add_router("host1") + tgen.add_router("host2") + + # Host1-PE1 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["host1"]) + switch.add_link(tgen.gears["PE1"]) + + # PE1-P1 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["PE1"]) + switch.add_link(tgen.gears["P1"]) + + # P1-PE2 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["P1"]) + switch.add_link(tgen.gears["PE2"]) + + # PE2-host2 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["PE2"]) + switch.add_link(tgen.gears["host2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + p1 = tgen.gears["P1"] + + # set up PE bridges with the EVPN member interfaces facing the CE hosts + pe1.run("ip link add name br101 type bridge stp_state 0") + pe1.run("ip addr add 10.10.1.1/24 dev br101") + pe1.run("ip link set dev br101 up") + pe1.run( + "ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.10.10.10 nolearning" + ) + pe1.run("ip link set dev vxlan101 master br101") + pe1.run("ip link set up dev vxlan101") + pe1.run("ip link set dev PE1-eth0 master br101") + + pe2.run("ip link add name br101 type bridge stp_state 0") + pe2.run("ip addr add 10.10.1.3/24 dev br101") + pe2.run("ip link set dev br101 up") + pe2.run( + "ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.30.30.30 nolearning" + ) + pe2.run("ip link set dev vxlan101 master br101") + pe2.run("ip link set up dev vxlan101") + pe2.run("ip link set dev PE2-eth1 master br101") + p1.run("sysctl -w net.ipv4.ip_forward=1") + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def show_vni_json_elide_ifindex(pe, vni, expected): + output_json = pe.vtysh_cmd("show evpn vni {} json".format(vni), isjson=True) + if "ifindex" in output_json: + output_json.pop("ifindex") + + return topotest.json_cmp(output_json, expected) + + +def check_vni_macs_present(tgen, router, vni, maclist): + result = router.vtysh_cmd("show evpn mac vni {} json".format(vni), isjson=True) + for rname, ifname in maclist: + m = tgen.net.macs[(rname, ifname)] + if m not in result["macs"]: + return "MAC ({}) for interface {} on {} missing on {} from {}".format( + m, ifname, rname, router.name, json.dumps(result, indent=4) + ) + return None + + +def test_pe1_converge_evpn(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + json_file = "{}/{}/evpn.vni.json".format(CWD, pe1.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(show_vni_json_elide_ifindex, pe1, 101, expected) + _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + + # Let's ensure that the hosts have actually tried talking to + # each other. Otherwise under certain startup conditions + # they may not actually do any l2 arp'ing and as such + # the bridges won't know about the hosts on their networks + host1 = tgen.gears["host1"] + host1.run("ping -c 1 10.10.1.56") + host2 = tgen.gears["host2"] + host2.run("ping -c 1 10.10.1.55") + + test_func = partial( + check_vni_macs_present, + tgen, + pe1, + 101, + (("host1", "host1-eth0"), ("host2", "host2-eth0")), + ) + + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + if result: + logger.warning("%s", result) + assert None, '"{}" missing expected MACs'.format(pe1.name) + + +def test_pe2_converge_evpn(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe2 = tgen.gears["PE2"] + json_file = "{}/{}/evpn.vni.json".format(CWD, pe2.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(show_vni_json_elide_ifindex, pe2, 101, expected) + _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe2.name) + assert result is None, assertmsg + + test_func = partial( + check_vni_macs_present, + tgen, + pe2, + 101, + (("host1", "host1-eth0"), ("host2", "host2-eth0")), + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + if result: + logger.warning("%s", result) + assert None, '"{}" missing expected MACs'.format(pe2.name) + + +def mac_learn_test(host, local): + "check the host MAC gets learned by the VNI" + + host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name)) + int_lines = host_output.splitlines() + for line in int_lines: + line_items = line.split(": ") + if "HWaddr" in line_items[0]: + mac = line_items[1] + break + + mac_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac)) + mac_output_json = json.loads(mac_output) + assertmsg = "Local MAC output does not match interface mac {}".format(mac) + assert mac_output_json[mac]["type"] == "local", assertmsg + + +def mac_test_local_remote(local, remote): + "test MAC transfer between local and remote" + + local_output = local.vtysh_cmd("show evpn mac vni all json") + remote_output = remote.vtysh_cmd("show evpn mac vni all json") + local_output_vni = local.vtysh_cmd("show evpn vni detail json") + local_output_json = json.loads(local_output) + remote_output_json = json.loads(remote_output) + local_output_vni_json = json.loads(local_output_vni) + + for vni in local_output_json: + mac_list = local_output_json[vni]["macs"] + for mac in mac_list: + if mac_list[mac]["type"] == "local" and mac_list[mac]["intf"] != "br101": + assertmsg = "JSON output mismatches local: {} remote: {}".format( + local_output_vni_json[0]["vtepIp"], + remote_output_json[vni]["macs"][mac]["remoteVtep"], + ) + assert ( + remote_output_json[vni]["macs"][mac]["remoteVtep"] + == local_output_vni_json[0]["vtepIp"] + ), assertmsg + + +def test_learning_pe1(): + "test MAC learning on PE1" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host1 = tgen.gears["host1"] + pe1 = tgen.gears["PE1"] + mac_learn_test(host1, pe1) + + +def test_learning_pe2(): + "test MAC learning on PE2" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host2 = tgen.gears["host2"] + pe2 = tgen.gears["PE2"] + mac_learn_test(host2, pe2) + + +def test_local_remote_mac_pe1(): + "Test MAC transfer PE1 local and PE2 remote" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + mac_test_local_remote(pe1, pe2) + + +def test_local_remote_mac_pe2(): + "Test MAC transfer PE2 local and PE1 remote" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + mac_test_local_remote(pe2, pe1) + + # Memory leak test template + + +def ip_learn_test(tgen, host, local, remote, ip_addr): + "check the host IP gets learned by the VNI" + host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name)) + int_lines = host_output.splitlines() + for line in int_lines: + line_items = line.split(": ") + if "HWaddr" in line_items[0]: + mac = line_items[1] + break + print(host_output) + + # check we have a local association between the MAC and IP + local_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac)) + print(local_output) + local_output_json = json.loads(local_output) + mac_type = local_output_json[mac]["type"] + assertmsg = "Failed to learn local IP address on host {}".format(host.name) + assert local_output_json[mac]["neighbors"] != "none", assertmsg + learned_ip = local_output_json[mac]["neighbors"]["active"][0] + + assertmsg = "local learned mac wrong type: {} ".format(mac_type) + assert mac_type == "local", assertmsg + + assertmsg = ( + "learned address mismatch with configured address host: {} learned: {}".format( + ip_addr, learned_ip + ) + ) + assert ip_addr == learned_ip, assertmsg + + # now lets check the remote + count = 0 + converged = False + while count < 30: + remote_output = remote.vtysh_cmd( + "show evpn mac vni 101 mac {} json".format(mac) + ) + print(remote_output) + remote_output_json = json.loads(remote_output) + type = remote_output_json[mac]["type"] + if not remote_output_json[mac]["neighbors"] == "none": + # due to a kernel quirk, learned IPs can be inactive + if ( + remote_output_json[mac]["neighbors"]["active"] + or remote_output_json[mac]["neighbors"]["inactive"] + ): + converged = True + break + count += 1 + sleep(1) + + print("tries: {}".format(count)) + assertmsg = "{} remote learned mac no address: {} ".format(host.name, mac) + # some debug for this failure + if not converged == True: + log_output = remote.run("cat zebra.log") + print(log_output) + + assert converged == True, assertmsg + if remote_output_json[mac]["neighbors"]["active"]: + learned_ip = remote_output_json[mac]["neighbors"]["active"][0] + else: + learned_ip = remote_output_json[mac]["neighbors"]["inactive"][0] + assertmsg = "remote learned mac wrong type: {} ".format(type) + assert type == "remote", assertmsg + + assertmsg = "remote learned address mismatch with configured address host: {} learned: {}".format( + ip_addr, learned_ip + ) + assert ip_addr == learned_ip, assertmsg + + +def test_ip_pe1_learn(): + "run the IP learn test for PE1" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host1 = tgen.gears["host1"] + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + # pe2.vtysh_cmd("debug zebra vxlan") + # pe2.vtysh_cmd("debug zebra kernel") + # lets populate that arp cache + host1.run("ping -c1 10.10.1.1") + ip_learn_test(tgen, host1, pe1, pe2, "10.10.1.55") + # tgen.mininet_cli() + + +def test_ip_pe2_learn(): + "run the IP learn test for PE2" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host2 = tgen.gears["host2"] + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + # pe1.vtysh_cmd("debug zebra vxlan") + # pe1.vtysh_cmd("debug zebra kernel") + # lets populate that arp cache + host2.run("ping -c1 10.10.1.3") + ip_learn_test(tgen, host2, pe2, pe1, "10.10.1.56") + # tgen.mininet_cli() + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_extcomm_list_delete/__init__.py b/tests/topotests/bgp_extcomm_list_delete/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_extcomm_list_delete/r1/bgpd.conf b/tests/topotests/bgp_extcomm_list_delete/r1/bgpd.conf new file mode 100644 index 0000000..3394c1c --- /dev/null +++ b/tests/topotests/bgp_extcomm_list_delete/r1/bgpd.conf @@ -0,0 +1,20 @@ +router bgp 65000 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.255.2 remote-as 65001 + address-family ipv4 unicast + network 10.10.10.1/32 route-map r2-out-rt + network 10.10.10.2/32 route-map r2-out-soo + network 10.10.10.3/32 route-map r2-out-nt + redistribute connected + exit-address-family +! +route-map r2-out-rt permit 10 + set extcommunity rt 1.1.1.1:1 2.2.2.2:2 3.3.3.3:3 4.4.4.4:4 +! +route-map r2-out-soo permit 20 + set extcommunity soo 1.1.1.1:1 2.2.2.2:2 3.3.3.3:3 4.4.4.4:4 +! +route-map r2-out-nt permit 30 + set extcommunity nt 192.168.255.2:0 2.2.2.2:0 3.3.3.3:0 4.4.4.4:0 +! diff --git a/tests/topotests/bgp_extcomm_list_delete/r1/zebra.conf b/tests/topotests/bgp_extcomm_list_delete/r1/zebra.conf new file mode 100644 index 0000000..e2c399e --- /dev/null +++ b/tests/topotests/bgp_extcomm_list_delete/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_extcomm_list_delete/r2/bgpd.conf b/tests/topotests/bgp_extcomm_list_delete/r2/bgpd.conf new file mode 100644 index 0000000..ca497e6 --- /dev/null +++ b/tests/topotests/bgp_extcomm_list_delete/r2/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 + neighbor 192.168.255.1 route-map r1-in in + exit-address-family +! +route-map r1-in permit 10 +! diff --git a/tests/topotests/bgp_extcomm_list_delete/r2/zebra.conf b/tests/topotests/bgp_extcomm_list_delete/r2/zebra.conf new file mode 100644 index 0000000..606c17b --- /dev/null +++ b/tests/topotests/bgp_extcomm_list_delete/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_extcomm_list_delete/test_bgp_extcomm-list_delete.py b/tests/topotests/bgp_extcomm_list_delete/test_bgp_extcomm-list_delete.py new file mode 100644 index 0000000..eb05986 --- /dev/null +++ b/tests/topotests/bgp_extcomm_list_delete/test_bgp_extcomm-list_delete.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright 2023 6WIND S.A. +# Authored by Farid Mihoub +# + +""" +bgp_extcomm_list-delete.py: + +Test the following commands: +route-map test permit 10 + set extended-comm-list delete +""" + +import functools +import json +import os +import pytest +import re +import sys + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib import topotest + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_convergence(): + tgen = get_topogen() + r2 = tgen.gears["r2"] + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 4, + } + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge initially" + + +def _set_extcomm_list(gear, ecom_t, ecom): + "Set the extended community for deletion." + cmd = [ + "con t\n", + f"bgp extcommunity-list standard r1-{ecom_t} permit {ecom_t} {ecom}\n", + f"route-map r1-in permit 10\n", + f"set extended-comm-list r1-{ecom_t} delete\n", + ] + gear.vtysh_cmd("".join(cmd)) + + +def _bgp_extcomm_list_del_check(gear, prefix, ecom): + """ + Check the non-presense of the extended community for the given prefix. + """ + # get the extended community list attribute for the given prefix + output = json.loads(gear.vtysh_cmd(f"show ip bgp {prefix} json")) + ecoms = output.get("paths", [])[0].get("extendedCommunity", {}) + ecoms = ecoms.get("string") + + # ecoms might be None at the first time + if not ecoms: + return False + return re.search(ecom, ecoms) is None + + +def test_rt_extcomm_list_delete(): + tgen = get_topogen() + r2 = tgen.gears["r2"] + + # set the extended community for deletion + _set_extcomm_list(r2, "rt", "1.1.1.1:1") + + # check for the deletion of the extended community + test_func = functools.partial( + _bgp_extcomm_list_del_check, r2, "10.10.10.1/32", r"1.1.1.1:1") + _, result = topotest.run_and_expect(test_func, True, count=60, wait=0.5) + assert result, "RT extended community 1.1.1.1:1 was not stripped." + + +def test_soo_extcomm_list_delete(): + tgen = get_topogen() + r2 = tgen.gears["r2"] + + # set the extended community for deletion + _set_extcomm_list(r2, "soo", "2.2.2.2:2") + + # check for the deletion of the extended community + test_func = functools.partial( + _bgp_extcomm_list_del_check, r2, "10.10.10.2/32", r"2.2.2.2:2") + _, result = topotest.run_and_expect(test_func, True, count=60, wait=0.5) + assert result, "SoO extended community 2.2.2.2:2 was not stripped." + + +def test_nt_extcomm_list_delete(): + tgen = get_topogen() + r2 = tgen.gears["r2"] + + # set the extended community for deletion + _set_extcomm_list(r2, "nt", "3.3.3.3:0") + + # check for the deletion of the extended community + test_func = functools.partial( + _bgp_extcomm_list_del_check, r2, "10.10.10.3/32", r"3.3.3.3") + _, result = topotest.run_and_expect(test_func, True, count=60, wait=0.5) + assert result, "NT extended community 3.3.3.3:0 was not stripped." + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_extended_optional_parameters_length/__init__.py b/tests/topotests/bgp_extended_optional_parameters_length/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_extended_optional_parameters_length/r1/bgpd.conf b/tests/topotests/bgp_extended_optional_parameters_length/r1/bgpd.conf new file mode 100644 index 0000000..d83013c --- /dev/null +++ b/tests/topotests/bgp_extended_optional_parameters_length/r1/bgpd.conf @@ -0,0 +1,6 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 extended-optional-parameters +! diff --git a/tests/topotests/bgp_extended_optional_parameters_length/r1/zebra.conf b/tests/topotests/bgp_extended_optional_parameters_length/r1/zebra.conf new file mode 100644 index 0000000..b29940f --- /dev/null +++ b/tests/topotests/bgp_extended_optional_parameters_length/r1/zebra.conf @@ -0,0 +1,4 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_extended_optional_parameters_length/r2/bgpd.conf b/tests/topotests/bgp_extended_optional_parameters_length/r2/bgpd.conf new file mode 100644 index 0000000..e390d6e --- /dev/null +++ b/tests/topotests/bgp_extended_optional_parameters_length/r2/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 extended-optional-parameters + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_extended_optional_parameters_length/r2/zebra.conf b/tests/topotests/bgp_extended_optional_parameters_length/r2/zebra.conf new file mode 100644 index 0000000..dc15cf7 --- /dev/null +++ b/tests/topotests/bgp_extended_optional_parameters_length/r2/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.16.1/32 +! +int r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_extended_optional_parameters_length/test_bgp_extended_optional_parameters_length.py b/tests/topotests/bgp_extended_optional_parameters_length/test_bgp_extended_optional_parameters_length.py new file mode 100644 index 0000000..eef122b --- /dev/null +++ b/tests/topotests/bgp_extended_optional_parameters_length/test_bgp_extended_optional_parameters_length.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2021 by +# Donatas Abraitis +# + +""" +Test if Extended Optional Parameters Length encoding format works +if forced with a knob. +https://datatracker.ietf.org/doc/html/rfc9072 +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_extended_optional_parameters_length(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast summary json")) + expected = { + "peers": { + "192.168.1.2": { + "pfxRcd": 2, + "pfxSnt": 2, + "state": "Established", + "peerState": "OK", + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge with extended-optional-parameters" + + def _bgp_extended_optional_parameters_length(router): + output = json.loads(router.vtysh_cmd("show bgp neighbor 192.168.1.2 json")) + expected = {"192.168.1.2": {"extendedOptionalParametersLength": True}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_extended_optional_parameters_length, router) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't see Extended Optional Parameters Length to be used" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_features/r1/bgp_delayopen_neighbor.json b/tests/topotests/bgp_features/r1/bgp_delayopen_neighbor.json new file mode 100644 index 0000000..5caaeab --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_delayopen_neighbor.json @@ -0,0 +1,6 @@ +{ + "192.168.101.2":{ + "remoteAs":65100, + "bgpTimerDelayOpenTimeMsecs":240000 + } +} diff --git a/tests/topotests/bgp_features/r1/bgp_delayopen_summary_established.json b/tests/topotests/bgp_features/r1/bgp_delayopen_summary_established.json new file mode 100644 index 0000000..3ab3588 --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_delayopen_summary_established.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65000, + "peers":{ + "192.168.101.2":{ + "remoteAs":65100, + "state":"Established" + } + } +} +} diff --git a/tests/topotests/bgp_features/r1/bgp_delayopen_summary_shutdown.json b/tests/topotests/bgp_features/r1/bgp_delayopen_summary_shutdown.json new file mode 100644 index 0000000..9a41236 --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_delayopen_summary_shutdown.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65000, + "peers":{ + "192.168.101.2":{ + "remoteAs":65100, + "state":"Idle (Admin)" + } + } +} +} diff --git a/tests/topotests/bgp_features/r1/bgp_shutdown_summary.json b/tests/topotests/bgp_features/r1/bgp_shutdown_summary.json new file mode 100644 index 0000000..d986071 --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_shutdown_summary.json @@ -0,0 +1,19 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.0.1", + "as":65000, + "vrfName":"default", + "peerCount":2, + "peers":{ + "192.168.0.2":{ + "remoteAs":65000, + "state":"Idle (Admin)" + }, + "192.168.101.2":{ + "remoteAs":65100, + "state":"Idle (Admin)" + } + }, + "totalPeers":2 +} +} diff --git a/tests/topotests/bgp_features/r1/bgp_summary.json b/tests/topotests/bgp_features/r1/bgp_summary.json new file mode 100644 index 0000000..1ad7342 --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_summary.json @@ -0,0 +1,27 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.0.1", + "as":65000, + "vrfName":"default", + "peerCount":2, + "peers":{ + "192.168.0.2":{ + "hostname":"r2", + "remoteAs":65000, + "outq":0, + "inq":0, + "pfxRcd":7, + "state":"Established" + }, + "192.168.101.2":{ + "hostname":"r4", + "remoteAs":65100, + "outq":0, + "inq":0, + "pfxRcd":3, + "state":"Established" + } + }, + "totalPeers":2 +} +} diff --git a/tests/topotests/bgp_features/r1/bgpd.conf b/tests/topotests/bgp_features/r1/bgpd.conf new file mode 100644 index 0000000..74d1993 --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgpd.conf @@ -0,0 +1,41 @@ +! +hostname r1 +log file bgpd.log +! +router bgp 65000 + timers bgp 3 10 + coalesce-time 0 + bgp router-id 192.168.0.1 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 192.168.0.2 remote-as 65000 + neighbor 192.168.0.2 timers 3 10 + neighbor 192.168.0.2 description Router R2 (iBGP) + neighbor 192.168.0.2 update-source lo + neighbor 192.168.0.2 timers connect 5 + neighbor 192.168.101.2 remote-as 65100 + neighbor 192.168.101.2 timers 3 10 + neighbor 192.168.101.2 description Router R4 (eBGP AS 65100) + neighbor 192.168.101.2 timers connect 5 + ! + address-family ipv4 unicast + network 192.168.0.0/24 + network 192.168.1.0/24 + network 192.168.2.0/24 + network 192.168.3.0/24 + network 192.168.6.0/24 + network 192.168.8.0/24 + neighbor 192.168.101.2 route-map testmap-in in + neighbor 192.168.101.2 route-map testmap-out out + exit-address-family +! +! +! +route-map testmap-in permit 999 +! +route-map testmap-out permit 999 +! +! +line vty +! + diff --git a/tests/topotests/bgp_features/r1/ip_route.json b/tests/topotests/bgp_features/r1/ip_route.json new file mode 100644 index 0000000..34c5803 --- /dev/null +++ b/tests/topotests/bgp_features/r1/ip_route.json @@ -0,0 +1,364 @@ +{ + "0.0.0.0\/0":[ + { + "prefix":"0.0.0.0\/0", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.101.2", + "afi":"ipv4", + "interfaceName":"r1-eth3", + "active":true, + "weight":1 + } + ] + } + ], + "192.168.0.1\/32":[ + { + "prefix":"192.168.0.1\/32", + "protocol":"ospf", + "distance":110, + "metric":0, + "nexthops":[ + { + "flags":1, + "directlyConnected":true, + "interfaceName":"lo", + "active":true, + "weight":1 + } + ] + }, + { + "prefix":"192.168.0.1\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "192.168.0.2\/32":[ + { + "prefix":"192.168.0.2\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "active":true, + "weight":1 + } + ] + } + ], + "192.168.0.3\/32":[ + { + "prefix":"192.168.0.3\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.3.2", + "afi":"ipv4", + "interfaceName":"r1-eth2", + "active":true, + "weight":1 + } + ] + } + ], + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "table":254, + "nexthops":[ + { + "flags":1, + "directlyConnected":true, + "interfaceName":"r1-eth1", + "active":true, + "weight":1 + } + ] + }, + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "active":true, + "weight":1 + }, + { + "flags":3, + "fib":true, + "ip":"192.168.3.2", + "afi":"ipv4", + "interfaceName":"r1-eth2", + "active":true, + "weight":1 + } + ] + } + ], + "192.168.3.0\/24":[ + { + "prefix":"192.168.3.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "flags":1, + "directlyConnected":true, + "interfaceName":"r1-eth2", + "active":true, + "weight":1 + } + ] + }, + { + "prefix":"192.168.3.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth2", + "active":true + } + ] + } + ], + "192.168.6.0\/24":[ + { + "prefix":"192.168.6.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "flags":1, + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + }, + { + "prefix":"192.168.6.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "192.168.7.0\/24":[ + { + "prefix":"192.168.7.0\/24", + "protocol":"bgp", + "distance":200, + "metric":0, + "nexthops":[ + { + "flags":5, + "ip":"192.168.0.2", + "afi":"ipv4", + "active":true, + "recursive":true, + "weight":1 + }, + { + "flags":1, + "ip":"192.168.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "active":true, + "weight":1 + } + ] + }, + { + "prefix":"192.168.7.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "active":true, + "weight":1 + } + ] + } + ], + "192.168.8.0\/24":[ + { + "prefix":"192.168.8.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.3.2", + "afi":"ipv4", + "interfaceName":"r1-eth2", + "active":true, + "weight":1 + } + ] + } + ], + "192.168.101.0\/24":[ + { + "prefix":"192.168.101.0\/24", + "protocol":"bgp", + "distance":20, + "metric":0, + "nexthops":[ + { + "flags":0, + "ip":"192.168.101.2", + "afi":"ipv4", + "weight":1 + } + ] + }, + { + "prefix":"192.168.101.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth3", + "active":true + } + ] + } + ], + "192.168.102.0\/24":[ + { + "prefix":"192.168.102.0\/24", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.101.2", + "afi":"ipv4", + "interfaceName":"r1-eth3", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_features/r1/ip_route_norib.json b/tests/topotests/bgp_features/r1/ip_route_norib.json new file mode 100644 index 0000000..f6536d1 --- /dev/null +++ b/tests/topotests/bgp_features/r1/ip_route_norib.json @@ -0,0 +1,281 @@ +{ + "192.168.0.1\/32":[ + { + "prefix":"192.168.0.1\/32", + "protocol":"ospf", + "distance":110, + "metric":0, + "nexthops":[ + { + "flags":1, + "directlyConnected":true, + "interfaceName":"lo", + "active":true, + "weight":1 + } + ] + }, + { + "prefix":"192.168.0.1\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "192.168.0.2\/32":[ + { + "prefix":"192.168.0.2\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "active":true, + "weight":1 + } + ] + } + ], + "192.168.0.3\/32":[ + { + "prefix":"192.168.0.3\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.3.2", + "afi":"ipv4", + "interfaceName":"r1-eth2", + "active":true, + "weight":1 + } + ] + } + ], + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "flags":1, + "directlyConnected":true, + "interfaceName":"r1-eth1", + "active":true, + "weight":1 + } + ] + }, + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "active":true, + "weight":1 + }, + { + "flags":3, + "fib":true, + "ip":"192.168.3.2", + "afi":"ipv4", + "interfaceName":"r1-eth2", + "active":true, + "weight":1 + } + ] + } + ], + "192.168.3.0\/24":[ + { + "prefix":"192.168.3.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "flags":1, + "directlyConnected":true, + "interfaceName":"r1-eth2", + "active":true, + "weight":1 + } + ] + }, + { + "prefix":"192.168.3.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth2", + "active":true + } + ] + } + ], + "192.168.6.0\/24":[ + { + "prefix":"192.168.6.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "flags":1, + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + }, + { + "prefix":"192.168.6.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "192.168.7.0\/24":[ + { + "prefix":"192.168.7.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "active":true, + "weight":1 + } + ] + } + ], + "192.168.8.0\/24":[ + { + "prefix":"192.168.8.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.3.2", + "afi":"ipv4", + "interfaceName":"r1-eth2", + "active":true, + "weight":1 + } + ] + } + ], + "192.168.101.0\/24":[ + { + "prefix":"192.168.101.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth3", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_features/r1/ospf6d.conf b/tests/topotests/bgp_features/r1/ospf6d.conf new file mode 100644 index 0000000..9afc6f4 --- /dev/null +++ b/tests/topotests/bgp_features/r1/ospf6d.conf @@ -0,0 +1,21 @@ +log file ospf6d.log +! +! debug ospf6 neighbor +! +interface r1-lo +! +interface r1-eth1 + ipv6 ospf6 priority 10 +! +interface r1-eth2 + ipv6 ospf6 priority 10 +! +router ospf6 + ospf6 router-id 192.168.0.1 + log-adjacency-changes + interface r1-lo area 0.0.0.0 + interface r1-eth1 area 0.0.0.0 + interface r1-eth2 area 0.0.0.0 +! +line vty +! diff --git a/tests/topotests/bgp_features/r1/ospf_neighbor.json b/tests/topotests/bgp_features/r1/ospf_neighbor.json new file mode 100644 index 0000000..caf700d --- /dev/null +++ b/tests/topotests/bgp_features/r1/ospf_neighbor.json @@ -0,0 +1,16 @@ +{ + "neighbors":{ + "192.168.0.2":[ + { + "nbrPriority":5, + "converged":"Full" + } + ], + "192.168.0.3":[ + { + "nbrPriority":5, + "converged":"Full" + } + ] + } +} diff --git a/tests/topotests/bgp_features/r1/ospfd.conf b/tests/topotests/bgp_features/r1/ospfd.conf new file mode 100644 index 0000000..aef017f --- /dev/null +++ b/tests/topotests/bgp_features/r1/ospfd.conf @@ -0,0 +1,26 @@ +log file ospfd.log +! +! debug ospf event +! debug ospf zebra +! +interface r1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf priority 10 +! +interface r1-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf priority 10 +! +router ospf + ospf router-id 192.168.0.1 + log-adjacency-changes + network 192.168.0.0/20 area 0.0.0.0 + timers throttle spf 0 0 0 + timers lsa min-arrival 10 + timers throttle lsa all 0 + refresh timer 10 +! +line vty +! diff --git a/tests/topotests/bgp_features/r1/show_bgp.json b/tests/topotests/bgp_features/r1/show_bgp.json new file mode 100644 index 0000000..45e0e17 --- /dev/null +++ b/tests/topotests/bgp_features/r1/show_bgp.json @@ -0,0 +1,350 @@ +{ + "vrfName": "default", + "routerId": "192.168.0.1", + "defaultLocPrf": 100, + "localAS": 65000, + "routes": { "0.0.0.0/0": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"0.0.0.0", + "prefixLen":0, + "network":"0.0.0.0\/0", + "weight":0, + "peerId":"192.168.101.2", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r4", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.0.0/24": [ + { + "pathFrom":"external", + "prefix":"192.168.0.0", + "prefixLen":24, + "network":"192.168.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.1.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.1.0", + "prefixLen":24, + "network":"192.168.1.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.1.0", + "prefixLen":24, + "network":"192.168.1.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.2.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.2.0", + "prefixLen":24, + "network":"192.168.2.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.2.0", + "prefixLen":24, + "network":"192.168.2.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.3.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.3.0", + "prefixLen":24, + "network":"192.168.3.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.3.0", + "prefixLen":24, + "network":"192.168.3.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.6.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.6.0", + "prefixLen":24, + "network":"192.168.6.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.7.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"internal", + "prefix":"192.168.7.0", + "prefixLen":24, + "network":"192.168.7.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.8.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.8.0", + "prefixLen":24, + "network":"192.168.8.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.8.0", + "prefixLen":24, + "network":"192.168.8.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.101.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.101.0", + "prefixLen":24, + "network":"192.168.101.0\/24", + "metric":0, + "weight":0, + "peerId":"192.168.101.2", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r4", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.102.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.102.0", + "prefixLen":24, + "network":"192.168.102.0\/24", + "metric":0, + "weight":0, + "peerId":"192.168.101.2", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r4", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.201.0/24": [ + { + "pathFrom":"internal", + "prefix":"192.168.201.0", + "prefixLen":24, + "network":"192.168.201.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"65200", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.201.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.202.0/24": [ + { + "pathFrom":"internal", + "prefix":"192.168.202.0", + "prefixLen":24, + "network":"192.168.202.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"65200", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.201.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_features/r1/show_bgp_metric_test.json b/tests/topotests/bgp_features/r1/show_bgp_metric_test.json new file mode 100644 index 0000000..1720572 --- /dev/null +++ b/tests/topotests/bgp_features/r1/show_bgp_metric_test.json @@ -0,0 +1,57 @@ +{ + "routerId": "192.168.0.1", + "routes": { + "192.168.1.0/24": [ + { + "valid":true, + "prefix":"192.168.1.0", + "prefixLen":24, + "metric":11, + "nexthops":[ + { + "ip":"192.168.0.2", + "used":true + } + ] + }, + { + "valid":true, + "prefix":"192.168.1.0", + "prefixLen":24, + "metric":0, + "nexthops":[ + { + "ip":"0.0.0.0", + "used":true + } + ] + } +],"192.168.101.0/24": [ + { + "prefix":"192.168.101.0", + "prefixLen":24, + "metric":111, + "peerId":"192.168.101.2" + } +],"192.168.102.0/24": [ + { + "prefix":"192.168.102.0", + "prefixLen":24, + "metric":0, + "peerId":"192.168.101.2" + } +],"192.168.201.0/24": [ + { + "prefix":"192.168.201.0", + "prefixLen":24, + "metric":210, + "peerId":"192.168.0.2" + } +],"192.168.202.0/24": [ + { + "prefix":"192.168.202.0", + "prefixLen":24, + "metric":11, + "peerId":"192.168.0.2" + } +] } } diff --git a/tests/topotests/bgp_features/r1/zebra.conf b/tests/topotests/bgp_features/r1/zebra.conf new file mode 100644 index 0000000..a4e51fd --- /dev/null +++ b/tests/topotests/bgp_features/r1/zebra.conf @@ -0,0 +1,29 @@ +! +hostname r1 +log file zebra.log +! +interface lo + ip address 192.168.0.1/32 + ipv6 address fc00::1/128 +! +interface r1-eth0 + description SW6 Stub Network + ip address 192.168.6.1/24 + ipv6 address fc00:0:0:6::1/64 +! +interface r1-eth1 + description SW0 R1-R2 OSPF & BGP Network + ip address 192.168.1.1/24 + ipv6 address fc00:0:0:1::1/64 +! +interface r1-eth2 + description SW2 R1-R3 OSPF Network + ip address 192.168.3.1/24 + ipv6 address fc00:0:0:3::1/64 +! +interface r1-eth3 + description SW4 R1-R4 eBGP Network + ip address 192.168.101.1/24 + ipv6 address fc00:100:0:1::1/64 +! +no ip nht resolve-via-default diff --git a/tests/topotests/bgp_features/r2/bgp_delayopen_neighbor.json b/tests/topotests/bgp_features/r2/bgp_delayopen_neighbor.json new file mode 100644 index 0000000..a74da03 --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_delayopen_neighbor.json @@ -0,0 +1,6 @@ +{ + "192.168.201.2":{ + "remoteAs":65200, + "bgpTimerDelayOpenTimeMsecs":60000 + } +} diff --git a/tests/topotests/bgp_features/r2/bgp_delayopen_summary_connect.json b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_connect.json new file mode 100644 index 0000000..c2b42ec --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_connect.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65000, + "peers":{ + "192.168.201.2":{ + "remoteAs":65200, + "state":"Connect" + } + } +} +} diff --git a/tests/topotests/bgp_features/r2/bgp_delayopen_summary_established.json b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_established.json new file mode 100644 index 0000000..77b6944 --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_established.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65000, + "peers":{ + "192.168.201.2":{ + "remoteAs":65200, + "state":"Established" + } + } +} +} diff --git a/tests/topotests/bgp_features/r2/bgp_delayopen_summary_shutdown.json b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_shutdown.json new file mode 100644 index 0000000..8f9476a --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_shutdown.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65000, + "peers":{ + "192.168.201.2":{ + "remoteAs":65200, + "state":"Idle (Admin)" + } + } +} +} diff --git a/tests/topotests/bgp_features/r2/bgp_shutdown_summary.json b/tests/topotests/bgp_features/r2/bgp_shutdown_summary.json new file mode 100644 index 0000000..b789263 --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_shutdown_summary.json @@ -0,0 +1,19 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.0.2", + "as":65000, + "vrfName":"default", + "peerCount":2, + "peers":{ + "192.168.0.1":{ + "remoteAs":65000, + "state":"Active" + }, + "192.168.201.2":{ + "remoteAs":65200, + "state":"Established" + } + }, + "totalPeers":2 +} +} diff --git a/tests/topotests/bgp_features/r2/bgp_summary.json b/tests/topotests/bgp_features/r2/bgp_summary.json new file mode 100644 index 0000000..30e0ef4 --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_summary.json @@ -0,0 +1,27 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.0.2", + "as":65000, + "vrfName":"default", + "peerCount":2, + "peers":{ + "192.168.0.1":{ + "hostname":"r1", + "remoteAs":65000, + "outq":0, + "inq":0, + "pfxRcd":8, + "state":"Established" + }, + "192.168.201.2":{ + "hostname":"r5", + "remoteAs":65200, + "outq":0, + "inq":0, + "pfxRcd":2, + "state":"Established" + } + }, + "totalPeers":2 +} +} diff --git a/tests/topotests/bgp_features/r2/bgpd.conf b/tests/topotests/bgp_features/r2/bgpd.conf new file mode 100644 index 0000000..99cec98 --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgpd.conf @@ -0,0 +1,41 @@ +! +hostname r2 +log file bgpd.log +! +router bgp 65000 + bgp router-id 192.168.0.2 + timers bgp 3 10 + coalesce-time 0 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 192.168.0.1 remote-as 65000 + neighbor 192.168.0.1 timers 3 10 + neighbor 192.168.0.1 description Router R1 (iBGP) + neighbor 192.168.0.1 update-source lo + neighbor 192.168.0.1 timers connect 5 + neighbor 192.168.201.2 remote-as 65200 + neighbor 192.168.201.2 timers 3 10 + neighbor 192.168.201.2 description Router R5 (eBGP AS 65200) + neighbor 192.168.201.2 timers connect 5 + ! + address-family ipv4 unicast + network 192.168.0.0/24 + network 192.168.1.0/24 + network 192.168.2.0/24 + network 192.168.3.0/24 + network 192.168.7.0/24 + network 192.168.8.0/24 + neighbor 192.168.201.2 route-map testmap-in in + neighbor 192.168.201.2 route-map testmap-out out + exit-address-family +! +! +! +route-map testmap-in permit 999 +! +route-map testmap-out permit 999 +! +! +line vty +! + diff --git a/tests/topotests/bgp_features/r2/ospf6d.conf b/tests/topotests/bgp_features/r2/ospf6d.conf new file mode 100644 index 0000000..7fe5356 --- /dev/null +++ b/tests/topotests/bgp_features/r2/ospf6d.conf @@ -0,0 +1,21 @@ +log file ospf6d.log +! +! debug ospf6 neighbor +! +interface r2-lo +! +interface r2-eth1 + ipv6 ospf6 priority 5 +! +interface r2-eth2 + ipv6 ospf6 priority 10 +! +router ospf6 + ospf6 router-id 192.168.0.2 + log-adjacency-changes + interface r2-lo area 0.0.0.0 + interface r2-eth1 area 0.0.0.0 + interface r2-eth2 area 0.0.0.0 +! +line vty +! diff --git a/tests/topotests/bgp_features/r2/ospf_neighbor.json b/tests/topotests/bgp_features/r2/ospf_neighbor.json new file mode 100644 index 0000000..3a168ba --- /dev/null +++ b/tests/topotests/bgp_features/r2/ospf_neighbor.json @@ -0,0 +1,16 @@ +{ + "neighbors":{ + "192.168.0.1":[ + { + "nbrPriority":10, + "converged":"Full" + } + ], + "192.168.0.3":[ + { + "nbrPriority":5, + "converged":"Full" + } + ] + } +} diff --git a/tests/topotests/bgp_features/r2/ospfd.conf b/tests/topotests/bgp_features/r2/ospfd.conf new file mode 100644 index 0000000..7f043c9 --- /dev/null +++ b/tests/topotests/bgp_features/r2/ospfd.conf @@ -0,0 +1,26 @@ +log file ospfd.log +! +! debug ospf event +! debug ospf zebra +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf priority 5 +! +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf priority 10 +! +router ospf + ospf router-id 192.168.0.2 + log-adjacency-changes + network 192.168.0.0/20 area 0.0.0.0 + timers throttle spf 0 0 0 + timers lsa min-arrival 10 + timers throttle lsa all 0 + refresh timer 10 +! +line vty +! diff --git a/tests/topotests/bgp_features/r2/show_bgp.json b/tests/topotests/bgp_features/r2/show_bgp.json new file mode 100644 index 0000000..830d5a9 --- /dev/null +++ b/tests/topotests/bgp_features/r2/show_bgp.json @@ -0,0 +1,349 @@ +{ + "vrfName": "default", + "routerId": "192.168.0.2", + "defaultLocPrf": 100, + "localAS": 65000, + "routes": { "0.0.0.0/0": [ + { + "pathFrom":"internal", + "prefix":"0.0.0.0", + "prefixLen":0, + "network":"0.0.0.0\/0", + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.0.0/24": [ + { + "pathFrom":"external", + "prefix":"192.168.0.0", + "prefixLen":24, + "network":"192.168.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.1.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.1.0", + "prefixLen":24, + "network":"192.168.1.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.1.0", + "prefixLen":24, + "network":"192.168.1.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.2.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.2.0", + "prefixLen":24, + "network":"192.168.2.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.2.0", + "prefixLen":24, + "network":"192.168.2.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.3.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.3.0", + "prefixLen":24, + "network":"192.168.3.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.3.0", + "prefixLen":24, + "network":"192.168.3.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.6.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"internal", + "prefix":"192.168.6.0", + "prefixLen":24, + "network":"192.168.6.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.7.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.7.0", + "prefixLen":24, + "network":"192.168.7.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.8.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.8.0", + "prefixLen":24, + "network":"192.168.8.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.8.0", + "prefixLen":24, + "network":"192.168.8.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.101.0/24": [ + { + "pathFrom":"internal", + "prefix":"192.168.101.0", + "prefixLen":24, + "network":"192.168.101.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.102.0/24": [ + { + "pathFrom":"internal", + "prefix":"192.168.102.0", + "prefixLen":24, + "network":"192.168.102.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.201.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.201.0", + "prefixLen":24, + "network":"192.168.201.0\/24", + "metric":0, + "weight":0, + "peerId":"192.168.201.2", + "path":"65200", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.201.2", + "hostname":"r5", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.202.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.202.0", + "prefixLen":24, + "network":"192.168.202.0\/24", + "metric":0, + "weight":0, + "peerId":"192.168.201.2", + "path":"65200", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.201.2", + "hostname":"r5", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_features/r2/show_bgp_metric_test.json b/tests/topotests/bgp_features/r2/show_bgp_metric_test.json new file mode 100644 index 0000000..960f13d --- /dev/null +++ b/tests/topotests/bgp_features/r2/show_bgp_metric_test.json @@ -0,0 +1,57 @@ +{ + "routerId": "192.168.0.2", + "routes": { + "192.168.2.0/24": [ + { + "valid":true, + "prefix":"192.168.2.0", + "prefixLen":24, + "metric":0, + "nexthops":[ + { + "ip":"192.168.0.1", + "used":true + } + ] + }, + { + "valid":true, + "prefix":"192.168.2.0", + "prefixLen":24, + "metric":0, + "nexthops":[ + { + "ip":"0.0.0.0", + "used":true + } + ] + } +],"192.168.101.0/24": [ + { + "prefix":"192.168.101.0", + "prefixLen":24, + "metric":101, + "peerId":"192.168.0.1" + } +],"192.168.102.0/24": [ + { + "prefix":"192.168.102.0", + "prefixLen":24, + "metric":0, + "peerId":"192.168.0.1" + } +],"192.168.201.0/24": [ + { + "prefix":"192.168.201.0", + "prefixLen":24, + "metric":222, + "peerId":"192.168.201.2" + } +],"192.168.202.0/24": [ + { + "prefix":"192.168.202.0", + "prefixLen":24, + "metric":0, + "peerId":"192.168.201.2" + } +] } } diff --git a/tests/topotests/bgp_features/r2/zebra.conf b/tests/topotests/bgp_features/r2/zebra.conf new file mode 100644 index 0000000..1d427da --- /dev/null +++ b/tests/topotests/bgp_features/r2/zebra.conf @@ -0,0 +1,28 @@ +! +hostname r2 +log file zebra.log +! +interface lo + ip address 192.168.0.2/32 + ipv6 address fc00::2/128 +! +interface r2-eth0 + description SW7 Stub Network + ip address 192.168.7.1/24 + ipv6 address fc00:0:0:7::1/64 +! +interface r2-eth1 + description SW0 R1-R2 OSPF & BGP Network + ip address 192.168.1.2/24 + ipv6 address fc00:0:0:1::2/64 +! +interface r2-eth2 + description SW1 R2-R3 OSPF Network + ip address 192.168.2.1/24 + ipv6 address fc00:0:0:2::1/64 +! +interface r2-eth3 + description SW5 R2-R5 eBGP Network + ip address 192.168.201.1/24 + ipv6 address fc00:200:0:1::1/64 +! diff --git a/tests/topotests/bgp_features/r3/bgp_summary.json b/tests/topotests/bgp_features/r3/bgp_summary.json new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_features/r3/ospf6d.conf b/tests/topotests/bgp_features/r3/ospf6d.conf new file mode 100644 index 0000000..07325b6 --- /dev/null +++ b/tests/topotests/bgp_features/r3/ospf6d.conf @@ -0,0 +1,21 @@ +log file ospf6d.log +! +! debug ospf6 neighbor +! +interface r3-lo +! +interface r3-eth1 + ipv6 ospf6 priority 5 +! +interface r3-eth2 + ipv6 ospf6 priority 5 +! +router ospf6 + ospf6 router-id 192.168.0.3 + log-adjacency-changes + interface r3-lo area 0.0.0.0 + interface r3-eth1 area 0.0.0.0 + interface r3-eth2 area 0.0.0.0 +! +line vty +! diff --git a/tests/topotests/bgp_features/r3/ospf_neighbor.json b/tests/topotests/bgp_features/r3/ospf_neighbor.json new file mode 100644 index 0000000..9f8c059 --- /dev/null +++ b/tests/topotests/bgp_features/r3/ospf_neighbor.json @@ -0,0 +1,16 @@ +{ + "neighbors":{ + "192.168.0.1":[ + { + "nbrPriority":10, + "converged":"Full" + } + ], + "192.168.0.2":[ + { + "nbrPriority":10, + "converged":"Full" + } + ] + } +} diff --git a/tests/topotests/bgp_features/r3/ospfd.conf b/tests/topotests/bgp_features/r3/ospfd.conf new file mode 100644 index 0000000..c3399fd --- /dev/null +++ b/tests/topotests/bgp_features/r3/ospfd.conf @@ -0,0 +1,26 @@ +log file ospfd.log +! +! debug ospf event +! debug ospf zebra +! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf priority 5 +! +int r3-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf priority 5 +! +router ospf + ospf router-id 192.168.0.3 + log-adjacency-changes + network 192.168.0.0/20 area 0.0.0.0 + timers throttle spf 0 0 0 + timers lsa min-arrival 10 + timers throttle lsa all 0 + refresh timer 10 +! +line vty +! diff --git a/tests/topotests/bgp_features/r3/zebra.conf b/tests/topotests/bgp_features/r3/zebra.conf new file mode 100644 index 0000000..62ba04d --- /dev/null +++ b/tests/topotests/bgp_features/r3/zebra.conf @@ -0,0 +1,23 @@ +! +hostname r3 +log file zebra.log +! +interface lo + ip address 192.168.0.3/32 + ipv6 address fc00::3/128 +! +interface r3-eth0 + description SW8 Stub Network + ip address 192.168.8.1/24 + ipv6 address fc00:0:0:8::1/64 +! +interface r3-eth1 + description SW1 R2-R3 OSPF Network + ip address 192.168.2.2/24 + ipv6 address fc00:0:0:2::2/64 +! +interface r3-eth2 + description SW2 R1-R3 OSPF Network + ip address 192.168.3.2/24 + ipv6 address fc00:0:0:3::2/64 +! diff --git a/tests/topotests/bgp_features/r4/bgp_delayopen_summary_established.json b/tests/topotests/bgp_features/r4/bgp_delayopen_summary_established.json new file mode 100644 index 0000000..85caf55 --- /dev/null +++ b/tests/topotests/bgp_features/r4/bgp_delayopen_summary_established.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65100, + "peers":{ + "192.168.101.1":{ + "remoteAs":65000, + "state":"Established" + } + } +} +} diff --git a/tests/topotests/bgp_features/r4/bgp_delayopen_summary_shutdown.json b/tests/topotests/bgp_features/r4/bgp_delayopen_summary_shutdown.json new file mode 100644 index 0000000..cf784d8 --- /dev/null +++ b/tests/topotests/bgp_features/r4/bgp_delayopen_summary_shutdown.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65100, + "peers":{ + "192.168.101.1":{ + "remoteAs":65000, + "state":"Idle (Admin)" + } + } +} +} diff --git a/tests/topotests/bgp_features/r4/bgp_shutdown_summary.json b/tests/topotests/bgp_features/r4/bgp_shutdown_summary.json new file mode 100644 index 0000000..ede4dd6 --- /dev/null +++ b/tests/topotests/bgp_features/r4/bgp_shutdown_summary.json @@ -0,0 +1,14 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.100.1", + "as":65100, + "vrfName":"default", + "peerCount":1, + "peers":{ + "192.168.101.1":{ + "remoteAs":65000, + "state":"Active" } + }, + "totalPeers":1 +} +} diff --git a/tests/topotests/bgp_features/r4/bgp_summary.json b/tests/topotests/bgp_features/r4/bgp_summary.json new file mode 100644 index 0000000..c0dfe78 --- /dev/null +++ b/tests/topotests/bgp_features/r4/bgp_summary.json @@ -0,0 +1,18 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.100.1", + "as":65100, + "vrfName":"default", + "peerCount":1, + "peers":{ + "192.168.101.1":{ + "hostname":"r1", + "remoteAs":65000, + "outq":0, + "inq":0, + "pfxRcd":6, + "state":"Established" } + }, + "totalPeers":1 +} +} diff --git a/tests/topotests/bgp_features/r4/bgpd.conf b/tests/topotests/bgp_features/r4/bgpd.conf new file mode 100644 index 0000000..cdf8f4e --- /dev/null +++ b/tests/topotests/bgp_features/r4/bgpd.conf @@ -0,0 +1,34 @@ +! +hostname r4 +log file bgpd.log +! +router bgp 65100 + bgp router-id 192.168.100.1 + timers bgp 3 10 + coalesce-time 0 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 192.168.101.1 remote-as 65000 + neighbor 192.168.101.1 timers 3 10 + neighbor 192.168.101.1 description Router R1 (eBGP AS 65000) + neighbor 192.168.101.1 timers connect 5 + ! + address-family ipv4 unicast + network 192.168.100.0/24 + network 192.168.101.0/24 + network 192.168.102.0/24 + neighbor 192.168.101.1 default-originate + neighbor 192.168.101.1 route-map testmap-in in + neighbor 192.168.101.1 route-map testmap-out out + exit-address-family +! +! +! +route-map testmap-in permit 999 +! +route-map testmap-out permit 999 +! +! +line vty +! + diff --git a/tests/topotests/bgp_features/r4/show_bgp_metric_test.json b/tests/topotests/bgp_features/r4/show_bgp_metric_test.json new file mode 100644 index 0000000..2329498 --- /dev/null +++ b/tests/topotests/bgp_features/r4/show_bgp_metric_test.json @@ -0,0 +1,27 @@ +{ + "routerId": "192.168.100.1", + "localAS": 65100, + "routes": { + "192.168.1.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.1.0", + "prefixLen":24, + "network":"192.168.1.0\/24", + "metric":1011, + "weight":0, + "peerId":"192.168.101.1", + "path":"65000", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_features/r4/zebra.conf b/tests/topotests/bgp_features/r4/zebra.conf new file mode 100644 index 0000000..08e3e1a --- /dev/null +++ b/tests/topotests/bgp_features/r4/zebra.conf @@ -0,0 +1,18 @@ +! +hostname r4 +log file zebra.log +! +interface lo + ip address 192.168.100.1/32 + ipv6 address fc00:100::1/128 +! +interface r4-eth0 + description SW5 Stub Network + ip address 192.168.102.1/24 + ipv6 address fc00:100:0:2::1/64 +! +interface r4-eth1 + description SW0 R1-R2 OSPF & BGP Network + ip address 192.168.101.2/24 + ipv6 address fc00:100:0:1::2/64 +! diff --git a/tests/topotests/bgp_features/r5/bgp_delayopen_neighbor.json b/tests/topotests/bgp_features/r5/bgp_delayopen_neighbor.json new file mode 100644 index 0000000..4b97254 --- /dev/null +++ b/tests/topotests/bgp_features/r5/bgp_delayopen_neighbor.json @@ -0,0 +1,6 @@ +{ + "192.168.201.1":{ + "remoteAs":65000, + "bgpTimerDelayOpenTimeMsecs":30000 + } +} diff --git a/tests/topotests/bgp_features/r5/bgp_delayopen_summary_connect.json b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_connect.json new file mode 100644 index 0000000..d7b4e77 --- /dev/null +++ b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_connect.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65200, + "peers":{ + "192.168.201.1":{ + "remoteAs":65000, + "state":"Connect" + } + } +} +} diff --git a/tests/topotests/bgp_features/r5/bgp_delayopen_summary_established.json b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_established.json new file mode 100644 index 0000000..15cfb19 --- /dev/null +++ b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_established.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65200, + "peers":{ + "192.168.201.1":{ + "remoteAs":65000, + "state":"Established" + } + } +} +} diff --git a/tests/topotests/bgp_features/r5/bgp_delayopen_summary_shutdown.json b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_shutdown.json new file mode 100644 index 0000000..94aceba --- /dev/null +++ b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_shutdown.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65200, + "peers":{ + "192.168.201.1":{ + "remoteAs":65000, + "state":"Idle (Admin)" + } + } +} +} diff --git a/tests/topotests/bgp_features/r5/bgp_summary.json b/tests/topotests/bgp_features/r5/bgp_summary.json new file mode 100644 index 0000000..b854af5 --- /dev/null +++ b/tests/topotests/bgp_features/r5/bgp_summary.json @@ -0,0 +1,19 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.200.1", + "as":65200, + "vrfName":"default", + "peerCount":1, + "peers":{ + "192.168.201.1":{ + "hostname":"r2", + "remoteAs":65000, + "outq":0, + "inq":0, + "pfxRcd":6, + "state":"Established" + } + }, + "totalPeers":1 +} +} diff --git a/tests/topotests/bgp_features/r5/bgpd.conf b/tests/topotests/bgp_features/r5/bgpd.conf new file mode 100644 index 0000000..6368fed --- /dev/null +++ b/tests/topotests/bgp_features/r5/bgpd.conf @@ -0,0 +1,34 @@ +! +hostname r5 +log file bgpd.log +! +router bgp 65200 + bgp router-id 192.168.200.1 + timers bgp 3 10 + coalesce-time 0 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 192.168.201.1 remote-as 65000 + neighbor 192.168.201.1 timers 3 10 + neighbor 192.168.201.1 description Router R2 (eBGP AS 65000) + neighbor 192.168.201.1 timers connect 5 + ! + address-family ipv4 unicast + network 192.168.200.0/24 + network 192.168.201.0/24 + network 192.168.202.0/24 + neighbor 192.168.101.1 default-originate + neighbor 192.168.201.1 route-map testmap-in in + neighbor 192.168.201.1 route-map testmap-out out + exit-address-family +! +! +! +route-map testmap-in permit 999 +! +route-map testmap-out permit 999 +! +! +line vty +! + diff --git a/tests/topotests/bgp_features/r5/show_bgp_metric_test.json b/tests/topotests/bgp_features/r5/show_bgp_metric_test.json new file mode 100644 index 0000000..e6608b4 --- /dev/null +++ b/tests/topotests/bgp_features/r5/show_bgp_metric_test.json @@ -0,0 +1,27 @@ +{ + "routerId": "192.168.200.1", + "localAS": 65200, + "routes": { + "192.168.2.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.2.0", + "prefixLen":24, + "network":"192.168.2.0\/24", + "metric":2022, + "weight":0, + "peerId":"192.168.201.1", + "path":"65000", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.201.1", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_features/r5/zebra.conf b/tests/topotests/bgp_features/r5/zebra.conf new file mode 100644 index 0000000..4d9064a --- /dev/null +++ b/tests/topotests/bgp_features/r5/zebra.conf @@ -0,0 +1,18 @@ +! +hostname r5 +log file zebra.log +! +interface lo + ip address 192.168.200.1/32 + ipv6 address fc00:200::1/128 +! +interface r5-eth0 + description SW6 Stub Network + ip address 192.168.202.1/24 + ipv6 address fc00:200:0:2::1/64 +! +interface r5-eth1 + description SW0 R1-R2 OSPF & BGP Network + ip address 192.168.201.2/24 + ipv6 address fc00:200:0:1::2/64 +! diff --git a/tests/topotests/bgp_features/test_bgp_features.dot b/tests/topotests/bgp_features/test_bgp_features.dot new file mode 100644 index 0000000..70b126c --- /dev/null +++ b/tests/topotests/bgp_features/test_bgp_features.dot @@ -0,0 +1,83 @@ +## GraphViz file for test_all_protocol_startup +## +## Color coding: +######################### +## Main FRR: #f08080 red +## No protocol: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #33ff99 light green +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +## LDP IPv4 #fedbe2 light pink +##### Colors (see http://www.color-hex.com/) + +graph test_all_protocol_startup { + overlap=false; + constraint=false; + + // title + labelloc="t"; + label="Test Topologoy BGP Features"; + rankdir = TB; + + ###################### + # Routers + ###################### + + # Main FRR Router with all protocols + R4 [shape=doubleoctagon, label="R4 FRR\nAS 65100\nlo: 192.168.100.1/32\nfc00:100::1/128", fillcolor="#f08080", style=filled]; + R5 [shape=doubleoctagon, label="R5 FRR\nAS 65200\nlo: 192.168.200.1/32\nfc00:200::1/128", fillcolor="#f08080", style=filled]; + #{ rank = same {R4, R5}} + + R1 [shape=doubleoctagon, label="R1 FRR\nAS 65000\nlo: 192.168.0.1/32\nfc00::1/128", fillcolor="#f08080", style=filled]; + R2 [shape=doubleoctagon, label="R2 FRR\nAS 65000\nlo: 192.168.0.1/32\nfc00::1/128", fillcolor="#f08080", style=filled]; + #{ rank = same { R1, R2}} + + R3 [shape=doubleoctagon, label="R3 FRR\nAS 65000\nlo: 192.168.0.1/32\nfc00::1/128", fillcolor="#f08080", style=filled]; + + ###################### + # Network Lists + ###################### + + SW1_R1_R2 [label="SW1 OSPF & iBGP\n192.168.1.0/24\nfc00:0:0:1::/64", fillcolor="#32b835", style=filled]; + SW2_R2_R3 [label="SW2 OSPF\n192.168.2.0/24\nfc00:0:0:2::/64", fillcolor="#19e3d9", style=filled]; + SW3_R3_R1 [label="SW3 OSPF\n192.168.3.0/24\nfc00:0:0:3::/64", fillcolor="#19e3d9", style=filled]; + + SW4_R4_R1 [label="SW4 eBGP\n192.168.101.0/24\nfc00:100:0:1::/64", fillcolor="#fedbe2", style=filled]; + SW5_R5_R2 [label="SW5 eBGP\n192.168.201.0/24\nfc00:200:0:1::/64", fillcolor="#fedbe2", style=filled]; + #{ rank = same {SW4_R4_R1, SW5_R5_R2}} + + SW6_STUB_R1 [label="SW6\n192.168.6.0/24\nfc00:0:0:6::/64", fillcolor="#d0e0d0", style=filled]; + SW7_STUB_R2 [label="SW7\n192.168.7.0/24\nfc00:0:0:7::/64", fillcolor="#d0e0d0", style=filled]; + SW8_STUB_R3 [label="SW8\n192.168.8.0/24\nfc00:0:0:8::/64", fillcolor="#d0e0d0", style=filled]; + SW9_STUB_R4 [label="SW9\n192.168.102.0/24\nfc00:100:0:2::/64", fillcolor="#d0e0d0", style=filled]; + SW10_STUB_R5 [label="SW10\n192.168.202.0/24\nfc00:200:0:2::/64", fillcolor="#d0e0d0", style=filled]; + + + ###################### + # Network Connections + ###################### + + R1 -- SW6_STUB_R1 [label = "eth0\n.1\n::1"]; + R2 -- SW7_STUB_R2 [label = "eth0\n.1\n::1"]; + R3 -- SW8_STUB_R3 [label = "eth0\n.1\n::1"]; + R4 -- SW9_STUB_R4 [label = "eth0\n.1\n::1"]; + R5 -- SW10_STUB_R5 [label = "eth0\n.1\n::1"]; + + R1 -- SW1_R1_R2 [label = "eth1\n.1\n::1"]; + R1 -- SW3_R3_R1 [label = "eth2\n.1\n::1"]; + R2 -- SW1_R1_R2 [label = "eth1\n.2\n::2"]; + R2 -- SW2_R2_R3 [label = "eth2\n.1\n::1"]; + R3 -- SW2_R2_R3 [label = "eth1\n.2\n::2"]; + R3 -- SW3_R3_R1 [label = "eth2\n.2\n::2"]; + + R1 -- SW4_R4_R1 [label = "eth3\n.1\n::1"]; + R2 -- SW5_R5_R2 [label = "eth3\n.1\n::1"]; + R4 -- SW4_R4_R1 [label = "eth1\n.2\n::2"]; + R5 -- SW5_R5_R2 [label = "eth1\n.2\n::2"]; + +} diff --git a/tests/topotests/bgp_features/test_bgp_features.pdf b/tests/topotests/bgp_features/test_bgp_features.pdf new file mode 100644 index 0000000..cb52a54 Binary files /dev/null and b/tests/topotests/bgp_features/test_bgp_features.pdf differ diff --git a/tests/topotests/bgp_features/test_bgp_features.py b/tests/topotests/bgp_features/test_bgp_features.py new file mode 100644 index 0000000..43f4905 --- /dev/null +++ b/tests/topotests/bgp_features/test_bgp_features.py @@ -0,0 +1,1102 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_features.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_bgp_features.py: Test various BGP features. +""" + +import json +import functools +import os +import sys +import pytest +import re +import time + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd] + +##################################################### +# +# Network Topology Definition +# +##################################################### + + +def build_topo(tgen): + for rtrNum in range(1, 6): + tgen.add_router("r{}".format(rtrNum)) + + # create ExaBGP peers + for peer_num in range(1, 5): + tgen.add_exabgp_peer( + "peer{}".format(peer_num), + ip="192.168.101.{}".format(peer_num + 2), + defaultRoute="via 192.168.101.1", + ) + + # Setup Switches and connections + for swNum in range(1, 11): + tgen.add_switch("sw{}".format(swNum)) + + # Add connections to stub switches + tgen.gears["r1"].add_link(tgen.gears["sw6"]) + tgen.gears["r2"].add_link(tgen.gears["sw7"]) + tgen.gears["r3"].add_link(tgen.gears["sw8"]) + tgen.gears["r4"].add_link(tgen.gears["sw9"]) + tgen.gears["r5"].add_link(tgen.gears["sw10"]) + + # Add connections to R1-R2-R3 core + tgen.gears["r1"].add_link(tgen.gears["sw1"]) + tgen.gears["r1"].add_link(tgen.gears["sw3"]) + tgen.gears["r2"].add_link(tgen.gears["sw1"]) + tgen.gears["r2"].add_link(tgen.gears["sw2"]) + tgen.gears["r3"].add_link(tgen.gears["sw2"]) + tgen.gears["r3"].add_link(tgen.gears["sw3"]) + + # Add connections to external R4/R5 Routers + tgen.gears["r1"].add_link(tgen.gears["sw4"]) + tgen.gears["r4"].add_link(tgen.gears["sw4"]) + tgen.gears["r2"].add_link(tgen.gears["sw5"]) + tgen.gears["r5"].add_link(tgen.gears["sw5"]) + + # Add ExaBGP peers to sw4 + tgen.gears["peer1"].add_link(tgen.gears["sw4"]) + tgen.gears["peer2"].add_link(tgen.gears["sw4"]) + tgen.gears["peer3"].add_link(tgen.gears["sw4"]) + tgen.gears["peer4"].add_link(tgen.gears["sw4"]) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def setup_module(module): + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + # Starting Routers + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + if os.path.exists(os.path.join(CWD, "{}/bgpd.conf".format(rname))): + logger.info("{} uses BGPd".format(rname)) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + if os.path.exists(os.path.join(CWD, "{}/ospfd.conf".format(rname))): + logger.info("{} uses OSPFd".format(rname)) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + if os.path.exists(os.path.join(CWD, "{}/ospf6d.conf".format(rname))): + logger.info("{} uses OSPF6d".format(rname)) + router.load_config( + TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname)) + ) + router.start() + + +def teardown_module(module): + tgen = get_topogen() + tgen.stop_topology() + + +def test_ospf_convergence(): + "Test for OSPFv2 topology convergence" + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check Router r1, r2 & r3 OSPF + for rtrNum in range(1, 4): + logger.info("Checking OSPFv2 convergence on router r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/ospf_neighbor.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip ospf neighbor json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "OSPF router R{} did not converge".format(rtrNum) + assert res is None, assertmsg + + +def test_bgp_convergence(): + "Test for BGP topology convergence" + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check Router r1 & r2 BGP + for rtrNum in [1, 2, 4, 5]: + logger.info("Checking BGP IPv4 convergence on router r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/bgp_summary.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "BGP router R{} did not converge".format(rtrNum) + assert res is None, assertmsg + + # tgen.mininet_cli() + + +def get_shut_msg_count(tgen): + shuts = {} + for rtrNum in [2, 4]: + shutmsg = tgen.net["r{}".format(rtrNum)].cmd_nostatus( + 'grep -c "NOTIFICATION.*Cease/Administrative Shutdown" bgpd.log', warn=False + ) + try: + shuts[rtrNum] = int(shutmsg.strip()) + except ValueError: + shuts[rtrNum] = 0 + return shuts + + +def test_bgp_shutdown(): + "Test BGP instance shutdown" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + shuts_before = get_shut_msg_count(tgen) + + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "bgp shutdown message ABCDabcd"' + ) + + # Check BGP Summary on local and remote routers + for rtrNum in [1, 2, 4]: + logger.info( + "Checking BGP Summary after shutdown of R1 BGP on router r{}".format(rtrNum) + ) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/bgp_shutdown_summary.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "BGP sessions on router R{} are in incorrect state (not down as expected?)".format( + rtrNum + ) + assert res is None, assertmsg + + shuts_after = get_shut_msg_count(tgen) + + for k in shuts_before: + assert shuts_before[k] + 1 == shuts_after[k] + + +def test_bgp_shutdown_message(): + "Test BGP Peer Shutdown Message" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rtrNum in [2, 4]: + logger.info("Checking BGP shutdown received on router r{}".format(rtrNum)) + + shut_message = tgen.net["r{}".format(rtrNum)].cmd( + 'grep -e "NOTIFICATION.*Cease/Administrative Shutdown.*ABCDabcd" bgpd.log' + ) + assertmsg = "BGP shutdown message not received on router R{}".format(rtrNum) + assert shut_message != "", assertmsg + + +def test_bgp_no_shutdown(): + "Test BGP instance no shutdown" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "no bgp shutdown"') + + # Check BGP Summary on local and remote routers + for rtrNum in [1, 2, 4]: + logger.info( + "Checking BGP Summary after removing bgp shutdown on router r1 on router r{}".format( + rtrNum + ) + ) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/bgp_summary.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "BGP sessions on router R{} are in incorrect state (not down as expected?)".format( + rtrNum + ) + assert res is None, assertmsg + + +def test_bgp_metric_config(): + "Test BGP Changing metric values in route-maps" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Configuring bgp route-maps on router r1 and r2 to update metric") + + # # Adding the following configuration to r1: + # router bgp 65000 + # address-family ipv4 unicast + # neighbor 192.168.0.2 route-map addmetric-in in + # neighbor 192.168.0.2 route-map addmetric-out out + # neighbor 192.168.101.2 route-map setmetric-in in + # neighbor 192.168.101.2 route-map setmetric-out out + # exit-address-family + # ! + # ip prefix-list net1 seq 10 permit 192.168.101.0/24 + # ip prefix-list net2 seq 20 permit 192.168.1.0/24 + # ! + # route-map setmetric-in permit 10 + # match ip address prefix-list net1 + # set metric 111 + # ! + # route-map setmetric-in permit 20 + # ! + # route-map setmetric-out permit 10 + # match ip address prefix-list net2 + # set metric 1011 + # ! + # route-map setmetric-out permit 20 + # ! + # route-map addmetric-in permit 10 + # set metric +11 + # ! + # route-map addmetric-out permit 10 + # set metric +12 + # ! + + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" ' + + '-c "address-family ipv4 unicast" ' + + '-c "neighbor 192.168.0.2 route-map addmetric-in in" ' + + '-c "neighbor 192.168.0.2 route-map addmetric-out out" ' + + '-c "neighbor 192.168.101.2 route-map setmetric-in in" ' + + '-c "neighbor 192.168.101.2 route-map setmetric-out out" ' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" ' + + '-c "ip prefix-list net1 seq 10 permit 192.168.101.0/24" ' + + '-c "ip prefix-list net2 seq 20 permit 192.168.1.0/24"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" ' + + '-c "route-map setmetric-in permit 10" ' + + '-c "match ip address prefix-list net1" ' + + '-c "set metric 111" ' + + '-c "route-map setmetric-in permit 20"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" ' + + '-c "route-map setmetric-out permit 10" ' + + '-c "match ip address prefix-list net2" ' + + '-c "set metric 1011" ' + + '-c "route-map setmetric-out permit 20"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" ' + + '-c "route-map addmetric-in permit 10" ' + + '-c "set metric +11"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" ' + + '-c "route-map addmetric-out permit 10" ' + + '-c "set metric +12"' + ) + + # # Adding the following configuration to r2: + # router bgp 65000 + # address-family ipv4 unicast + # neighbor 192.168.0.1 route-map subtractmetric-in in + # neighbor 192.168.0.1 route-map subtractmetric-out out + # neighbor 192.168.201.2 route-map setmetric-in in + # neighbor 192.168.201.2 route-map setmetric-out out + # exit-address-family + # ! + # ip prefix-list net1 seq 10 permit 192.168.201.0/24 + # ip prefix-list net2 seq 20 permit 192.168.2.0/24 + # ! + # route-map setmetric-in permit 10 + # match ip address prefix-list net1 + # set metric 222 + # ! + # route-map setmetric-in permit 20 + # ! + # route-map setmetric-out permit 10 + # match ip address prefix-list net2 + # set metric 2022 + # ! + # route-map setmetric-out permit 20 + # ! + # route-map subtractmetric-in permit 10 + # set metric -22 + # ! + # route-map subtractmetric-out permit 10 + # set metric -23 + # ! + + tgen.net["r2"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" ' + + '-c "address-family ipv4 unicast" ' + + '-c "neighbor 192.168.0.1 route-map subtractmetric-in in" ' + + '-c "neighbor 192.168.0.1 route-map subtractmetric-out out" ' + + '-c "neighbor 192.168.201.2 route-map setmetric-in in" ' + + '-c "neighbor 192.168.201.2 route-map setmetric-out out" ' + ) + tgen.net["r2"].cmd( + 'vtysh -c "conf t" ' + + '-c "ip prefix-list net1 seq 10 permit 192.168.201.0/24" ' + + '-c "ip prefix-list net2 seq 20 permit 192.168.2.0/24" ' + ) + tgen.net["r2"].cmd( + 'vtysh -c "conf t" ' + + '-c "route-map setmetric-in permit 10" ' + + '-c "match ip address prefix-list net1" ' + + '-c "set metric 222" ' + + '-c "route-map setmetric-in permit 20"' + ) + tgen.net["r2"].cmd( + 'vtysh -c "conf t" ' + + '-c "route-map setmetric-out permit 10" ' + + '-c "match ip address prefix-list net2" ' + + '-c "set metric 2022" ' + + '-c "route-map setmetric-out permit 20"' + ) + tgen.net["r2"].cmd( + 'vtysh -c "conf t" ' + + '-c "route-map subtractmetric-in permit 10" ' + + '-c "set metric -22"' + ) + tgen.net["r2"].cmd( + 'vtysh -c "conf t" ' + + '-c "route-map subtractmetric-out permit 10" ' + + '-c "set metric -23"' + ) + + # Clear IN the bgp neighbors to make sure the route-maps are applied + tgen.net["r1"].cmd( + 'vtysh -c "clear ip bgp 192.168.0.2 in" ' + '-c "clear ip bgp 192.168.101.2 in"' + ) + tgen.net["r2"].cmd( + 'vtysh -c "clear ip bgp 192.168.0.1 in" ' + '-c "clear ip bgp 192.168.201.2 in"' + ) + + # tgen.mininet_cli() + + # Checking BGP config - should show the bgp metric settings in the route-maps + logger.info("Checking BGP configuration for correct 'set metric' values") + + setmetric111 = ( + tgen.net["r1"].cmd('vtysh -c "show running" | grep "^ set metric 111"').rstrip() + ) + assertmsg = ( + "'set metric 111' configuration applied to R1, but not visible in configuration" + ) + assert setmetric111 == " set metric 111", assertmsg + + setmetric222 = ( + tgen.net["r2"].cmd('vtysh -c "show running" | grep "^ set metric 222"').rstrip() + ) + assertmsg = ( + "'set metric 222' configuration applied to R2, but not visible in configuration" + ) + assert setmetric222 == " set metric 222", assertmsg + + +def test_bgp_metric_add_config(): + "Test BGP Changing metric values in route-maps" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking BGP configuration for correct 'set metric' ADD value") + + setmetricP11 = ( + tgen.net["r1"].cmd('vtysh -c "show running" | grep "^ set metric +11"').rstrip() + ) + assertmsg = ( + "'set metric +11' configuration applied to R1, but not visible in configuration" + ) + assert setmetricP11 == " set metric +11", assertmsg + + +def test_bgp_metric_subtract_config(): + "Test BGP Changing metric values in route-maps" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking BGP configuration for correct 'set metric' SUBTRACT value") + + setmetricM22 = ( + tgen.net["r2"].cmd('vtysh -c "show running" | grep "^ set metric -22"').rstrip() + ) + assertmsg = ( + "'set metric -22' configuration applied to R2, but not visible in configuration" + ) + assert setmetricM22 == " set metric -22", assertmsg + + +def test_bgp_set_metric(): + "Test setting metrics" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Test absolute metric") + + # Check BGP Summary on local and remote routers + for rtrNum in [1, 2, 4, 5]: + logger.info("Checking metrics of BGP router on r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/show_bgp_metric_test.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "BGP metrics on router r{} wrong".format(rtrNum) + assert res is None, assertmsg + + +def test_bgp_remove_metric_rmaps(): + "Test removing route-maps with metric changes again" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Test absolute metric") + + # Remove metric route-maps and relevant comfiguration + + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" ' + + '-c "address-family ipv4 unicast" ' + + '-c "no neighbor 192.168.0.2 route-map addmetric-in in" ' + + '-c "no neighbor 192.168.0.2 route-map addmetric-out out" ' + + '-c "no neighbor 192.168.101.2 route-map setmetric-in in" ' + + '-c "no neighbor 192.168.101.2 route-map setmetric-out out" ' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" ' + + '-c "no ip prefix-list net1" ' + + '-c "no ip prefix-list net2"' + ) + tgen.net["r1"].cmd('vtysh -c "conf t" ' + '-c "no route-map setmetric-in" ') + tgen.net["r1"].cmd('vtysh -c "conf t" ' + '-c "no route-map setmetric-out" ') + tgen.net["r1"].cmd('vtysh -c "conf t" ' + '-c "no route-map addmetric-in" ') + tgen.net["r1"].cmd('vtysh -c "conf t" ' + '-c "no route-map addmetric-out" ') + + tgen.net["r2"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" ' + + '-c "address-family ipv4 unicast" ' + + '-c "no neighbor 192.168.0.1 route-map subtractmetric-in in" ' + + '-c "no neighbor 192.168.0.1 route-map subtractmetric-out out" ' + + '-c "no neighbor 192.168.201.2 route-map setmetric-in in" ' + + '-c "no neighbor 192.168.201.2 route-map setmetric-out out" ' + ) + tgen.net["r2"].cmd( + 'vtysh -c "conf t" ' + + '-c "no ip prefix-list net1" ' + + '-c "no ip prefix-list net2" ' + ) + tgen.net["r2"].cmd('vtysh -c "conf t" ' + '-c "no route-map setmetric-in" ') + tgen.net["r2"].cmd('vtysh -c "conf t" ' + '-c "no route-map setmetric-out" ') + tgen.net["r2"].cmd('vtysh -c "conf t" ' + '-c "no route-map addmetric-in" ') + tgen.net["r2"].cmd('vtysh -c "conf t" ' + '-c "no route-map addmetric-out" ') + + # Clear IN the bgp neighbors to make sure the route-maps are applied + tgen.net["r1"].cmd( + 'vtysh -c "clear ip bgp 192.168.0.2 in" ' + '-c "clear ip bgp 192.168.101.2 in"' + ) + tgen.net["r2"].cmd( + 'vtysh -c "clear ip bgp 192.168.0.1 in" ' + '-c "clear ip bgp 192.168.201.2 in"' + ) + + # tgen.mininet_cli() + + # Check BGP Summary on local and remote routers + for rtrNum in [1, 2]: + logger.info("Checking metrics of BGP router on r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/show_bgp.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "BGP routes on router r{} are wrong after removing metric route-maps".format( + rtrNum + ) + assert res is None, assertmsg + + +def test_bgp_norib(): + "Test BGP disable RIB (Zebra) Route Install" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Configuring 'bgp no-rib' on router r1") + + tgen.net["r1"].cmd('vtysh -c "conf t" -c "bgp no-rib"') + + # Checking BGP config - should show the "bgp no-rib" under the router bgp section + logger.info("Checking BGP configuration for 'bgp no-rib'") + + norib_cfg = ( + tgen.net["r1"].cmd('vtysh -c "show running bgpd" | grep "^bgp no-rib"').rstrip() + ) + + assertmsg = "'bgp no-rib' configuration applied, but not visible in configuration" + assert norib_cfg == "bgp no-rib", assertmsg + + +def test_bgp_norib_routes(): + "Test Routes in Zebra and BGP with the 'bgp-norib' configuration" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Checking local BGP routes - they need to be gone from Zebra + logger.info("Checking Zebra routes after removing bgp shutdown on router r1") + + router = tgen.gears["r1"] + reffile = os.path.join(CWD, "r1/ip_route_norib.json") + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip route json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=2) + assertmsg = "Zebra IPv4 Routes after configuring 'bgp no-rib' (There should be no BGP routes in Zebra anymore)" + assert res is None, assertmsg + + # Check BGP Summary on local and remote routers + for rtrNum in [1, 2, 4]: + logger.info( + "Checking BGP Summary after 'bgp no-rib' on router r1 on router r{}".format( + rtrNum + ) + ) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/bgp_summary.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=2) + assertmsg = "BGP sessions on router R{} has incorrect routes after adding 'bgp no-rib on r1'".format( + rtrNum + ) + assert res is None, assertmsg + + # tgen.mininet_cli() + + +def test_bgp_disable_norib(): + "Test BGP disabling the no-RIB (Zebra) Route Install" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Configuring 'no bgp no-rib' on router r1") + + tgen.net["r1"].cmd('vtysh -c "conf t" -c "no bgp no-rib"') + + # Checking BGP config - should show the "bgp no-rib" under the router bgp section + logger.info("Checking BGP configuration for 'bgp no-rib'") + + norib_cfg = ( + tgen.net["r1"] + .cmd('vtysh -c "show running bgpd" | grep "^ bgp no-rib"') + .rstrip() + ) + + assertmsg = ( + "'no bgp no-rib'configuration applied, but still visible in configuration" + ) + assert norib_cfg == "", assertmsg + + +def test_bgp_disable_norib_routes(): + "Test Routes in Zebra and BGP with the 'bgp-norib' configuration" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Checking local BGP routes - they need to be gone from Zebra + logger.info("Checking Zebra routes after removing bgp shutdown on router r1") + + router = tgen.gears["r1"] + reffile = os.path.join(CWD, "r1/ip_route.json") + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip route json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=2) + assertmsg = "Zebra IPv4 Routes wrong after removing the 'bgp no-rib'" + assert res is None, assertmsg + + # Check BGP Summary on local and remote routers + for rtrNum in [1, 2, 4]: + logger.info( + "Checking BGP Summary after removing the 'bgp no-rib' on router r1 on router r{}".format( + rtrNum + ) + ) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/bgp_summary.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=2) + assertmsg = "BGP sessions on router R{} has incorrect routes after removing 'bgp no-rib on r1'".format( + rtrNum + ) + assert res is None, assertmsg + + # tgen.mininet_cli() + + +def test_bgp_delayopen_without(): + "Optional test of BGP functionality and behaviour without DelayOpenTimer enabled to establish a reference for following tests" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # part 1: no delay r1 <=> no delay r4 + logger.info( + "Starting optional test of BGP functionality without DelayOpenTimer enabled to establish a reference for following tests" + ) + + # 1.1 enable peering shutdown + logger.info("Enable shutdown of peering between r1 and r4") + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 shutdown"' + ) + tgen.net["r4"].cmd( + 'vtysh -c "conf t" -c "router bgp 65100" -c "neighbor 192.168.101.1 shutdown"' + ) + + # 1.2 wait for peers to shut down (poll output) + for router_num in [1, 4]: + logger.info( + "Checking BGP summary after enabling shutdown of peering on r{}".format( + router_num + ) + ) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_summary_shutdown.json".format(router_num) + ) + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=5, wait=1) + assertmsg = "BGP session on r{} did not shut down peer".format(router_num) + assert res is None, assertmsg + + # 1.3 disable peering shutdown + logger.info("Disable shutdown of peering between r1 and r4") + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 shutdown"' + ) + tgen.net["r4"].cmd( + 'vtysh -c "conf t" -c "router bgp 65100" -c "no neighbor 192.168.101.1 shutdown"' + ) + + # 1.4 wait for peers to establish connection (poll output) + for router_num in [1, 4]: + logger.info( + "Checking BGP summary after disabling shutdown of peering on r{}".format( + router_num + ) + ) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_summary_established.json".format(router_num) + ) + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=5, wait=1) + assertmsg = ( + "BGP session on r{} did not establish a connection with peer".format( + router_num + ) + ) + assert res is None, assertmsg + + # tgen.mininet_cli() + + # end test_bgp_delayopen_without + + +def test_bgp_delayopen_singular(): + "Test of BGP functionality and behaviour with DelayOpenTimer enabled on one side of the peering" + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # part 2: delay 240s r1 <=> no delay r4 + logger.info( + "Starting test of BGP functionality and behaviour with DelayOpenTimer enabled on one side of the peering" + ) + + # 2.1 enable peering shutdown + logger.info("Enable shutdown of peering between r1 and r4") + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 shutdown"' + ) + tgen.net["r4"].cmd( + 'vtysh -c "conf t" -c "router bgp 65100" -c "neighbor 192.168.101.1 shutdown"' + ) + + # 2.2 wait for peers to shut down (poll output) + for router_num in [1, 4]: + logger.info( + "Checking BGP summary after disabling shutdown of peering on r{}".format( + router_num + ) + ) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_summary_shutdown.json".format(router_num) + ) + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=5, wait=1) + assertmsg = "BGP session on r{} did not shut down peer".format(router_num) + assert res is None, assertmsg + + # 2.3 set delayopen on R1 to 240 + logger.info("Setting DelayOpenTime for neighbor r4 to 240 seconds on r1") + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 timers delayopen 240"' + ) + + # 2.4 check config (poll output) + logger.info("Checking BGP neighbor configuration after setting DelayOpenTime on r1") + router = tgen.gears["r1"] + reffile = os.path.join(CWD, "r1/bgp_delayopen_neighbor.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show bgp neighbors json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=5, wait=1) + assertmsg = "BGP session on r1 failed to set DelayOpenTime for r4" + assert res is None, assertmsg + + # 2.5 disable peering shutdown + logger.info("Disable shutdown of peering between r1 and r4") + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 shutdown"' + ) + tgen.net["r4"].cmd( + 'vtysh -c "conf t" -c "router bgp 65100" -c "no neighbor 192.168.101.1 shutdown"' + ) + + # 2.6 wait for peers to establish connection (poll output) + for router_num in [1, 4]: + logger.info( + "Checking BGP summary after disabling shutdown of peering on r{}".format( + router_num + ) + ) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_summary_established.json".format(router_num) + ) + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=5, wait=1) + assertmsg = ( + "BGP session on r{} did not establish a connection with peer".format( + router_num + ) + ) + assert res is None, assertmsg + + # 2.7 unset delayopen on R1 + logger.info("Disabling DelayOpenTimer for neighbor r4 on r1") + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 timers delayopen"' + ) + + # 2.8 check config (poll output) + logger.info( + "Checking BGP neighbor configuration after disabling DelayOpenTimer on r1" + ) + delayopen_cfg = ( + tgen.net["r1"] + .cmd('vtysh -c "show bgp neighbors json" | grep "DelayOpenTimeMsecs"') + .rstrip() + ) + assertmsg = "BGP session on r1 failed disable DelayOpenTimer for peer r4" + assert delayopen_cfg == "", assertmsg + + # tgen.mininet_cli() + + # end test_bgp_delayopen_singular + + +def test_bgp_delayopen_dual(): + "Test of BGP functionality and behaviour with DelayOpenTimer enabled on both sides of the peering with different timer intervals" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # part 3: delay 60s R2 <=> delay 30s R5 + logger.info( + "Starting test of BGP functionality and behaviour with DelayOpenTimer enabled on both sides of the peering with different timer intervals" + ) + + # 3.1 enable peering shutdown + logger.info("Enable shutdown of peering between r2 and r5") + tgen.net["r2"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.201.2 shutdown"' + ) + tgen.net["r5"].cmd( + 'vtysh -c "conf t" -c "router bgp 65200" -c "neighbor 192.168.201.1 shutdown"' + ) + + # 3.2 wait for peers to shut down (pool output) + for router_num in [2, 5]: + logger.info( + "Checking BGP summary after disabling shutdown of peering on r{}".format( + router_num + ) + ) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_summary_shutdown.json".format(router_num) + ) + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=5, wait=1) + assertmsg = "BGP session on r{} did not shut down peer".format(router_num) + assert res is None, assertmsg + + # 3.3 set delayopen on R2 to 60s and on R5 to 30s + logger.info("Setting DelayOpenTime for neighbor r5 to 60 seconds on r2") + tgen.net["r2"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.201.2 timers delayopen 60"' + ) + logger.info("Setting DelayOpenTime for neighbor r2 to 30 seconds on r5") + tgen.net["r5"].cmd( + 'vtysh -c "conf t" -c "router bgp 65200" -c "neighbor 192.168.201.1 timers delayopen 30"' + ) + + # 3.4 check config (poll output) + for router_num in [2, 5]: + logger.info( + "Checking BGP neighbor configuration after setting DelayOpenTime on r{}i".format( + router_num + ) + ) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_neighbor.json".format(router_num) + ) + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show bgp neighbors json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=5, wait=1) + assertmsg = "BGP session on r{} failed to set DelayOpenTime".format(router_num) + assert res is None, assertmsg + + ## 3.5 disable peering shutdown + logger.info("Disable shutdown of peering between r2 and r5") + tgen.net["r2"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.201.2 shutdown"' + ) + tgen.net["r5"].cmd( + 'vtysh -c "conf t" -c "router bgp 65200" -c "no neighbor 192.168.201.1 shutdown"' + ) + + ## 3.6 wait for peers to reach connect or active state (poll output) + delay_start = int(time.time()) + for router_num in [2, 5]: + logger.info( + "Checking BGP summary after disabling shutdown of peering on r{}".format( + router_num + ) + ) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_summary_connect.json".format(router_num) + ) + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=5, wait=1) + assertmsg = "BGP session on r{} did not enter Connect state with peer".format( + router_num + ) + assert res is None, assertmsg + + ## 3.7 wait for peers to establish connection (poll output) + for router_num in [2, 5]: + logger.info( + "Checking BGP summary after disabling shutdown of peering on r{}".format( + router_num + ) + ) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_summary_established.json".format(router_num) + ) + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=35, wait=1) + assertmsg = ( + "BGP session on r{} did not establish a connection with peer".format( + router_num + ) + ) + assert res is None, assertmsg + + delay_stop = int(time.time()) + assertmsg = "BGP peering between r2 and r5 was established before DelayOpenTimer (30sec) on r2 could expire" + assert (delay_stop - delay_start) >= 30, assertmsg + + # 3.8 unset delayopen on R2 and R5 + logger.info("Disabling DelayOpenTimer for neighbor r5 on r2") + tgen.net["r2"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.201.2 timers delayopen"' + ) + logger.info("Disabling DelayOpenTimer for neighbor r2 on r5") + tgen.net["r5"].cmd( + 'vtysh -c "conf t" -c "router bgp 65200" -c "no neighbor 192.168.201.1 timers delayopen"' + ) + + # 3.9 check config (poll output) + for router_num in [2, 5]: + logger.info( + "Checking BGP neighbor configuration after disabling DelayOpenTimer on r{}".format( + router_num + ) + ) + delayopen_cfg = ( + tgen.net["r{}".format(router_num)] + .cmd('vtysh -c "show bgp neighbors json" | grep "DelayOpenTimeMsecs"') + .rstrip() + ) + assertmsg = "BGP session on r{} failed disable DelayOpenTimer".format( + router_num + ) + assert delayopen_cfg == "", assertmsg + + # tgen.mininet_cli() + + # end test_bgp_delayopen_dual + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_flowspec/__init__.py b/tests/topotests/bgp_flowspec/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_flowspec/exabgp.env b/tests/topotests/bgp_flowspec/exabgp.env new file mode 100644 index 0000000..a328e04 --- /dev/null +++ b/tests/topotests/bgp_flowspec/exabgp.env @@ -0,0 +1,54 @@ + +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' +##daemonize = false + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_flowspec/peer1/exabgp.cfg b/tests/topotests/bgp_flowspec/peer1/exabgp.cfg new file mode 100644 index 0000000..383a95b --- /dev/null +++ b/tests/topotests/bgp_flowspec/peer1/exabgp.cfg @@ -0,0 +1,33 @@ +neighbor 10.0.1.1 { +router-id 10.0.1.101; +hold-time 10; +local-address 10.0.1.101; +local-as 100; +peer-as 100; +flow { +route { +match { +source 1.1.1.2/32; +destination 3.3.3.3/32; +packet-length <200; +} +then { +redirect 50.0.0.2; +rate-limit 55; +} +} +#end route 1 +route { +match { +source 1::2/128/0; +destination 3::3/128/0; +packet-length <200; +} +then { +redirect 50::2; +rate-limit 55; +} +} +#end route 2 +} +} diff --git a/tests/topotests/bgp_flowspec/r1/bgpd.conf b/tests/topotests/bgp_flowspec/r1/bgpd.conf new file mode 100644 index 0000000..4b7a20f --- /dev/null +++ b/tests/topotests/bgp_flowspec/r1/bgpd.conf @@ -0,0 +1,19 @@ +! +hostname r1 +password zebra +log stdout debugging +router bgp 100 + bgp router-id 10.0.1.1 + neighbor 10.0.1.101 remote-as 100 + neighbor 10.0.1.101 timers 3 10 + neighbor 10.0.1.101 update-source 10.0.1.1 + address-family ipv6 flowspec + local-install r1-eth0 + neighbor 10.0.1.101 activate + exit-address-family + address-family ipv4 flowspec + local-install r1-eth0 + neighbor 10.0.1.101 activate + exit-address-family + ! +! diff --git a/tests/topotests/bgp_flowspec/r1/summary.txt b/tests/topotests/bgp_flowspec/r1/summary.txt new file mode 100644 index 0000000..82426f3 --- /dev/null +++ b/tests/topotests/bgp_flowspec/r1/summary.txt @@ -0,0 +1,50 @@ +{ +"ipv4Unicast":{ + "routerId":"10.0.1.1", + "as":100, + "vrfName":"default", + "peerCount":1, + "peers":{ + "10.0.1.101":{ + "outq":0, + "inq":0, + "pfxRcd":0, + "pfxSnt":0, + "state":"Established" + } + }, + "totalPeers":1 +}, +"ipv4Flowspec":{ + "routerId":"10.0.1.1", + "as":100, + "vrfName":"default", + "peerCount":1, + "peers":{ + "10.0.1.101":{ + "outq":0, + "inq":0, + "pfxRcd":1, + "pfxSnt":0, + "state":"Established" + } + }, + "totalPeers":1 +}, +"ipv6Flowspec":{ + "routerId":"10.0.1.1", + "as":100, + "vrfName":"default", + "peerCount":1, + "peers":{ + "10.0.1.101":{ + "outq":0, + "inq":0, + "pfxRcd":1, + "pfxSnt":0, + "state":"Established" + } + }, + "totalPeers":1 +} +} diff --git a/tests/topotests/bgp_flowspec/r1/zebra.conf b/tests/topotests/bgp_flowspec/r1/zebra.conf new file mode 100644 index 0000000..4b103cb --- /dev/null +++ b/tests/topotests/bgp_flowspec/r1/zebra.conf @@ -0,0 +1,9 @@ +! +hostname r1 +password zebra +ip table range 500 600 +interface r1-eth0 + ip address 10.0.1.1/24 + ipv6 address 1001::1/112 +! + diff --git a/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py new file mode 100644 index 0000000..a2be859 --- /dev/null +++ b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_flowspec_topo.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# + +""" +test_bgp_flowspec_topo.py: Test BGP topology with Flowspec EBGP peering + + + +------+------+ + | peer1 | + | BGP peer 1 | + |192.168.0.161| + | | + +------+------+ + .2 | r1-eth0 + | + ~~~~~~~~~ + +---~~ s1 ~~------+ + ~~ ~~ + ~~~~~~~~~ + | 10.0.1.1 r1-eth0 + | 1001::1 r1-eth0 + +--------+--------+ + | r1 | + |BGP 192.168.0.162| + | | + | | + | | + +-----------------+ + +""" + +import json +import functools +import os +import sys +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import generate_support_bundle + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +def build_topo(tgen): + tgen.add_router("r1") + + # Setup Control Path Switch 1. r1-eth0 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + + ## Add eBGP ExaBGP neighbors + peer_ip = "10.0.1.101" ## peer + peer_route = "via 10.0.1.1" ## router + peer = tgen.add_exabgp_peer("peer1", ip=peer_ip, defaultRoute=peer_route) + switch.add_link(peer) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + tgen = Topogen(build_topo, module.__name__) + + tgen.start_topology() + # check for zebra capability + router = tgen.gears["r1"] + + # Get r1 reference and run Daemons + logger.info("Launching BGP and ZEBRA on r1") + router = tgen.gears["r1"] + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format("r1")) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format("r1")) + ) + router.start() + + peer_list = tgen.exabgp_peers() + for pname, peer in peer_list.items(): + peer_dir = os.path.join(CWD, pname) + env_file = os.path.join(CWD, "exabgp.env") + peer.start(peer_dir, env_file) + logger.info(pname) + + +def teardown_module(module): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_convergence(): + "Test for BGP topology convergence" + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bgp convergence") + + # Expected result + router = tgen.gears["r1"] + reffile = os.path.join(CWD, "r1/summary.txt") + + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=210, wait=1) + assertmsg = "BGP router network did not converge" + if res is not None: + generate_support_bundle() + assert res is None, assertmsg + generate_support_bundle() + + +def test_bgp_flowspec(): + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + + logger.info("Check BGP FS entry for 3.3.3.3 with redirect IP") + output = router.vtysh_cmd( + "show bgp ipv4 flowspec 3.3.3.3", isjson=False, daemon="bgpd" + ) + logger.info(output) + if ( + "NH 50.0.0.2" not in output + or "FS:redirect IP" not in output + or "Packet Length < 200" not in output + ): + assertmsg = "traffic to 3.3.3.3 should have been detected as FS entry. NOK" + assert 0, assertmsg + else: + logger.info("Check BGP FS entry for 3.3.3.3 with redirect IP OK") + + logger.info("Check BGP FS entry for 3::3 with redirect IP") + output = router.vtysh_cmd( + "show bgp ipv6 flowspec 3::3", isjson=False, daemon="bgpd" + ) + logger.info(output) + if ( + "NH 50::2" not in output + or "FS:redirect IP" not in output + or "Packet Length < 200" not in output + ): + assertmsg = "traffic to 3::3 should have been detected as FS entry. NOK" + assert 0, assertmsg + else: + logger.info("Check BGP FS entry for 3::3 with redirect IP OK") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + ret = pytest.main(args) + + sys.exit(ret) diff --git a/tests/topotests/bgp_gr_functionality_topo1/bgp_gr_topojson_topo1.json b/tests/topotests/bgp_gr_functionality_topo1/bgp_gr_topojson_topo1.json new file mode 100644 index 0000000..7b3cac8 --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo1/bgp_gr_topojson_topo1.json @@ -0,0 +1,115 @@ +{ + "ipv4base":"192.168.0.0", + "ipv4mask":24, + "ipv6base":"fd00::", + "ipv6mask":64, + "link_ip_start":{"ipv4":"192.168.0.0", "v4mask":24, "ipv6":"fd00::", "v6mask":64}, + "lo_prefix":{"ipv4":"1.0.", "v4mask":32, "ipv6":"2001:DB8:F::", "v6mask":128}, + "routers":{ + "r1":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2-link1": {"ipv4":"auto", "ipv6":"auto"}, + "r2-link2": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + } + } + } + } + } + } + } + }, + "static_routes":[ + { + "network":"100.0.10.1/32", + "no_of_ip":5, + "next_hop":"192.168.1.2" + }, + { + "network":"1::1/128", + "no_of_ip":5, + "next_hop":"fd00:0:0:1::2" + }]}, + "r2":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4":"auto", "ipv6":"auto"}, + "r1-link2": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + } + } + } + } + } + } + } + }, + "static_routes":[ + { + "network":"200.0.20.1/32", + "no_of_ip":5, + "next_hop":"192.168.1.1" + }, + { + "network":"2::1/128", + "no_of_ip":5, + "next_hop":"fd00:0:0:1::1" + }] + } + } +} diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-1.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-1.py new file mode 100644 index 0000000..5e2b2f3 --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-1.py @@ -0,0 +1,1527 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# + +""" +Following tests are covered to test BGP Graceful Restart functionality. +Basic Common Test steps for all the test case below : +- Create topology (setup module) + Creating 2 routers topology, r1, r2 in IBGP +- Bring up topology +- Verify for bgp to converge +- Configure BGP Garceful Restart on both the routers. + +1. Transition from Peer-level helper to Global Restarting +2. Transition from Peer-level helper to Global inherit helper +3. Transition from Peer-level restarting to Global inherit helper +4. Default GR functional mode is Helper. +5. Verify that the restarting node sets "R" bit while sending the + BGP open messages after the node restart, only if GR is enabled. +6. Verify if restarting node resets R bit in BGP open message + during normal BGP session flaps as well, even when GR restarting + mode is enabled. Here link flap happen due to interface UP/DOWN. +7. Verify if restarting node resets R bit in BGP + open message during normal BGP session flaps when GR is disabled. +8. Verify that restarting nodes set "F" bit while sending + the BGP open messages after it restarts, only when BGP GR is enabled. +9. Verify that only GR helper routers keep the stale + route entries, not any GR disabled router. +10. Verify that GR helper routers keeps all the routes received + from restarting node if both the routers are configured as + GR restarting node. +11. Verify that GR helper routers delete all the routes + received from a node if both the routers are configured as GR + helper node. +12. After BGP neighborship is established and GR capability is exchanged, + transition restarting router to disabled state and vice versa. +13. After BGP neighborship is established and GR capability is exchanged, + transition restarting router to disabled state and vice versa. +14. Verify that restarting nodes reset "F" bit while sending + the BGP open messages after it's restarts, when BGP GR is **NOT** enabled. +15. Verify that only GR helper routers keep the stale + route entries, not any GR disabled router. +16. Transition from Global Restarting to Disable and then Global + Disable to Restarting. +17. Transition from Global Helper to Disable and then Global + Disable to Helper. +18. Transition from Global Restart to Helper and then Global + Helper to Restart, Global Mode : GR Restarting + PerPeer Mode : GR Helper + GR Mode effective : GR Helper +19. Transition from Peer-level helper to Global Restarting, + Global Mode : GR Restarting + PerPeer Mode : GR Restarting + GR Mode effective : GR Restarting +20. Transition from Peer-level restart to Global Restart + Global Mode : GR Restarting + PerPeer Mode : GR Restarting + GR Mode effective : GR Restarting +21. Transition from Peer-level disabled to Global Restart + Global Mode : GR Restarting + PerPeer Mode : GR Disabled + GR Mode effective : GR Disabled +22. Peer-level inherit from Global Restarting + Global Mode : GR Restart + PerPeer Mode : None + GR Mode effective : GR Restart +23. Transition from Peer-level disable to Global inherit helper + Global Mode : None + PerPeer Mode : GR Disable + GR Mode effective : GR Disable + +These tests have been broken up into 4 sub python scripts because +the totality of this run was fairly significant. +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join("../")) +sys.path.append(os.path.join("../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +# Import topoJson from lib, to create topology and initial configuration +from lib.topojson import build_config_from_json +from lib.bgp import ( + clear_bgp, + verify_bgp_rib, + verify_graceful_restart, + create_router_bgp, + verify_r_bit, + verify_f_bit, + verify_bgp_convergence, + verify_bgp_convergence_from_running_config, +) + +from lib.common_config import ( + write_test_header, + reset_config_on_routers, + start_topology, + kill_router_daemons, + start_router_daemons, + verify_rib, + check_address_types, + write_test_footer, + check_router_status, + shutdown_bringup_interface, + step, + get_frr_ipv6_linklocal, + required_linux_kernel_version, +) + +pytestmark = [pytest.mark.bgpd] + + +# Global variables +NEXT_HOP_IP = {"ipv4": "192.168.1.10", "ipv6": "fd00:0:0:1::10"} +NEXT_HOP_IP_1 = {"ipv4": "192.168.0.1", "ipv6": "fd00::1"} +NEXT_HOP_IP_2 = {"ipv4": "192.168.0.2", "ipv6": "fd00::2"} +BGP_CONVERGENCE = False +GR_RESTART_TIMER = 20 +PREFERRED_NEXT_HOP = "link_local" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global ADDR_TYPES + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.16") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_gr_topojson_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + This function groups the repetitive function calls into one function. + """ + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][peer]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, dut, neighbor=neighbor) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][dut]["links"]["r2-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, peer, neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + return True + + +def next_hop_per_address_family( + tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP +): + """ + This function returns link_local or global next_hop per address-family + """ + + intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in preferred_next_hop: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +def BGP_GR_TC_50_p1(request): + """ + Test Objective : Transition from Peer-level helper to Global inherit helper + Global Mode : None + PerPeer Mode : Helper + GR Mode effective : GR Helper + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step( + "Configure R1 as GR helper node at per Peer-level for R2" + " and configure R2 as global restarting node." + ) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a helper node") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Bring up BGP on R2 and remove Peer-level GR config from R1 ") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": False} + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, "r1", neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify on R2 that R1 still advertises GR capabilities as a helper node") + + input_dict = { + "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}}, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Start BGP on R2") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_51_p1(request): + """ + Test Objective : Transition from Peer-level restarting to Global inherit helper + Global Mode : None + PerPeer Mode : GR Restart + GR Mode effective : GR Restart + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Configure R1 as GR restarting node at per Peer-level for R2") + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + step("Verify on R2 that R1 advertises GR capabilities as a restarting node") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, "r2", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step( + "Verify that R1 keeps the stale entries in FIB & R2 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, "r2", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Bring up BGP on R1 and remove Peer-level GR config") + + start_router_daemons(tgen, "r1", ["bgpd"]) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": False} + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, "r1", neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify on R2 that R1 advertises GR capabilities as a helper node") + + input_dict = { + "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}}, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Kill BGPd on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Start BGP on R2") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_53_p1(request): + """ + Test Objective : Default GR functional mode is Helper. + Global Mode : None + PerPeer Mode : None + GR Mode effective : GR Helper + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("configure R2 as global restarting node") + + input_dict = {"r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}} + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step( + "Verify on R2 that R1 advertises GR capabilities as a helper node based on inherit" + ) + + input_dict = { + "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}}, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Kill BGPd on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Start BGP on R2") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_4_p0(request): + """ + Test Objective : Verify that the restarting node sets "R" bit while sending the + BGP open messages after the node restart, only if GR is enabled. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Phase 1] : Test Setup" " [Restart Mode]R1-----R2[Helper Mode] initialized " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : R2 goes for reload ") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info( + "[Phase 3] : R2 is still down, restart time {} sec." + "So time verify the routes are present in BGP RIB and ZEBRA ".format( + GR_RESTART_TIMER + ) + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + logger.info("[Phase 5] : R2 is about to come up now ") + start_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r2") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_5_1_2_p1(request): + """ + Test Objective : Verify if restarting node resets R bit in BGP open message + during normal BGP session flaps as well, even when GR restarting mode is enabled. + Here link flap happen due to interface UP/DOWN. + + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Phase 1] : Test Setup" " [Restart Mode]R1-----R2[Restart Mode] initialized " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : Now flap the link running the BGP session ") + # Shutdown interface + intf = "r2-r1-eth0" + shutdown_bringup_interface(tgen, "r2", intf) + + # Bring up Interface + shutdown_bringup_interface(tgen, "r2", intf, ifaceaction=True) + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r2") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : Restart BGPd on router R2. ") + kill_router_daemons(tgen, "r2", ["bgpd"]) + + start_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r2") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_6_1_2_p1(request): + """ + Test Objective : Verify if restarting node resets R bit in BGP + open message during normal BGP session flaps when GR is disabled. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Phase 1] : Test Setup" "[Restart Mode]R1-----R2[Helper Mode] initialized " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 1] : Changing mode" "[Disable Mode]R1-----R2[Helper Mode]") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, "r1", neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verify GR stats + input_dict = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + }, + } + + # here the verify_graceful_restart fro the neighbor would be + # "NotReceived" as the latest GR config is not yet applied. + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : Now flap the link running the BGP session ") + # Shutdown interface + intf = "r2-r1-eth0" + shutdown_bringup_interface(tgen, "r2", intf) + + # Bring up Interface + shutdown_bringup_interface(tgen, "r2", intf, ifaceaction=True) + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: R-bit should not be set to True in r2\n" + "Found: {}".format(tc_name, result) + ) + + logger.info("Restart BGPd on R2 ") + kill_router_daemons(tgen, "r2", ["bgpd"]) + + start_router_daemons(tgen, "r2", ["bgpd"]) + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: R-bit should not be set to True in r2\n" + "Found: {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-2.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-2.py new file mode 100644 index 0000000..13c5ba5 --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-2.py @@ -0,0 +1,391 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# + +""" +Following tests are covered to test BGP Graceful Restart functionality. +Basic Common Test steps for all the test case below : +- Create topology (setup module) + Creating 2 routers topology, r1, r2 in IBGP +- Bring up topology +- Verify for bgp to converge +- Configure BGP Garceful Restart on both the routers. + +1. Transition from Peer-level helper to Global Restarting +2. Transition from Peer-level helper to Global inherit helper +3. Transition from Peer-level restarting to Global inherit helper +4. Default GR functional mode is Helper. +5. Verify that the restarting node sets "R" bit while sending the + BGP open messages after the node restart, only if GR is enabled. +6. Verify if restarting node resets R bit in BGP open message + during normal BGP session flaps as well, even when GR restarting + mode is enabled. Here link flap happen due to interface UP/DOWN. +7. Verify if restarting node resets R bit in BGP + open message during normal BGP session flaps when GR is disabled. +8. Verify that restarting nodes set "F" bit while sending + the BGP open messages after it restarts, only when BGP GR is enabled. +9. Verify that only GR helper routers keep the stale + route entries, not any GR disabled router. +10. Verify that GR helper routers keeps all the routes received + from restarting node if both the routers are configured as + GR restarting node. +11. Verify that GR helper routers delete all the routes + received from a node if both the routers are configured as GR + helper node. +12. After BGP neighborship is established and GR capability is exchanged, + transition restarting router to disabled state and vice versa. +13. After BGP neighborship is established and GR capability is exchanged, + transition restarting router to disabled state and vice versa. +14. Verify that restarting nodes reset "F" bit while sending + the BGP open messages after it's restarts, when BGP GR is **NOT** enabled. +15. Verify that only GR helper routers keep the stale + route entries, not any GR disabled router. +16. Transition from Global Restarting to Disable and then Global + Disable to Restarting. +17. Transition from Global Helper to Disable and then Global + Disable to Helper. +18. Transition from Global Restart to Helper and then Global + Helper to Restart, Global Mode : GR Restarting + PerPeer Mode : GR Helper + GR Mode effective : GR Helper +19. Transition from Peer-level helper to Global Restarting, + Global Mode : GR Restarting + PerPeer Mode : GR Restarting + GR Mode effective : GR Restarting +20. Transition from Peer-level restart to Global Restart + Global Mode : GR Restarting + PerPeer Mode : GR Restarting + GR Mode effective : GR Restarting +21. Transition from Peer-level disabled to Global Restart + Global Mode : GR Restarting + PerPeer Mode : GR Disabled + GR Mode effective : GR Disabled +22. Peer-level inherit from Global Restarting + Global Mode : GR Restart + PerPeer Mode : None + GR Mode effective : GR Restart +23. Transition from Peer-level disable to Global inherit helper + Global Mode : None + PerPeer Mode : GR Disable + GR Mode effective : GR Disable + +These tests have been broken up into 4 sub python scripts because +the totality of this run was fairly significant. +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join("../")) +sys.path.append(os.path.join("../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +# Import topoJson from lib, to create topology and initial configuration +from lib.topojson import build_config_from_json +from lib.bgp import ( + clear_bgp, + verify_bgp_rib, + verify_graceful_restart, + create_router_bgp, + verify_r_bit, + verify_f_bit, + verify_bgp_convergence, + verify_bgp_convergence_from_running_config, +) + +from lib.common_config import ( + write_test_header, + reset_config_on_routers, + start_topology, + kill_router_daemons, + start_router_daemons, + verify_rib, + check_address_types, + write_test_footer, + check_router_status, + shutdown_bringup_interface, + step, + get_frr_ipv6_linklocal, + required_linux_kernel_version, +) + +pytestmark = [pytest.mark.bgpd] + + +# Global variables +NEXT_HOP_IP = {"ipv4": "192.168.1.10", "ipv6": "fd00:0:0:1::10"} +NEXT_HOP_IP_1 = {"ipv4": "192.168.0.1", "ipv6": "fd00::1"} +NEXT_HOP_IP_2 = {"ipv4": "192.168.0.2", "ipv6": "fd00::2"} +BGP_CONVERGENCE = False +GR_RESTART_TIMER = 20 +PREFERRED_NEXT_HOP = "link_local" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global ADDR_TYPES + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.16") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_gr_topojson_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + This function groups the repetitive function calls into one function. + """ + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][peer]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, dut, neighbor=neighbor) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][dut]["links"]["r2-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, peer, neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + return True + + +def next_hop_per_address_family( + tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP +): + """ + This function returns link_local or global next_hop per address-family + """ + + intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in preferred_next_hop: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +def test_BGP_GR_TC_8_p1(request): + """ + Test Objective : Verify that restarting nodes set "F" bit while sending + the BGP open messages after it restarts, only when BGP GR is enabled. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Phase 1] : Test Setup" " [Restart Mode]R1-----R2[Restart Mode] initialized " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": {"preserve-fw-state": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + }, + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : R1 goes for reload ") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Phase 3] : R1 is about to come up now ") + start_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r2", peer="r1") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_f_bit(tgen, topo, addr_type, input_dict, dut="r2", peer="r1") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py new file mode 100644 index 0000000..1a8f830 --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py @@ -0,0 +1,2720 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# + +""" +Following tests are covered to test BGP Graceful Restart functionality. +Basic Common Test steps for all the test case below : +- Create topology (setup module) + Creating 2 routers topology, r1, r2 in IBGP +- Bring up topology +- Verify for bgp to converge +- Configure BGP Garceful Restart on both the routers. + +1. Transition from Peer-level helper to Global Restarting +2. Transition from Peer-level helper to Global inherit helper +3. Transition from Peer-level restarting to Global inherit helper +4. Default GR functional mode is Helper. +5. Verify that the restarting node sets "R" bit while sending the + BGP open messages after the node restart, only if GR is enabled. +6. Verify if restarting node resets R bit in BGP open message + during normal BGP session flaps as well, even when GR restarting + mode is enabled. Here link flap happen due to interface UP/DOWN. +7. Verify if restarting node resets R bit in BGP + open message during normal BGP session flaps when GR is disabled. +8. Verify that restarting nodes set "F" bit while sending + the BGP open messages after it restarts, only when BGP GR is enabled. +9. Verify that only GR helper routers keep the stale + route entries, not any GR disabled router. +10. Verify that GR helper routers keeps all the routes received + from restarting node if both the routers are configured as + GR restarting node. +11. Verify that GR helper routers delete all the routes + received from a node if both the routers are configured as GR + helper node. +12. After BGP neighborship is established and GR capability is exchanged, + transition restarting router to disabled state and vice versa. +13. After BGP neighborship is established and GR capability is exchanged, + transition restarting router to disabled state and vice versa. +14. Verify that restarting nodes reset "F" bit while sending + the BGP open messages after it's restarts, when BGP GR is **NOT** enabled. +15. Verify that only GR helper routers keep the stale + route entries, not any GR disabled router. +16. Transition from Global Restarting to Disable and then Global + Disable to Restarting. +17. Transition from Global Helper to Disable and then Global + Disable to Helper. +18. Transition from Global Restart to Helper and then Global + Helper to Restart, Global Mode : GR Restarting + PerPeer Mode : GR Helper + GR Mode effective : GR Helper +19. Transition from Peer-level helper to Global Restarting, + Global Mode : GR Restarting + PerPeer Mode : GR Restarting + GR Mode effective : GR Restarting +20. Transition from Peer-level restart to Global Restart + Global Mode : GR Restarting + PerPeer Mode : GR Restarting + GR Mode effective : GR Restarting +21. Transition from Peer-level disabled to Global Restart + Global Mode : GR Restarting + PerPeer Mode : GR Disabled + GR Mode effective : GR Disabled +22. Peer-level inherit from Global Restarting + Global Mode : GR Restart + PerPeer Mode : None + GR Mode effective : GR Restart +23. Transition from Peer-level disable to Global inherit helper + Global Mode : None + PerPeer Mode : GR Disable + GR Mode effective : GR Disable + +These tests have been broken up into 4 sub python scripts because +the totality of this run was fairly significant. +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join("../")) +sys.path.append(os.path.join("../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +# Import topoJson from lib, to create topology and initial configuration +from lib.topojson import build_config_from_json +from lib.bgp import ( + clear_bgp, + verify_bgp_rib, + verify_graceful_restart, + create_router_bgp, + verify_r_bit, + verify_f_bit, + verify_bgp_convergence, + verify_bgp_convergence_from_running_config, +) + +from lib.common_config import ( + write_test_header, + reset_config_on_routers, + start_topology, + kill_router_daemons, + start_router_daemons, + verify_rib, + check_address_types, + write_test_footer, + check_router_status, + shutdown_bringup_interface, + step, + get_frr_ipv6_linklocal, + required_linux_kernel_version, +) + +pytestmark = [pytest.mark.bgpd] + + +# Global variables +NEXT_HOP_IP = {"ipv4": "192.168.1.10", "ipv6": "fd00:0:0:1::10"} +NEXT_HOP_IP_1 = {"ipv4": "192.168.0.1", "ipv6": "fd00::1"} +NEXT_HOP_IP_2 = {"ipv4": "192.168.0.2", "ipv6": "fd00::2"} +BGP_CONVERGENCE = False +GR_RESTART_TIMER = 20 +PREFERRED_NEXT_HOP = "link_local" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global ADDR_TYPES + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.16") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_gr_topojson_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + This function groups the repetitive function calls into one function. + """ + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][peer]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, dut, neighbor=neighbor) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][dut]["links"]["r2-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, peer, neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + return True + + +def next_hop_per_address_family( + tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP +): + """ + This function returns link_local or global next_hop per address-family + """ + + intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in preferred_next_hop: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +def BGP_GR_TC_50_p1(request): + """ + Test Objective : Transition from Peer-level helper to Global inherit helper + Global Mode : None + PerPeer Mode : Helper + GR Mode effective : GR Helper + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step( + "Configure R1 as GR helper node at per Peer-level for R2" + " and configure R2 as global restarting node." + ) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a helper node") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Bring up BGP on R2 and remove Peer-level GR config from R1 ") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": False} + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, "r1", neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify on R2 that R1 still advertises GR capabilities as a helper node") + + input_dict = { + "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}}, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Start BGP on R2") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_19_p1(request): + """ + Test Objective : Verify that GR helper routers keeps all the routes received + from restarting node if both the routers are configured as GR restarting node. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info("[Phase 1] : Test Setup [Helper]R1-----R2[Restart] initialized ") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + "preserve-fw-state": True, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + }, + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info( + "[Phase 2] : R1's Gr state cahnge to Graceful" + " Restart without resetting the session " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + logger.info( + "[Phase 3] : R2 is still down, restart time 120 sec." + " So time verify the routes are present in BGP RIB and ZEBRA " + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_20_p1(request): + """ + Test Objective : Verify that GR helper routers delete all the routes + received from a node if both the routers are configured as GR helper node. + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info("[Phase 1] : Test Setup [Helper]R1-----R2[Helper] initialized ") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + "preserve-fw-state": True, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + }, + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + logger.info("[Phase 5] : R2 is about to come up now ") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_31_1_p1(request): + """ + After BGP neighborship is established and GR capability is exchanged, + transition restarting router to disabled state and vice versa. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Phase 1] : Test Setup" " [Helper Mode]R2-----R1[Restart Mode] initialized " + ) + + # Configure graceful-restart + input_dict = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r1": { + "bgp": { + "graceful-restart": {"preserve-fw-state": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + }, + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : R1 Goes from Restart to Disable Mode ") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, "r1", neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verify GR stats + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : R1 goes for reload ") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info( + "[Phase 3] : R1 is still down, restart time 120 sec." + " So time verify the routes are not present in BGP RIB and ZEBRA" + ) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + logger.info("[Phase 4] : R1 is about to come up now ") + start_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Phase 5] : R1 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_31_2_p1(request): + """ + After BGP neighborship is established and GR capability is exchanged, + transition restarting router to disabled state and vice versa. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Phase 1] : Test Setup " "[Disable Mode]R1-----R2[Helper Mode] initialized " + ) + + # Configure graceful-restart + input_dict = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : R1 Goes from Disable to Restart Mode ") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": {"preserve-fw-state": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, "r1", neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verify GR stats + input_dict = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + } + + logger.info("[Phase 4] : R1 is UP and GR state is correct ") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 3] : R1 goes for reload ") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info( + "[Phase 4] : R1 is still down, restart time 120 sec." + " So time verify the routes are present in BGP RIB and ZEBRA " + ) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 6] : R1 is about to come up now ") + start_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Phase 4] : R1 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_9_p1(request): + """ + Test Objective : Verify that restarting nodes reset "F" bit while sending + the BGP open messages after it's restarts, when BGP GR is **NOT** enabled. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Phase 1] : Test Setup" " [Restart Mode]R1-----R2[Helper Mode] Initiliazed " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + "r2": { + "bgp": { + "graceful-restart": {"preserve-fw-state": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + }, + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info("[Phase 2] : R2 goes for reload ") + kill_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info( + "[Phase 3] : R2 is still down, restart time 120 sec." + "So time verify the routes are present in BGP RIB and ZEBRA " + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + logger.info("[Phase 5] : R2 is about to come up now ") + start_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + result = verify_bgp_convergence(tgen, topo) + assert ( + result is True + ), "BGP Convergence after BGPd restart" " :Failed \n Error:{}".format(result) + + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r2") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_f_bit( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: F-bit should not be set to True in r1\n" + "Found: {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_17_p1(request): + """ + Test Objective : Verify that only GR helper routers keep the stale + route entries, not any GR disabled router. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info("[Phase 1] : Test Setup [Disable]R1-----R2[Restart] " "Initiliazed ") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + "preserve-fw-state": True, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + }, + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info("[Phase 2] : R2 goes for reload ") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info( + "[Phase 3] : R2 is still down, restart time 120 sec." + " So time verify the routes are present in BGP RIB and ZEBRA " + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + logger.info("[Phase 5] : R2 is about to come up now ") + start_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + result = verify_bgp_convergence(tgen, topo) + assert ( + result is True + ), "BGP Convergence after BGPd restart" " :Failed \n Error:{}".format(result) + + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_r_bit( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: R-bit should not be set to True in r1\n" + "Found: {}".format(tc_name, result) + ) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_43_p1(request): + """ + Test Objective : Transition from Global Restarting to Disable + and then Global Disable to Restarting. + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Configure R1 and R2 as GR restarting node in global level") + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a restarting node") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Kill BGP on R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step( + "Verify that R1 keeps BGP routes in zebra and R2 retains" + " the stale entry for received routes from R1" + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Bring up BGPd on R1 and configure it as GR disabled node in global level") + + start_router_daemons(tgen, "r1", ["bgpd"]) + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": False, + "graceful-restart-disable": True, + } + } + } + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 doesn't advertise any GR capabilities") + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart-disable": True, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Kill BGP on R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step( + "Verify that R1 flush all BGP routes from RIB & FIB and FIB and R2" + " does not retain stale entry for received routes from R1" + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + protocol = "bgp" + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + step( + "Bring up BGPd on R1 and configure it as GR" " restarting node in global level" + ) + + start_router_daemons(tgen, "r1", ["bgpd"]) + + input_dict = {"r1": {"bgp": {"graceful-restart": {"graceful-restart": True}}}} + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a restarting node") + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step( + "Verify that R1 keeps BGP routes in zebra and R2" + " retains the stale entry for received routes from R1" + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_44_p1(request): + """ + Test Objective : Transition from Global Helper to Disable + and then Global Disable to Helper. + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step( + "Configure R2 as GR restating node in global level and" + " leave R1 without any GR related config" + ) + + input_dict = {"r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}} + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a helper node") + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart-helper": True, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r2" + peer = "r1" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step("Verify that R1 keeps stale entry for BGP routes when BGPd on R2 is down") + + for addr_type in ADDR_TYPES: + dut = "r2" + peer = "r1" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Bring up BGPd on R2 and configure R1 as GR disabled node in global level") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart-disable": True, + } + } + } + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 doesn't advertise any GR capabilities") + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart-disable": True, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r2" + peer = "r1" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step("Verify that R1 does not retain stale entry for received routes from R2") + + for addr_type in ADDR_TYPES: + dut = "r2" + peer = "r1" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + next_hop = NEXT_HOP_IP_2[addr_type] + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + step("Bring up BGPd on R2 and remove GR related config from R1 in global level") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + input_dict = { + "r1": {"bgp": {"graceful-restart": {"graceful-restart-disable": False}}} + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a helper node") + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart-helper": True, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r2" + peer = "r1" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step("Verify that R1 keeps stale entry for BGP routes when BGPd on R2 is down") + + for addr_type in ADDR_TYPES: + dut = "r2" + peer = "r1" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r2", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_45_p1(request): + """ + Test Objective : Transition from Global Restart to Helper + and then Global Helper to Restart. + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Configure R1 and R2 as GR restarting node in global level") + + input_dict = { + "r1": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a restarting node") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Kill BGP on R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step( + "Verify that R1 keeps BGP routes in zebra and R2" + " retains the stale entry for received routes from R1" + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Bring up BGPd on R1 and remove GR related config in global level") + + start_router_daemons(tgen, "r1", ["bgpd"]) + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": False, + } + } + } + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a helper node") + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart-helper": True, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r2" + peer = "r1" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step("Verify that R1 keeps stale entry for BGP routes when BGPd on R2 is down") + + for addr_type in ADDR_TYPES: + dut = "r2" + peer = "r1" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Bring up BGPd on R2 and configure R1 as GR restarting node in global level") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + } + } + } + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a restarting node") + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Kill BGP on R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step( + "Verify that R1 keeps BGP routes in zebra and R2" + " retains the stale entry for received routes from R1" + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py new file mode 100644 index 0000000..31aaa0b --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py @@ -0,0 +1,1685 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# + +""" +Following tests are covered to test BGP Graceful Restart functionality. +Basic Common Test steps for all the test case below : +- Create topology (setup module) + Creating 2 routers topology, r1, r2 in IBGP +- Bring up topology +- Verify for bgp to converge +- Configure BGP Garceful Restart on both the routers. + +1. Transition from Peer-level helper to Global Restarting +2. Transition from Peer-level helper to Global inherit helper +3. Transition from Peer-level restarting to Global inherit helper +4. Default GR functional mode is Helper. +5. Verify that the restarting node sets "R" bit while sending the + BGP open messages after the node restart, only if GR is enabled. +6. Verify if restarting node resets R bit in BGP open message + during normal BGP session flaps as well, even when GR restarting + mode is enabled. Here link flap happen due to interface UP/DOWN. +7. Verify if restarting node resets R bit in BGP + open message during normal BGP session flaps when GR is disabled. +8. Verify that restarting nodes set "F" bit while sending + the BGP open messages after it restarts, only when BGP GR is enabled. +9. Verify that only GR helper routers keep the stale + route entries, not any GR disabled router. +10. Verify that GR helper routers keeps all the routes received + from restarting node if both the routers are configured as + GR restarting node. +11. Verify that GR helper routers delete all the routes + received from a node if both the routers are configured as GR + helper node. +12. After BGP neighborship is established and GR capability is exchanged, + transition restarting router to disabled state and vice versa. +13. After BGP neighborship is established and GR capability is exchanged, + transition restarting router to disabled state and vice versa. +14. Verify that restarting nodes reset "F" bit while sending + the BGP open messages after it's restarts, when BGP GR is **NOT** enabled. +15. Verify that only GR helper routers keep the stale + route entries, not any GR disabled router. +16. Transition from Global Restarting to Disable and then Global + Disable to Restarting. +17. Transition from Global Helper to Disable and then Global + Disable to Helper. +18. Transition from Global Restart to Helper and then Global + Helper to Restart, Global Mode : GR Restarting + PerPeer Mode : GR Helper + GR Mode effective : GR Helper +19. Transition from Peer-level helper to Global Restarting, + Global Mode : GR Restarting + PerPeer Mode : GR Restarting + GR Mode effective : GR Restarting +20. Transition from Peer-level restart to Global Restart + Global Mode : GR Restarting + PerPeer Mode : GR Restarting + GR Mode effective : GR Restarting +21. Transition from Peer-level disabled to Global Restart + Global Mode : GR Restarting + PerPeer Mode : GR Disabled + GR Mode effective : GR Disabled +22. Peer-level inherit from Global Restarting + Global Mode : GR Restart + PerPeer Mode : None + GR Mode effective : GR Restart +23. Transition from Peer-level disable to Global inherit helper + Global Mode : None + PerPeer Mode : GR Disable + GR Mode effective : GR Disable + +These tests have been broken up into 4 sub python scripts because +the totality of this run was fairly significant. +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join("../")) +sys.path.append(os.path.join("../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +# Import topoJson from lib, to create topology and initial configuration +from lib.topojson import build_config_from_json +from lib.bgp import ( + clear_bgp, + verify_bgp_rib, + verify_graceful_restart, + create_router_bgp, + verify_r_bit, + verify_f_bit, + verify_bgp_convergence, + verify_bgp_convergence_from_running_config, +) + +from lib.common_config import ( + write_test_header, + reset_config_on_routers, + start_topology, + kill_router_daemons, + start_router_daemons, + verify_rib, + check_address_types, + write_test_footer, + check_router_status, + shutdown_bringup_interface, + step, + get_frr_ipv6_linklocal, + required_linux_kernel_version, +) + +pytestmark = [pytest.mark.bgpd] + + +# Global variables +NEXT_HOP_IP = {"ipv4": "192.168.1.10", "ipv6": "fd00:0:0:1::10"} +NEXT_HOP_IP_1 = {"ipv4": "192.168.0.1", "ipv6": "fd00::1"} +NEXT_HOP_IP_2 = {"ipv4": "192.168.0.2", "ipv6": "fd00::2"} +BGP_CONVERGENCE = False +GR_RESTART_TIMER = 20 +PREFERRED_NEXT_HOP = "link_local" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global ADDR_TYPES + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.16") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_gr_topojson_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + This function groups the repetitive function calls into one function. + """ + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][peer]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, dut, neighbor=neighbor) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][dut]["links"]["r2-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, peer, neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + return True + + +def next_hop_per_address_family( + tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP +): + """ + This function returns link_local or global next_hop per address-family + """ + + intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in preferred_next_hop: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +def BGP_GR_TC_50_p1(request): + """ + Test Objective : Transition from Peer-level helper to Global inherit helper + Global Mode : None + PerPeer Mode : Helper + GR Mode effective : GR Helper + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step( + "Configure R1 as GR helper node at per Peer-level for R2" + " and configure R2 as global restarting node." + ) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a helper node") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Bring up BGP on R2 and remove Peer-level GR config from R1 ") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": False} + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, "r1", neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify on R2 that R1 still advertises GR capabilities as a helper node") + + input_dict = { + "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}}, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Start BGP on R2") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_46_p1(request): + """ + Test Objective : transition from Peer-level helper to Global Restarting + Global Mode : GR Restarting + PerPeer Mode : GR Helper + GR Mode effective : GR Helper + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step( + "Configure R1 and R2 as GR restarting node in global" + " and helper in per-Peer-level" + ) + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + }, + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a restarting node") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r2" + peer = "r1" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R1 keeps the stale entries in RIB & FIB and R2 keeps stale entries in FIB using" + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + peer = "r1" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step( + "Bring up BGP on R1 and remove Peer-level GR config" + " from R1 following by a session reset" + ) + + start_router_daemons(tgen, "r2", ["bgpd"]) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": False} + } + } + } + } + }, + } + } + } + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a restarting node") + + input_dict = { + "r1": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step( + "Verify that R1 keeps the stale entries in FIB command and R2 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_47_p1(request): + """ + Test Objective : transition from Peer-level restart to Global Restart + Global Mode : GR Restarting + PerPeer Mode : GR Restarting + GR Mode effective : GR Restarting + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Configure R1 and R2 as GR restarting node in global and per-Peer-level") + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + }, + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a restarting node") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Kill BGP on R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step( + "Verify that R1 keeps the stale entries in FIB and R2 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step( + "Bring up BGP on R1 and remove Peer-level GR" + " config from R1 following by a session reset" + ) + + start_router_daemons(tgen, "r1", ["bgpd"]) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": False} + } + } + } + } + }, + } + } + } + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 still advertises GR capabilities as a restarting node") + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step( + "Verify that R1 keeps the stale entries in FIB and R2 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_48_p1(request): + """ + Test Objective : transition from Peer-level disabled to Global Restart + Global Mode : GR Restarting + PerPeer Mode : GR Disabled + GR Mode effective : GR Disabled + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step( + "Configure R1 as GR restarting node in global level and" + " GR Disabled in per-Peer-level" + ) + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + }, + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}}, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 does't advertise any GR capabilities") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Kill BGP on R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step("Verify on R2 and R1 that none of the routers keep stale entries") + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + step("Bring up BGP on R1 and remove Peer-level GR config from R1") + + start_router_daemons(tgen, "r1", ["bgpd"]) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": False} + } + } + } + } + }, + } + } + } + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a restarting node") + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step( + "Verify that R1 keeps the stale entries in FIB and R2 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_49_p1(request): + """ + Test Objective : Peer-level inherit from Global Restarting + Global Mode : GR Restart + PerPeer Mode : None + GR Mode effective : GR Restart + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Configure R1 as GR restarting node in global level") + + input_dict = { + "r1": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + "r2": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}}, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step( + "Verify that R2 receives GR restarting capabilities" + " from R1 based on inheritence" + ) + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Kill BGPd on router R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step( + "Verify that R1 keeps the stale entries in FIB and R2 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def BGP_GR_TC_52_p1(request): + """ + Test Objective : Transition from Peer-level disable to Global inherit helper + Global Mode : None + PerPeer Mode : GR Disable + GR Mode effective : GR Disable + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step( + "Configure R1 as GR disabled node at per Peer-level for R2" + " & R2 as GR restarting node" + ) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 does't advertise any GR capabilities") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r2" + peer = "r1" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R2 keeps the stale entries in FIB & R1 doesn't keep RIB & FIB entries." + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + peer = "r1" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + step("Bring up BGP on R2 and remove Peer-level GR config from R1") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": False} + } + } + } + } + }, + } + } + } + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step( + "Verify on R2 that R1 advertises GR capabilities as a helper node from global inherit" + ) + + input_dict = { + "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}}, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r2" + peer = "r1" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + peer = "r1" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gr_functionality_topo2/bgp_gr_topojson_topo2.json b/tests/topotests/bgp_gr_functionality_topo2/bgp_gr_topojson_topo2.json new file mode 100644 index 0000000..75f192d --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo2/bgp_gr_topojson_topo2.json @@ -0,0 +1,334 @@ +{ + "ipv4base":"192.168.0.0", + "ipv4mask":24, + "ipv6base":"fd00::", + "ipv6mask":64, + "link_ip_start":{"ipv4":"192.168.0.0", "v4mask":24, "ipv6":"fd00::", "v6mask":64}, + "lo_prefix":{"ipv4":"1.0.", "v4mask":32, "ipv6":"2001:DB8:F::", "v6mask":128}, + "routers":{ + "r1":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4":"auto", "ipv6":"auto"}, + "r3": {"ipv4":"auto", "ipv6":"auto"}, + "r5": {"ipv4":"auto", "ipv6":"auto"}, + "r6": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "101.0.20.1/32", + "no_of_network": 5 + } + ], + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + }, + "r5": { + "dest_link": { + "r1": {} + } + }, + "r6": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "1::1/128", + "no_of_network": 5 + } + ], + "neighbor": { + "r2": { + "dest_link": { + "r1": { + } + } + }, + "r3": { + "dest_link": { + "r1": { + } + } + }, + "r5": { + "dest_link": { + "r1": { + } + } + }, + "r6": { + "dest_link": { + "r1": { + } + } + } + } + } + } + } + } + }, + "r2":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "102.0.20.1/32", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "2::1/128", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r2": { + } + } + } + } + } + } + } + } + }, + "r3":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4":"auto", "ipv6":"auto"}, + "r4": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "103.0.20.1/32", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "3::1/128", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r3": { + } + } + }, + "r4": { + "dest_link": { + "r3": { + } + } + } + } + } + } + } + } + }, + "r4":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "104.0.20.1/32", + "no_of_network": 5 + } + ], + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "4::1/128", + "no_of_network": 5 + } + ], + "neighbor": { + "r3": { + "dest_link": { + "r4": { + } + } + } + } + } + } + } + } + }, + "r5":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as": "500", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "105.0.20.1/32", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r5": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "5::1/128", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r5": { + } + } + } + } + } + } + } + } + }, + "r6":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as": "600", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "106.0.20.1/32", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r6": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "6::1/128", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r6": { + } + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-1.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-1.py new file mode 100644 index 0000000..cb6bf56 --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-1.py @@ -0,0 +1,1497 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# + +""" +Following tests are covered to test BGP Graceful Restart functionality. +Basic Common Test steps for all the test case below : +- Create topology (setup module) + Creating 7 routers topology +- Bring up topology +- Verify for bgp to converge +- Configure BGP Graceful Restart on both the routers. + +TC_1_2: + Verify that EOR message is sent out only after initial convergence + Verify whether EOR message is received from all the peers after restart +TC_3: + Verify the selection deferral timer functionality when EOR is not sent + by the helper router +TC_11: + Verify that selection-deferral timer sets the maximum time to + avoid deadlock during which the best-path +TC_10: + Test Objective : Test GR scenarios on helper router by enabling + Graceful Restart for multiple address families. +TC_15: + Test Objective : Test GR scenarios by enabling Graceful Restart + for multiple address families.. +TC_16: + Test Objective : Verify BGP-GR feature when restarting node + is a transit router for it's iBGP peers. +TC_18: + Test Objective : Verify that GR helper router deletes stale routes + received from restarting node, if GR capability is not present in +TC_19: + Test Objective : Verify that GR routers keeps all the routes + received from restarting node if both the routers are +TC_26: + Test Objective : Test GR scenarios on helper router by enabling + Graceful Restart for multiple address families. +TC_28: + Test Objective : Verify if helper node goes down before restarting + node comes up online, helper node sets the R-bit to avoid dead-lock +TC_29: + Test Objective : Change timers on the fly, and + verify if it takes immediate effect. +TC_33: + Test Objective : Helper router receives same prefixes from two + different routers (GR-restarting and GR-disabled). Keeps the +TC_34_1: + Test Objective : Restarting node doesn't preserve forwarding + state, helper router should not keep the stale entries. +TC_34_2: + Test Objective : Restarting node doesn't preserve the forwarding + state verify the behaviour on helper node, if it still keeps the +TC_32: + Test Objective : Restarting node is connected to multiple helper + nodes, one of them doesn't send EOR to restarting router. Verify +TC_37: + Test Objective : Verify if helper node restarts before sending the + EOR message, restarting node doesn't wait until stale path timer +TC_30: + Test Objective : Restarting node removes stale routes from Zebra + after receiving an EOR from helper router. + +""" + +import os +import sys +import time +import pytest +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join("../")) +sys.path.append(os.path.join("../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +# Import topoJson from lib, to create topology and initial configuration +from lib.topojson import build_config_from_json +from lib.bgp import ( + clear_bgp, + verify_bgp_rib, + verify_graceful_restart, + create_router_bgp, + verify_r_bit, + verify_eor, + verify_f_bit, + verify_bgp_convergence, + verify_gr_address_family, + modify_bgp_config_when_bgpd_down, + verify_graceful_restart_timers, + verify_bgp_convergence_from_running_config, +) + +from lib.common_config import ( + write_test_header, + reset_config_on_routers, + start_topology, + kill_router_daemons, + start_router_daemons, + verify_rib, + check_address_types, + write_test_footer, + check_router_status, + step, + get_frr_ipv6_linklocal, + required_linux_kernel_version, +) + +pytestmark = [pytest.mark.bgpd] + + +# Global variables +BGP_CONVERGENCE = False +GR_RESTART_TIMER = 5 +GR_SELECT_DEFER_TIMER = 5 +GR_STALEPATH_TIMER = 5 +PREFERRED_NEXT_HOP = "link_local" +NEXT_HOP_4 = ["192.168.1.1", "192.168.4.2"] +NEXT_HOP_6 = ["fd00:0:0:1::1", "fd00:0:0:4::2"] + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.16") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") + + global ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_gr_topojson_topo2.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + for addr_type in ADDR_TYPES: + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + This function groups the repetitive function calls into one function. + """ + + logger.info("configure_gr_followed_by_clear: dut %s peer %s", dut, peer) + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][peer]["links"][dut][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, dut, neighbor=neighbor) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][dut]["links"][peer][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, peer, neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + return True + + +def next_hop_per_address_family(tgen, dut, peer, addr_type, next_hop_dict): + """ + This function returns link_local or global next_hop per address-family + """ + + intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +def test_BGP_GR_TC_1_2_p0(request): + """ + Verify that EOR message is sent out only after initial convergence + Verify whether EOR message is received from all the peers after restart + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "Verify EOR Sent and Received : BGP_GR_TC_1_2 >> " + "BGP GR [Helper Mode]R3-----R1[Restart Mode] " + ) + + # Configure graceful-restart + input_dict = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + "preserve-fw-state": True, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + }, + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes received from router R3 + dut = "r1" + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("R1 goes for reload") + kill_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("Starting bgpd process") + start_router_daemons(tgen, "r1", ["bgpd"]) + logger.info("R1 is UP Now") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes received from router R3 + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying EOR on restarting router + result = verify_eor(tgen, topo, addr_type, input_dict, dut="r3", peer="r1") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_3_p0(request): + """ + Verify the selection deferral timer functionality when EOR is not sent + by the helper router + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Verify route download to RIB: BGP_GR_TC_3 >> " + "BGP GR [Helper Mode]R1-----R2[Restart Mode] " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "disable-eor": True, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {"graceful-restart-helper": True} + } + } + } + } + }, + }, + } + }, + "r2": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + "preserve-fw-state": True, + "timer": {"select-defer-time": GR_SELECT_DEFER_TIMER}, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"graceful-restart": True}}} + } + } + }, + }, + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes received from router R1 + dut = "r2" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("R2 goes for reload ") + kill_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info("R2 is about to come up now") + start_router_daemons(tgen, "r2", ["bgpd"]) + logger.info("R2 is UP Now") + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes received from router R1 + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verify EOR on restarting router + result = verify_eor( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: EOR should not be set to True in r2\n" + "Found: {}".format(tc_name, result) + ) + + logger.info( + "Waiting for selection deferral timer({} sec)..".format(GR_SELECT_DEFER_TIMER) + ) + sleep(GR_SELECT_DEFER_TIMER) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + result = verify_rib(tgen, addr_type, "r2", input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_11_p0(request): + """ + Verify that selection-deferral timer sets the maximum time to + avoid deadlock during which the best-path + selection process is deferred, after a peer session was restarted + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info("Verify EOR Sent after deferral timeout : BGP_GR_TC_11") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + "select-defer-time": GR_SELECT_DEFER_TIMER, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"graceful-restart": True}}}, + "r3": {"dest_link": {"r1": {"graceful-restart": True}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"graceful-restart": True}}}, + "r3": {"dest_link": {"r1": {"graceful-restart": True}}}, + } + } + }, + }, + } + }, + "r3": { + "bgp": { + "graceful-restart": {"disable-eor": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + }, + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r1") + clear_bgp(tgen, addr_type, "r3") + + result = verify_bgp_convergence_from_running_config(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes received from router R1 + dut = "r1" + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("R1 goes for reload") + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("Starting bgpd process") + start_router_daemons(tgen, "r1", ["bgpd"]) + logger.info("R1 is UP Now") + + for addr_type in ADDR_TYPES: + # Verify EOR on restarting router + result = verify_eor( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: EOR should not be set to True in r1\n" + "Found: {}".format(tc_name, result) + ) + + logger.info( + "Waiting for selection deferral timer({} sec).. ".format( + GR_SELECT_DEFER_TIMER + 2 + ) + ) + sleep(GR_SELECT_DEFER_TIMER + 2) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes received from router R1 + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying EOR on restarting router + result = verify_eor( + tgen, topo, addr_type, input_dict, dut="r3", peer="r1", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: EOR should not be set to True in r3\n" + "Found: {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_10_p2(request): + """ + Test Objective : Test GR scenarios on helper router by enabling + Graceful Restart for multiple address families. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Test Setup: [Helper Mode]R3-----R1[Restart Mode] initialized") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "next_hop_self": True, + "graceful-restart": True, + "activate": "ipv6", + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "next_hop_self": True, + "graceful-restart": True, + "activate": "ipv4", + } + } + } + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "graceful-restart-helper": True, + "activate": "ipv6", + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "graceful-restart-helper": True, + "activate": "ipv4", + } + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + step( + "Verifying GR config and operational state for addr_type {}".format( + addr_type + ) + ) + + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r3" + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, + topo, + addr_type, + "ipv4Unicast", + dut="r1", + peer="r3", + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, + topo, + addr_type, + "ipv6Unicast", + dut="r1", + peer="r3", + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, + topo, + addr_type, + "ipv4Unicast", + dut="r3", + peer="r1", + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, + topo, + addr_type, + "ipv6Unicast", + dut="r3", + peer="r1", + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Killing bgpd on r1") + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Starting bgpd on r1") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def BGP_GR_16_p2(request): + """ + Test Objective : Verify BGP-GR feature when restarting node + is a transit router for it's iBGP peers. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Step 1] : Test Setup " "[Helper Mode]R3-----R1[Restart Mode] initialized" + ) + + # Configure graceful-restart and timers + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "graceful-restart": True, + "next_hop_self": True, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "graceful-restart": True, + "next_hop_self": True, + } + } + } + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info( + "[Step 2] : Test Setup " + "[Helper Mode]R3-----R1[Restart Mode]" + "--------R6[Helper Mode] initialized" + ) + + # Configure graceful-restart and timers + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r3" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + input_dict_2 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + input_dict_2 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + input_dict_2 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_bgp_convergence_from_running_config(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_18_p1(request): + """ + Test Objective : Verify that GR helper router deletes stale routes + received from restarting node, if GR capability is not present in + restarting node's OPEN message. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Step 1] : Test Setup " "[Helper Mode]R6-----R1[Restart Mode] initialized" + ) + + # Configure graceful-restart and timers + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r6": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r6": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + } + } + }, + "r6": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r6": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r6": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r6") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r6" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info( + "[Step 2] : Test Setup " + "[Helper Mode]R6-----R1[Restart Mode]" + "--------R2[Helper Mode] initialized" + ) + + # Configure graceful-restart and timers + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r6" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r2" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 3] : Configure R1 to prevent sending EOR") + + # Modify graceful-restart config to prevent sending EOR + input_dict_3 = {"r1": {"bgp": {"graceful-restart": {"disable-eor": True}}}} + + result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_3) + + # Modify configuration to delete routes + network = {"ipv4": "101.0.20.1/32", "ipv6": "1::1/128"} + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": network[addr_type], + "no_of_network": 5, + "delete": True, + } + ] + } + } + } + } + } + } + + result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Modify graceful-restart config + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {"graceful-restart-disable": True} + } + }, + "r6": { + "dest_link": { + "r1": {"graceful-restart-disable": True} + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {"graceful-restart-disable": True} + } + }, + "r6": { + "dest_link": { + "r1": {"graceful-restart-disable": True} + } + }, + } + } + }, + } + } + } + } + + result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + logger.info("[Step 4] : Bring up the BGPd daemon on R1 for 30" " seconds..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + dut = "r6" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying BGP RIB routes + dut = "r2" + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-2.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-2.py new file mode 100644 index 0000000..cf9a474 --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-2.py @@ -0,0 +1,1194 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# + +""" +Following tests are covered to test BGP Graceful Restart functionality. +Basic Common Test steps for all the test case below : +- Create topology (setup module) + Creating 7 routers topology +- Bring up topology +- Verify for bgp to converge +- Configure BGP Graceful Restart on both the routers. + +TC_1_2: + Verify that EOR message is sent out only after initial convergence + Verify whether EOR message is received from all the peers after restart +TC_3: + Verify the selection deferral timer functionality when EOR is not sent + by the helper router +TC_11: + Verify that selection-deferral timer sets the maximum time to + avoid deadlock during which the best-path +TC_10: + Test Objective : Test GR scenarios on helper router by enabling + Graceful Restart for multiple address families. +TC_15: + Test Objective : Test GR scenarios by enabling Graceful Restart + for multiple address families.. +TC_16: + Test Objective : Verify BGP-GR feature when restarting node + is a transit router for it's iBGP peers. +TC_18: + Test Objective : Verify that GR helper router deletes stale routes + received from restarting node, if GR capability is not present in +TC_19: + Test Objective : Verify that GR routers keeps all the routes + received from restarting node if both the routers are +TC_26: + Test Objective : Test GR scenarios on helper router by enabling + Graceful Restart for multiple address families. +TC_28: + Test Objective : Verify if helper node goes down before restarting + node comes up online, helper node sets the R-bit to avoid dead-lock +TC_29: + Test Objective : Change timers on the fly, and + verify if it takes immediate effect. +TC_33: + Test Objective : Helper router receives same prefixes from two + different routers (GR-restarting and GR-disabled). Keeps the +TC_34_1: + Test Objective : Restarting node doesn't preserve forwarding + state, helper router should not keep the stale entries. +TC_34_2: + Test Objective : Restarting node doesn't preserve the forwarding + state verify the behaviour on helper node, if it still keeps the +TC_32: + Test Objective : Restarting node is connected to multiple helper + nodes, one of them doesn't send EOR to restarting router. Verify +TC_37: + Test Objective : Verify if helper node restarts before sending the + EOR message, restarting node doesn't wait until stale path timer +TC_30: + Test Objective : Restarting node removes stale routes from Zebra + after receiving an EOR from helper router. + +""" + +import os +import sys +import time +import pytest +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join("../")) +sys.path.append(os.path.join("../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +# Import topoJson from lib, to create topology and initial configuration +from lib.topojson import build_config_from_json +from lib.bgp import ( + clear_bgp, + verify_bgp_rib, + verify_graceful_restart, + create_router_bgp, + verify_r_bit, + verify_eor, + verify_f_bit, + verify_bgp_convergence, + verify_gr_address_family, + modify_bgp_config_when_bgpd_down, + verify_graceful_restart_timers, + verify_bgp_convergence_from_running_config, +) + +from lib.common_config import ( + write_test_header, + reset_config_on_routers, + start_topology, + kill_router_daemons, + start_router_daemons, + verify_rib, + check_address_types, + write_test_footer, + check_router_status, + step, + get_frr_ipv6_linklocal, + required_linux_kernel_version, +) + +pytestmark = [pytest.mark.bgpd] + + +# Global variables +BGP_CONVERGENCE = False +GR_RESTART_TIMER = 5 +GR_SELECT_DEFER_TIMER = 5 +GR_STALEPATH_TIMER = 5 +PREFERRED_NEXT_HOP = "link_local" +NEXT_HOP_4 = ["192.168.1.1", "192.168.4.2"] +NEXT_HOP_6 = ["fd00:0:0:1::1", "fd00:0:0:4::2"] + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.16") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") + + global ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_gr_topojson_topo2.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + for addr_type in ADDR_TYPES: + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + This function groups the repetitive function calls into one function. + """ + + logger.info("configure_gr_followed_by_clear: dut %s peer %s", dut, peer) + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][peer]["links"][dut][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, dut, neighbor=neighbor) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][dut]["links"][peer][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, peer, neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + return True + + +def next_hop_per_address_family(tgen, dut, peer, addr_type, next_hop_dict): + """ + This function returns link_local or global next_hop per address-family + """ + + intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +def test_BGP_GR_26_p2(request): + """ + Test Objective : Test GR scenarios on helper router by enabling + Graceful Restart for multiple address families. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Step 1] : Test Setup " "[Helper Mode]R3-----R1[Restart Mode] initialized" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "graceful-restart": True, + "next_hop_self": True, + "activate": "ipv6", + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "graceful-restart": True, + "next_hop_self": True, + "activate": "ipv4", + } + } + } + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "graceful-restart-helper": True, + "activate": "ipv6", + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "graceful-restart-helper": True, + "activate": "ipv4", + } + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r3", peer="r1" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r3" + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, + topo, + addr_type, + "ipv4Unicast", + dut="r1", + peer="r3", + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, + topo, + addr_type, + "ipv6Unicast", + dut="r1", + peer="r3", + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, + topo, + addr_type, + "ipv4Unicast", + dut="r3", + peer="r1", + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, + topo, + addr_type, + "ipv6Unicast", + dut="r3", + peer="r1", + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_chaos_28_p1(request): + """ + Test Objective : Verify if helper node goes down before restarting + node comes up online, helper node sets the R-bit to avoid dead-lock + till SDT expiry. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "Test Case: test_BGP_GR_chaos_28 :" + "[Helper Mode]R3-----R1[Restart Mode] initialized" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 1] : Kill BGPd daemon on R1..") + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 2] : Kill BGPd daemon on R3..") + + # Kill BGPd daemon on R3 + kill_router_daemons(tgen, "r3", ["bgpd"]) + + logger.info("[Step 3] : Start BGPd daemon on R1..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 4] : Start BGPd daemon on R3..") + + # Start BGPd daemon on R3 + start_router_daemons(tgen, "r3", ["bgpd"]) + + # Verify r_bit + for addr_type in ADDR_TYPES: + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r3", peer="r1") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_chaos_29_p1(request): + """ + Test Objective : Change timers on the fly, and + verify if it takes immediate effect. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Test Case : test_BGP_GR_chaos_29" + " BGP GR [Helper Mode]R3-----R1[Restart Mode]" + " and [restart-time 150]R1 initialized" + ) + + # Configure graceful-restart and timers + input_dict = { + "r1": { + "bgp": { + "graceful-restart": {"timer": {"restart-time": GR_RESTART_TIMER}}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + }, + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verify graceful-restart timers + input_dict_2 = { + "r1": { + "bgp": { + "graceful-restart": {"timer": {"restart-time": GR_RESTART_TIMER + 5}} + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r1": { + "bgp": { + "graceful-restart": {"timer": {"restart-time": GR_RESTART_TIMER}} + } + } + } + + result = verify_graceful_restart_timers( + tgen, topo, addr_type, input_dict_2, dut="r3", peer="r1" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes before shutting down BGPd daemon + dut = "r3" + input_dict = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 2] : Kill BGPd daemon on R1..") + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 3] : Wait for {} seconds..".format(GR_RESTART_TIMER)) + + # Waiting for GR_RESTART_TIMER + sleep(GR_RESTART_TIMER) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes before shutting down BGPd daemon + input_dict = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + logger.info("[Step 4] : Start BGPd daemon on R1..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_chaos_33_p1(request): + """ + Test Objective : Helper router receives same prefixes from two + different routers (GR-restarting and GR-disabled). Keeps the + stale entry only for GR-restarting node(next-hop is correct). + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Test Case : test_BGP_GR_chaos_33 " + "BGP GR " + "[Restart Mode]R1--R3[Helper Mode]--R4[Disabled Mode]" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 2] : Advertise same networks from R1 and R4..") + + # Api call to delete advertised networks + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "200.0.20.1/32", + "no_of_network": 2, + } + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "2001::1/128", "no_of_network": 2} + ] + } + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.0.20.1/32", "no_of_network": 2} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "2001::1/128", "no_of_network": 2} + ] + } + }, + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + dut = "r3" + peer1 = "r1" + peer2 = "r4" + intf1 = topo["routers"][peer1]["links"][dut]["interface"] + intf2 = topo["routers"][peer2]["links"][dut]["interface"] + + if addr_type == "ipv4": + next_hop_4 = NEXT_HOP_4 + result = verify_rib(tgen, addr_type, dut, input_dict_2, next_hop_4) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + if addr_type == "ipv6": + if "link_local" in PREFERRED_NEXT_HOP: + next_hop1 = get_frr_ipv6_linklocal(tgen, peer1, intf=intf1) + next_hop2 = get_frr_ipv6_linklocal(tgen, peer2, intf=intf2) + + next_hop_6 = [next_hop1, next_hop2] + else: + next_hop_6 = NEXT_HOP_6 + + result = verify_rib(tgen, addr_type, dut, input_dict_2, next_hop_6) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 3] : Kill BGPd daemon on R1 and R4..") + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + # Kill BGPd daemon on R4 + kill_router_daemons(tgen, "r4", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + next_hop_6 = ["fd00:0:0:1::1"] + if addr_type == "ipv4": + next_hop_4 = NEXT_HOP_4[0] + + result = verify_rib(tgen, addr_type, dut, input_dict_2, next_hop_4) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + if addr_type == "ipv6": + if "link_local" in PREFERRED_NEXT_HOP: + next_hop_6 = get_frr_ipv6_linklocal(tgen, peer1, intf=intf1) + else: + next_hop_6 = NEXT_HOP_6[0] + + result = verify_rib(tgen, addr_type, dut, input_dict_2, next_hop_6) + + # Verifying RIB routes + if addr_type == "ipv4": + next_hop_4 = NEXT_HOP_4[1] + result = verify_rib( + tgen, addr_type, dut, input_dict_2, next_hop_4, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + if addr_type == "ipv6": + if "link_local" in PREFERRED_NEXT_HOP: + next_hop_6 = get_frr_ipv6_linklocal(tgen, peer2, intf=intf2) + else: + next_hop_6 = NEXT_HOP_6[1] + + result = verify_rib( + tgen, addr_type, dut, input_dict_2, next_hop_6, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + logger.info("[Step 4] : Start BGPd daemon on R1 and R4..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + # Start BGPd daemon on R4 + start_router_daemons(tgen, "r4", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_chaos_34_2_p1(request): + """ + Test Objective : Restarting node doesn't preserve the forwarding + state verify the behaviour on helper node, if it still keeps the + stale routes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Test Case : test_BGP_GR_chaos_34 " + "BGP GR " + "[Restart Mode]R1---R3[Helper Mode]" + ) + + logger.info("[Step 1] : Configure restarting" " router R1 to prevent ") + logger.info("[Step 2] : Reset the session" " between R1 and R3..") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": {"preserve-fw-state": True, "disable-eor": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + }, + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verify f-bit before killing BGPd daemon + result = verify_f_bit(tgen, topo, addr_type, input_dict, "r3", "r1") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes after starting BGPd daemon + dut = "r3" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 3] : Kill BGPd daemon on R1..") + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 4] : Withdraw/delete the prefixes " "originated from R1..") + + # Api call to delete advertised networks + network = {"ipv4": "101.0.20.1/32", "ipv6": "1::1/128"} + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": network[addr_type], + "no_of_network": 5, + "delete": True, + } + ] + } + } + } + } + } + } + + result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 5] : Remove the CLI from R1's config to " "set the F-bit..") + + # Modify graceful-restart config not to set f-bit + # and write to /etc/frr + input_dict_2 = {"r1": {"bgp": {"graceful-restart": {"preserve-fw-state": False}}}} + + result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + logger.info("[Step 6] : Bring up the BGPd daemon on R1 again..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verify f-bit after starting BGPd daemon + result = verify_f_bit( + tgen, topo, addr_type, input_dict, "r3", "r1", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: F-bit should not be set to True in r3\n" + "Found: {}".format(tc_name, result) + ) + + # Verifying BGP RIB routes after starting BGPd daemon + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-3.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-3.py new file mode 100644 index 0000000..4746d71 --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-3.py @@ -0,0 +1,1346 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# + +""" +Following tests are covered to test BGP Graceful Restart functionality. +Basic Common Test steps for all the test case below : +- Create topology (setup module) + Creating 7 routers topology +- Bring up topology +- Verify for bgp to converge +- Configure BGP Graceful Restart on both the routers. + +TC_1_2: + Verify that EOR message is sent out only after initial convergence + Verify whether EOR message is received from all the peers after restart +TC_3: + Verify the selection deferral timer functionality when EOR is not sent + by the helper router +TC_11: + Verify that selection-deferral timer sets the maximum time to + avoid deadlock during which the best-path +TC_10: + Test Objective : Test GR scenarios on helper router by enabling + Graceful Restart for multiple address families. +TC_15: + Test Objective : Test GR scenarios by enabling Graceful Restart + for multiple address families.. +TC_16: + Test Objective : Verify BGP-GR feature when restarting node + is a transit router for it's iBGP peers. +TC_18: + Test Objective : Verify that GR helper router deletes stale routes + received from restarting node, if GR capability is not present in +TC_19: + Test Objective : Verify that GR routers keeps all the routes + received from restarting node if both the routers are +TC_26: + Test Objective : Test GR scenarios on helper router by enabling + Graceful Restart for multiple address families. +TC_28: + Test Objective : Verify if helper node goes down before restarting + node comes up online, helper node sets the R-bit to avoid dead-lock +TC_29: + Test Objective : Change timers on the fly, and + verify if it takes immediate effect. +TC_33: + Test Objective : Helper router receives same prefixes from two + different routers (GR-restarting and GR-disabled). Keeps the +TC_34_1: + Test Objective : Restarting node doesn't preserve forwarding + state, helper router should not keep the stale entries. +TC_34_2: + Test Objective : Restarting node doesn't preserve the forwarding + state verify the behaviour on helper node, if it still keeps the +TC_32: + Test Objective : Restarting node is connected to multiple helper + nodes, one of them doesn't send EOR to restarting router. Verify +TC_37: + Test Objective : Verify if helper node restarts before sending the + EOR message, restarting node doesn't wait until stale path timer +TC_30: + Test Objective : Restarting node removes stale routes from Zebra + after receiving an EOR from helper router. + +""" + +import os +import sys +import time +import pytest +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join("../")) +sys.path.append(os.path.join("../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +# Import topoJson from lib, to create topology and initial configuration +from lib.topojson import build_config_from_json +from lib.bgp import ( + clear_bgp, + verify_bgp_rib, + verify_graceful_restart, + create_router_bgp, + verify_r_bit, + verify_eor, + verify_f_bit, + verify_bgp_convergence, + verify_gr_address_family, + modify_bgp_config_when_bgpd_down, + verify_graceful_restart_timers, + verify_bgp_convergence_from_running_config, +) + +from lib.common_config import ( + write_test_header, + reset_config_on_routers, + start_topology, + kill_router_daemons, + start_router_daemons, + verify_rib, + check_address_types, + write_test_footer, + check_router_status, + step, + get_frr_ipv6_linklocal, + required_linux_kernel_version, +) + +pytestmark = [pytest.mark.bgpd] + + +# Global variables +BGP_CONVERGENCE = False +GR_RESTART_TIMER = 5 +GR_SELECT_DEFER_TIMER = 5 +GR_STALEPATH_TIMER = 5 +PREFERRED_NEXT_HOP = "link_local" +NEXT_HOP_4 = ["192.168.1.1", "192.168.4.2"] +NEXT_HOP_6 = ["fd00:0:0:1::1", "fd00:0:0:4::2"] + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.16") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") + + global ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_gr_topojson_topo2.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + for addr_type in ADDR_TYPES: + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + This function groups the repetitive function calls into one function. + """ + + logger.info("configure_gr_followed_by_clear: dut %s peer %s", dut, peer) + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][peer]["links"][dut][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, dut, neighbor=neighbor) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][dut]["links"][peer][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, peer, neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + return True + + +def next_hop_per_address_family(tgen, dut, peer, addr_type, next_hop_dict): + """ + This function returns link_local or global next_hop per address-family + """ + + intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +def test_BGP_GR_chaos_34_1_p1(request): + """ + Test Objective : Restarting node doesn't preserve forwarding + state, helper router should not keep the stale entries. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Test Case : test_BGP_GR_chaos_31 " + "BGP GR " + "[Restart Mode]R1---R3[Helper Mode]" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "preserve-fw-state": True, + "timer": {"restart-time": GR_RESTART_TIMER}, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + }, + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes after starting BGPd daemon + dut = "r3" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info( + "[Step 1] : Remove the preserve-fw-state command" + " from restarting node R1's config" + ) + + # Configure graceful-restart to set f-bit as False + input_dict_2 = {"r1": {"bgp": {"graceful-restart": {"preserve-fw-state": False}}}} + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + logger.info("[Step 2] : Reset the session between R1 and R3..") + + # Reset sessions + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r1") + + result = verify_bgp_convergence_from_running_config(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + # Verify f-bit after starting BGPd daemon + result = verify_f_bit( + tgen, topo, addr_type, input_dict_2, "r3", "r1", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: F-bit should not be set to True in r3\n" + "Found: {}".format(tc_name, result) + ) + + logger.info("[Step 3] : Kill BGPd daemon on R1..") + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + # Waiting for GR_RESTART_TIMER + logger.info("Waiting for {} sec..".format(GR_RESTART_TIMER)) + sleep(GR_RESTART_TIMER) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + input_dict = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_chaos_32_p1(request): + """ + Test Objective : Restarting node is connected to multiple helper + nodes, one of them doesn't send EOR to restarting router. Verify + that only after SDT restarting node send EOR to all helper peers + excluding the prefixes originated by faulty router. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Test Case : test_BGP_GR_chaos_32 " + "BGP GR " + "[Restart Mode]R1---R3&R5[Helper Mode]" + ) + + logger.info( + "[Step 1] : Change the mode on R1 be a restarting" " node on global level" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": {"graceful-restart": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"next_hop_self": True}}}, + "r5": {"dest_link": {"r1": {"graceful-restart": True}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"next_hop_self": True}}}, + "r5": {"dest_link": {"r1": {"graceful-restart": True}}}, + } + } + }, + }, + } + }, + "r5": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r5": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r5": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r5") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r5" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes after starting BGPd daemon + dut = "r3" + input_dict_1 = {key: topo["routers"][key] for key in ["r5"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 2] : Kill BGPd daemon on R1..") + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 3] : Withdraw all the advertised prefixes from R5") + + # Api call to delete advertised networks + network = {"ipv4": "105.0.20.1/32", "ipv6": "5::1/128"} + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r5": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": network[addr_type], + "no_of_network": 5, + "delete": True, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info( + "[Step 4] : Stop the helper router R5 from sending EOR" " message using CLI" + ) + + # Modify graceful-restart config to prevent sending EOR + input_dict_3 = {"r5": {"bgp": {"graceful-restart": {"disable-eor": True}}}} + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + logger.info("[Step 5] : Bring up the BGPd daemon on R1..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verify EOR is disabled + result = verify_eor( + tgen, topo, addr_type, input_dict_3, dut="r5", peer="r1", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: EOR should not be set to True in r5\n" + "Found: {}".format(tc_name, result) + ) + + # Verifying BGP RIB routes after starting BGPd daemon + input_dict_1 = {key: topo["routers"][key] for key in ["r5"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_chaos_37_p1(request): + """ + Test Objective : Verify if helper node restarts before sending the + EOR message, restarting node doesn't wait until stale path timer + expiry to do the best path selection and sends an EOR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Test Case : test_BGP_GR_chaos_37 " + "BGP GR " + "[Restart Mode]R1---R3[Helper Mode]" + ) + + logger.info( + "[Step 1] : Configure restarting router R3 to prevent " "sending an EOR.." + ) + + logger.info("[Step 2] : Reset the session between R3 and R1..") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "graceful-restart": {"disable-eor": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + }, + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verify EOR is disabled + result = verify_eor( + tgen, topo, addr_type, input_dict, dut="r3", peer="r1", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: EOR should not be set to True in r3\n" + "Found: {}".format(tc_name, result) + ) + + # Verifying BGP RIB routes after starting BGPd daemon + dut = "r1" + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 3] : Kill BGPd daemon on R1..") + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 4] : Start BGPd daemon on R1..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 5] : Kill BGPd daemon on R3..") + + # Kill BGPd daemon on R3 + kill_router_daemons(tgen, "r3", ["bgpd"]) + + # Modify graceful-restart config to prevent sending EOR + input_dict_2 = {"r3": {"bgp": {"graceful-restart": {"disable-eor": True}}}} + + result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + logger.info("[Step 6] : Start BGPd daemon on R3..") + + # Start BGPd daemon on R3 + start_router_daemons(tgen, "r3", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verify r_bit + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r3") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verify EOR is send from R1 to R3 + input_dict_3 = {"r1": {"bgp": {"graceful-restart": {"disable-eor": True}}}} + + result = verify_eor( + tgen, topo, addr_type, input_dict_3, dut="r1", peer="r3", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: EOR should not be set to True in r1\n" + "Found: {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_chaos_30_p1(request): + """ + Test Objective : Restarting node removes stale routes from Zebra + after receiving an EOR from helper router. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Test Case : test_BGP_GR_chaos_30 " + "BGP GR [Helper Mode]R3-----R1[Restart Mode] " + ) + + # Configure graceful-restart and timers + input_dict = { + "r1": { + "bgp": { + "graceful-restart": {"preserve-fw-state": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + }, + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes before shutting down BGPd daemon + dut = "r1" + input_dict = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 2] : Kill BGPd daemon on R1..") + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 3] : Withdraw advertised prefixes from R3...") + + # Api call to delete advertised networks + network = {"ipv4": "103.0.20.1/32", "ipv6": "3::1/128"} + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": network[addr_type], + "no_of_network": 5, + "delete": True, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info("[Step 4] : Start BGPd daemon on R1..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes before shutting down BGPd daemon + input_dict = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_15_p2(request): + """ + Test Objective : Test GR scenarios by enabling Graceful Restart + for multiple address families.. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r6": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r6": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + } + } + }, + "r6": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r6": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r6": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r6") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r6" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info( + "[Step 2] : Test Setup " + "[Helper Mode]R6-----R1[Restart Mode]" + "--------R2[Helper Mode] Initialized" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r6" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r6" + input_dict_2 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + dut = "r6" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r6" + input_dict_2 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r6" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r6" + input_dict_2 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def BGP_GR_TC_7_p1(request): + """ + Verify that BGP restarting node deletes all the routes received from peer + if BGP Graceful capability is not present in BGP Open message from the + peer + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Verify route download to RIB: BGP_GR_TC_7 >> " + "BGP GR [Helper Mode]R3-----R1[Restart Mode] " + ) + + # Configure graceful-restart + input_dict = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + "preserve-fw-state": True, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + }, + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes received from router R1 + dut = "r1" + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info("R1 goes for reload") + kill_router_daemons(tgen, "r1", ["bgpd"]) + + # Change the configuration on router R1 + input_dict_2 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Change the configuration on R1 + network = {"ipv4": "103.0.20.1/32", "ipv6": "3::1/128"} + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": network[addr_type], + "no_of_network": 5, + "delete": True, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info("R1 is about to come up now") + start_router_daemons(tgen, "r1", ["bgpd"]) + logger.info("R1 is UP Now") + + # Wait for RIB stale timeout + logger.info("Verify routes are not present" "in restart router") + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + dut = "r1" + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-4.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-4.py new file mode 100644 index 0000000..1c41df9 --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-4.py @@ -0,0 +1,1009 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# + +""" +Following tests are covered to test BGP Graceful Restart functionality. +Basic Common Test steps for all the test case below : +- Create topology (setup module) + Creating 7 routers topology +- Bring up topology +- Verify for bgp to converge +- Configure BGP Graceful Restart on both the routers. + +TC_1_2: + Verify that EOR message is sent out only after initial convergence + Verify whether EOR message is received from all the peers after restart +TC_3: + Verify the selection deferral timer functionality when EOR is not sent + by the helper router +TC_11: + Verify that selection-deferral timer sets the maximum time to + avoid deadlock during which the best-path +TC_10: + Test Objective : Test GR scenarios on helper router by enabling + Graceful Restart for multiple address families. +TC_15: + Test Objective : Test GR scenarios by enabling Graceful Restart + for multiple address families.. +TC_16: + Test Objective : Verify BGP-GR feature when restarting node + is a transit router for it's iBGP peers. +TC_18: + Test Objective : Verify that GR helper router deletes stale routes + received from restarting node, if GR capability is not present in +TC_19: + Test Objective : Verify that GR routers keeps all the routes + received from restarting node if both the routers are +TC_26: + Test Objective : Test GR scenarios on helper router by enabling + Graceful Restart for multiple address families. +TC_28: + Test Objective : Verify if helper node goes down before restarting + node comes up online, helper node sets the R-bit to avoid dead-lock +TC_29: + Test Objective : Change timers on the fly, and + verify if it takes immediate effect. +TC_33: + Test Objective : Helper router receives same prefixes from two + different routers (GR-restarting and GR-disabled). Keeps the +TC_34_1: + Test Objective : Restarting node doesn't preserve forwarding + state, helper router should not keep the stale entries. +TC_34_2: + Test Objective : Restarting node doesn't preserve the forwarding + state verify the behaviour on helper node, if it still keeps the +TC_32: + Test Objective : Restarting node is connected to multiple helper + nodes, one of them doesn't send EOR to restarting router. Verify +TC_37: + Test Objective : Verify if helper node restarts before sending the + EOR message, restarting node doesn't wait until stale path timer +TC_30: + Test Objective : Restarting node removes stale routes from Zebra + after receiving an EOR from helper router. + +These tests have been broken up into 4 sub python scripts because +the totality of run time for this script was greater than 10 minutes +""" + +import os +import sys +import time +import pytest +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join("../")) +sys.path.append(os.path.join("../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +# Import topoJson from lib, to create topology and initial configuration +from lib.topojson import build_config_from_json +from lib.bgp import ( + clear_bgp, + verify_bgp_rib, + verify_graceful_restart, + create_router_bgp, + verify_r_bit, + verify_eor, + verify_f_bit, + verify_bgp_convergence, + verify_gr_address_family, + modify_bgp_config_when_bgpd_down, + verify_graceful_restart_timers, + verify_bgp_convergence_from_running_config, +) + +from lib.common_config import ( + write_test_header, + reset_config_on_routers, + start_topology, + kill_router_daemons, + start_router_daemons, + verify_rib, + check_address_types, + write_test_footer, + check_router_status, + step, + get_frr_ipv6_linklocal, + required_linux_kernel_version, +) + +pytestmark = [pytest.mark.bgpd] + + +# Global variables +BGP_CONVERGENCE = False +GR_RESTART_TIMER = 5 +GR_SELECT_DEFER_TIMER = 5 +GR_STALEPATH_TIMER = 5 +PREFERRED_NEXT_HOP = "link_local" +NEXT_HOP_4 = ["192.168.1.1", "192.168.4.2"] +NEXT_HOP_6 = ["fd00:0:0:1::1", "fd00:0:0:4::2"] + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.16") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") + + global ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_gr_topojson_topo2.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + for addr_type in ADDR_TYPES: + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + This function groups the repetitive function calls into one function. + """ + + logger.info("configure_gr_followed_by_clear: dut %s peer %s", dut, peer) + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][peer]["links"][dut][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, dut, neighbor=neighbor) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"][dut]["links"][peer][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, peer, neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + return True + + +def next_hop_per_address_family(tgen, dut, peer, addr_type, next_hop_dict): + """ + This function returns link_local or global next_hop per address-family + """ + + intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +def test_BGP_GR_TC_23_p1(request): + """ + Verify that helper routers are deleting stale routes after stale route + timer's expiry. If all the routes are not received from restating node + after restart. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "Verify Stale Routes are deleted on helper: BGP_GR_TC_23 >> " + "BGP GR [Helper Mode]R1-----R2[Restart Mode] " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": {"timer": {"stalepath-time": GR_STALEPATH_TIMER}}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {"graceful-restart-helper": True} + } + } + } + } + }, + }, + } + }, + "r2": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + "preserve-fw-state": True, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"graceful-restart": True}}} + } + } + }, + }, + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes received from router R1 + dut = "r1" + input_dict_1 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info("R2 goes for reload") + kill_router_daemons(tgen, "r2", ["bgpd"]) + + # Modify configuration to delete routes and include disable-eor + input_dict_3 = {"r2": {"bgp": {"graceful-restart": {"disable-eor": True}}}} + + result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_3) + + # Modify configuration to delete routes and include disable-eor + network = {"ipv4": "102.0.20.1/32", "ipv6": "2::1/128"} + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": network[addr_type], + "no_of_network": 3, + "delete": True, + } + ] + } + } + } + } + } + } + + result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_3) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info("BGPd comes up for r2") + start_router_daemons(tgen, "r2", ["bgpd"]) + + # Wait for stalepath timer + logger.info("Waiting for stalepath timer({} sec..)".format(GR_STALEPATH_TIMER)) + sleep(GR_STALEPATH_TIMER) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r2") + + # Verifying RIB routes + dut = "r1" + network = {"ipv4": "102.0.20.4/32", "ipv6": "2::4/128"} + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + {"network": network[addr_type], "no_of_network": 2} + ] + } + } + } + } + } + } + + # Verify EOR on helper router + result = verify_eor( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: EOR should not be set to True in r2\n" + "Found: {}".format(tc_name, result) + ) + + # Verifying BGP RIB routes received from router R1 + dut = "r1" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_BGP_GR_20_p1(request): + """ + Test Objective : Verify that GR routers delete all the routes + received from a node if both the routers are configured as GR + helper node + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Step 1] : Test Setup " "[Restart Mode]R3-----R1[Restart Mode] Initialized" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r3" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + dut = "r3" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r3" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_BGP_GR_21_p2(request): + """ + Test Objective : VVerify BGP-GR feature when helper node is + a transit router for it's eBGP peers. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Step 1] : Test Setup " "[Helper Mode]R6-----R1[Restart Mode] Initialized" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r1": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + }, + "r6": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r6": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r6": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r6") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r6" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info( + "[Step 2] : Test Setup " + "[Restart Mode]R2-----[Helper Mode]R1[Disable Mode]" + "--------R6[Helper Mode] Initialized" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r2": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r6" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r6" + input_dict_2 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r2", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + dut = "r6" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r6" + input_dict_2 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r2", ["bgpd"]) + + for addr_type in ADDR_TYPES: + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r6" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes after bringing up BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r6" + input_dict_2 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_BGP_GR_22_p2(request): + """ + Test Objective : Verify BGP-GR feature when helper node + is a transit router for it's iBGP peers. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Step 1] : Test Setup " "[Helper Mode]R3-----R1[Restart Mode] Initialized" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "graceful-restart-disable": True, + "next_hop_self": True, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "graceful-restart-disable": True, + "next_hop_self": True, + } + } + } + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info( + "[Step 2] : Test Setup " + "[Restart Mode]R2-----[Helper Mode]R1[Disable Mode]" + "--------R3[Helper Mode] Initialized" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r3" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r3" + input_dict_2 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r2", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + dut = "r3" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r3" + input_dict_2 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r2", ["bgpd"]) + + for addr_type in ADDR_TYPES: + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r3" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r3" + input_dict_2 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.json b/tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.json new file mode 100644 index 0000000..84ddc61 --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.json @@ -0,0 +1,222 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "192.168.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "192.168.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + } + }, + "vrfs": [ + { + "name": "RED", + "id": "1" + } + ], + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + } + } + } + ] + }, + "r2": { + "links": { + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "vrfs": [ + { + "name": "RED", + "id": "1" + } + ], + "bgp": [ + { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + } + } + }, + { + "local_as": "200", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + } + } + } + ] + }, + "r3": { + "links": { + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [ + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + } + } + } + ] + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_gr_functionality_topo3/test_bgp_gr_functionality_topo3.py b/tests/topotests/bgp_gr_functionality_topo3/test_bgp_gr_functionality_topo3.py new file mode 100644 index 0000000..593a8d6 --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo3/test_bgp_gr_functionality_topo3.py @@ -0,0 +1,541 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# + +import os +import sys +import time +import pytest +from time import sleep + +import traceback +import ipaddress + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join("../")) +sys.path.append(os.path.join("../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +# Import topoJson from lib, to create topology and initial configuration +from lib.topojson import build_config_from_json +from lib.bgp import ( + clear_bgp, + verify_bgp_rib, + verify_graceful_restart, + create_router_bgp, + verify_r_bit, + verify_eor, + verify_f_bit, + verify_bgp_convergence, + verify_gr_address_family, + modify_bgp_config_when_bgpd_down, + verify_graceful_restart_timers, + verify_bgp_convergence_from_running_config, +) + +# Import common_config to use commomnly used APIs +from lib.common_config import ( + create_common_configuration, + InvalidCLIError, + retry, + generate_ips, + FRRCFG_FILE, + find_interface_with_greater_ip, + check_address_types, + validate_ip_address, + run_frr_cmd, + get_frr_ipv6_linklocal, +) + +from lib.common_config import ( + write_test_header, + reset_config_on_routers, + start_topology, + kill_router_daemons, + start_router_daemons, + verify_rib, + check_address_types, + write_test_footer, + check_router_status, + step, + get_frr_ipv6_linklocal, + create_static_routes, + required_linux_kernel_version, +) + +pytestmark = [pytest.mark.bgpd] + + +# Global variables +BGP_CONVERGENCE = False +GR_RESTART_TIMER = 5 +GR_SELECT_DEFER_TIMER = 5 +GR_STALEPATH_TIMER = 5 +# Global variables +# STATIC_ROUTES=[] +NETWORK1_1 = {"ipv4": "192.0.2.1/32", "ipv6": "2001:DB8::1:1/128"} +NETWORK1_2 = {"ipv4": "192.0.2.2/32", "ipv6": "2001:DB8::2:1/128"} +NETWORK2_1 = {"ipv4": "192.0.2.3/32", "ipv6": "2001:DB8::3:1/128"} +NETWORK2_2 = {"ipv4": "192.0.2.4/32", "ipv6": "2001:DB8::4:1/128"} +NETWORK3_1 = {"ipv4": "192.0.2.5/32", "ipv6": "2001:DB8::5:1/128"} +NETWORK3_2 = {"ipv4": "192.0.2.6/32", "ipv6": "2001:DB8::6:1/128"} +NETWORK4_1 = {"ipv4": "192.0.2.7/32", "ipv6": "2001:DB8::7:1/128"} +NETWORK4_2 = {"ipv4": "192.0.2.8/32", "ipv6": "2001:DB8::8:1/128"} +NETWORK5_1 = {"ipv4": "192.0.2.9/32", "ipv6": "2001:DB8::9:1/128"} +NETWORK5_2 = {"ipv4": "192.0.2.10/32", "ipv6": "2001:DB8::10:1/128"} + +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + +PREFERRED_NEXT_HOP = "link_local" + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + result = configure_gr_followed_by_clear(tgen, topo, dut) + assert result is True, \ + "Testcase {} :Failed \n Error {}". \ + format(tc_name, result) + """ + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, dut) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, peer) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + return True + + +def verify_stale_routes_list(tgen, addr_type, dut, input_dict): + """ + This API is use verify Stale routes on refering the network with next hop value + Parameters + ---------- + * `tgen`: topogen object + * `dut`: input dut router name + * `addr_type` : ip type ipv4/ipv6 + * `input_dict` : input dict, has details of static routes + Usage + ----- + dut = 'r1' + input_dict = { + "r3": { + "static_routes": [ + + { + "network": [NETWORK1_1[addr_type]], + "no_of_ip": 2, + "vrf": "RED" + } + ] + } + } + + result = verify_stale_routes_list(tgen, addr_type, dut, input_dict) + Returns + ------- + errormsg(str) or True + """ + logger.debug("Entering lib API: verify_stale_routes_list()") + router_list = tgen.routers() + additional_nexthops_in_required_nhs = [] + list1 = [] + list2 = [] + found_hops = [] + for routerInput in input_dict.keys(): + for router, rnode in router_list.items(): + if router != dut: + continue + # Verifying RIB routes + command = "show bgp" + # Static routes + sleep(2) + logger.info("Checking router {} BGP RIB:".format(dut)) + if "static_routes" in input_dict[routerInput]: + static_routes = input_dict[routerInput]["static_routes"] + for static_route in static_routes: + found_routes = [] + missing_routes = [] + st_found = False + nh_found = False + vrf = static_route.setdefault("vrf", None) + community = static_route.setdefault("community", None) + largeCommunity = static_route.setdefault("largeCommunity", None) + if vrf: + cmd = "{} vrf {} {}".format(command, vrf, addr_type) + if community: + cmd = "{} community {}".format(cmd, community) + if largeCommunity: + cmd = "{} large-community {}".format(cmd, largeCommunity) + else: + cmd = "{} {}".format(command, addr_type) + cmd = "{} json".format(cmd) + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + # Verifying output dictionary rib_routes_json is not empty + if bool(rib_routes_json) == False: + errormsg = "[DUT: {}]: No route found in rib of router".format( + router + ) + return errormsg + elif "warning" in rib_routes_json: + errormsg = "[DUT: {}]: {}".format( + router, rib_routes_json["warning"] + ) + return errormsg + network = static_route["network"] + if "no_of_ip" in static_route: + no_of_ip = static_route["no_of_ip"] + else: + no_of_ip = 1 + # Generating IPs for verification + ip_list = generate_ips(network, no_of_ip) + + for st_rt in ip_list: + st_rt = str(ipaddress.ip_network(st_rt)) + _addr_type = validate_ip_address(st_rt) + if _addr_type != addr_type: + continue + if st_rt in rib_routes_json["routes"]: + st_found = True + + found_routes.append(st_rt) + for mnh in range(0, len(rib_routes_json["routes"][st_rt])): + found_hops.append( + [ + rib_r["ip"] + for rib_r in rib_routes_json["routes"][st_rt][ + mnh + ]["nexthops"] + ] + ) + return found_hops + else: + return "error msg - no hops found" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.16") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") + + global ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_gr_functionality_topo3.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + for addr_type in ADDR_TYPES: + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +################################################################################ +# +# TEST CASES +# +################################################################################ +def test_bgp_gr_stale_routes(request): + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + step("Verify the router failures") + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Creating 5 static Routes in Router R3 with NULL0 as Next hop") + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK5_1[addr_type]] + [NETWORK5_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("verifying Created Route at R3 in VRF default") + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict_1 = {"r3": topo["routers"]["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + # done + step("verifying Created Route at R2 in VRF default") + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + step("importing vrf RED on R2 under Address Family") + for addr_type in ADDR_TYPES: + input_import_vrf = { + "r2": { + "bgp": [ + { + "local_as": 200, + "vrf": "RED", + "address_family": { + addr_type: {"unicast": {"import": {"vrf": "default"}}} + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_import_vrf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + # done + step("verifying static Routes at R2 in VRF RED") + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("verifying static Routes at R1 in VRF RED") + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_1 = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Configuring Graceful restart at R2 and R3 ") + input_dict = { + "r2": { + "bgp": { + "local_as": "200", + "graceful-restart": { + "graceful-restart": True, + }, + } + }, + "r3": { + "bgp": {"local_as": "300", "graceful-restart": {"graceful-restart": True}} + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r2", peer="r3") + + step("verify Graceful restart at R2") + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r3" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("verify Graceful restart at R3") + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r3", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Configuring Graceful-restart-disable at R3") + input_dict = { + "r2": { + "bgp": { + "local_as": "200", + "graceful-restart": { + "graceful-restart": False, + }, + } + }, + "r3": { + "bgp": {"local_as": "300", "graceful-restart": {"graceful-restart": False}} + }, + } + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r3", peer="r2") + + step("Verify Graceful-restart-disable at R3") + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r3", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for iteration in range(5): + step("graceful-restart-disable:True at R3") + input_dict = { + "r3": { + "bgp": { + "graceful-restart": { + "graceful-restart-disable": True, + } + } + } + } + configure_gr_followed_by_clear( + tgen, topo, input_dict, tc_name, dut="r3", peer="r2" + ) + + step("Verifying Routes at R2 on enabling GRD") + dut = "r2" + for addr_type in ADDR_TYPES: + input_dict_1 = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format( + tc_name, result + ) + + step("Verify stale Routes in Router R2 enabling GRD") + for addr_type in ADDR_TYPES: + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "no_of_ip": 2, + "vrf": "RED", + } + ] + } + } + bgp_rib_next_hops = verify_stale_routes_list( + tgen, addr_type, dut, verify_nh_for_static_rtes + ) + assert ( + len(bgp_rib_next_hops) == 1 + ) is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_rib_next_hops, expected=True + ) + + step("graceful-restart-disable:False at R3") + input_dict = { + "r3": { + "bgp": { + "graceful-restart": { + "graceful-restart-disable": False, + } + } + } + } + configure_gr_followed_by_clear( + tgen, topo, input_dict, tc_name, dut="r3", peer="r2" + ) + + step("Verifying Routes at R2 on disabling GRD") + dut = "r2" + for addr_type in ADDR_TYPES: + input_dict_1 = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format( + tc_name, result + ) + + step("Verify stale Routes in Router R2 on disabling GRD") + for addr_type in ADDR_TYPES: + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "no_of_ip": 2, + "vrf": "RED", + } + ] + } + } + bgp_rib_next_hops = verify_stale_routes_list( + tgen, addr_type, dut, verify_nh_for_static_rtes + ) + + stale_route_status = len(bgp_rib_next_hops) == 1 + assert ( + stale_route_status is True + ), "Testcase {} : Failed \n Error: {}".format( + tc_name, stale_route_status, expected=True + ) + write_test_footer(tc_name) diff --git a/tests/topotests/bgp_gr_notification/__init__.py b/tests/topotests/bgp_gr_notification/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_gr_notification/r1/bgpd.conf b/tests/topotests/bgp_gr_notification/r1/bgpd.conf new file mode 100644 index 0000000..6119f67 --- /dev/null +++ b/tests/topotests/bgp_gr_notification/r1/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65001 + no bgp ebgp-requires-policy + bgp graceful-restart + neighbor 192.168.255.2 remote-as external + neighbor 192.168.255.2 timers 1 3 + neighbor 192.168.255.2 timers connect 1 + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_gr_notification/r1/zebra.conf b/tests/topotests/bgp_gr_notification/r1/zebra.conf new file mode 100644 index 0000000..091794f --- /dev/null +++ b/tests/topotests/bgp_gr_notification/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.1/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_gr_notification/r2/bgpd.conf b/tests/topotests/bgp_gr_notification/r2/bgpd.conf new file mode 100644 index 0000000..05e17f0 --- /dev/null +++ b/tests/topotests/bgp_gr_notification/r2/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 65002 + no bgp ebgp-requires-policy + no bgp hard-administrative-reset + bgp graceful-restart + neighbor 192.168.255.1 remote-as external + neighbor 192.168.255.1 timers 1 3 + neighbor 192.168.255.1 timers connect 1 + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_gr_notification/r2/zebra.conf b/tests/topotests/bgp_gr_notification/r2/zebra.conf new file mode 100644 index 0000000..1fcccbe --- /dev/null +++ b/tests/topotests/bgp_gr_notification/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.2/32 +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py b/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py new file mode 100644 index 0000000..2ffcb72 --- /dev/null +++ b/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# bgp_gr_notification.py +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +TC1: Disable the link between R1-R2 and wait for HoldTimerExpire notification: + 1) Check if R2 sent HoldTimerExpired notification + 2) Check if the routes are retained at R2 +TC2: Trigger `clear bgp` (Administrative Reset): + `bgp hard-administrative-reset` disabled: + a) Check if Administrative Reset notification was sent from R2 + b) Routes should be retained on R1 +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_hold_timer_expired_gr(): + # TC1 + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + def _disable_link_r1_r2(): + r1.cmd_raises("ip link set down dev r1-eth0") + + def _enable_link_r1_r2(): + r1.cmd_raises("ip link set up dev r1-eth0") + + def _bgp_check_hold_timer_expire_reason(): + output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "lastNotificationReason": "Hold Timer Expired", + } + } + return topotest.json_cmp(output, expected) + + def _bgp_check_hold_timer_expire_stale(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json")) + expected = { + "paths": [ + { + "stale": True, + "valid": True, + } + ] + } + return topotest.json_cmp(output, expected) + + step("Initial BGP converge") + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP convergence on R2" + + step("Disable the link between R1-R2") + _disable_link_r1_r2() + + step("Check if R2 sent HoldTimerExpire notification to R1") + test_func = functools.partial(_bgp_check_hold_timer_expire_reason) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see Hold Timer Expired notification from R2 on R1" + + step("Check if the routes are retained at R2") + test_func = functools.partial(_bgp_check_hold_timer_expire_stale) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see retained stale routes on R2" + + step("Enable the link between R1-R2") + _enable_link_r1_r2() + + +def test_bgp_administrative_reset_gr(): + # TC2 + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_check_hard_reset(): + output = json.loads(r1.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "lastNotificationReason": "Cease/Administrative Reset", + "lastNotificationHardReset": False, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_check_gr_notification_stale(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 172.16.255.2/32 json")) + expected = { + "paths": [ + { + "stale": True, + "valid": True, + } + ] + } + return topotest.json_cmp(output, expected) + + def _bgp_clear_r1_and_shutdown(): + r2.vtysh_cmd( + """ + clear ip bgp 192.168.255.1 + configure terminal + router bgp + neighbor 192.168.255.1 shutdown + """ + ) + + step("Initial BGP converge") + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP convergence on R2" + + step("Reset and shutdown R1") + _bgp_clear_r1_and_shutdown() + + step("Check if Hard Reset notification wasn't sent from R2") + test_func = functools.partial(_bgp_check_hard_reset) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to send Administrative Reset notification from R2" + + step("Check if stale routes are retained on R1") + test_func = functools.partial(_bgp_check_gr_notification_stale) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see retained stale routes on R1" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gr_restart_retain_routes/__init__.py b/tests/topotests/bgp_gr_restart_retain_routes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_gr_restart_retain_routes/r1/bgpd.conf b/tests/topotests/bgp_gr_restart_retain_routes/r1/bgpd.conf new file mode 100644 index 0000000..50d1583 --- /dev/null +++ b/tests/topotests/bgp_gr_restart_retain_routes/r1/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 65001 + no bgp ebgp-requires-policy + bgp graceful-restart + bgp graceful-restart preserve-fw-state + neighbor 192.168.255.2 remote-as external + neighbor 192.168.255.2 timers 1 3 + neighbor 192.168.255.2 timers connect 1 + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_gr_restart_retain_routes/r1/zebra.conf b/tests/topotests/bgp_gr_restart_retain_routes/r1/zebra.conf new file mode 100644 index 0000000..e65bfb2 --- /dev/null +++ b/tests/topotests/bgp_gr_restart_retain_routes/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface lo + ip address 172.16.255.1/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! diff --git a/tests/topotests/bgp_gr_restart_retain_routes/r2/bgpd.conf b/tests/topotests/bgp_gr_restart_retain_routes/r2/bgpd.conf new file mode 100644 index 0000000..97418ca --- /dev/null +++ b/tests/topotests/bgp_gr_restart_retain_routes/r2/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65002 + no bgp ebgp-requires-policy + bgp graceful-restart + bgp graceful-restart preserve-fw-state + neighbor 192.168.255.1 remote-as external + neighbor 192.168.255.1 timers 1 3 + neighbor 192.168.255.1 timers connect 1 +! diff --git a/tests/topotests/bgp_gr_restart_retain_routes/r2/zebra.conf b/tests/topotests/bgp_gr_restart_retain_routes/r2/zebra.conf new file mode 100644 index 0000000..758d797 --- /dev/null +++ b/tests/topotests/bgp_gr_restart_retain_routes/r2/zebra.conf @@ -0,0 +1,5 @@ +no zebra nexthop kernel enable +! +interface r2-eth0 + ip address 192.168.255.2/24 +! diff --git a/tests/topotests/bgp_gr_restart_retain_routes/test_bgp_gr_restart_retain_routes.py b/tests/topotests/bgp_gr_restart_retain_routes/test_bgp_gr_restart_retain_routes.py new file mode 100644 index 0000000..a820b4b --- /dev/null +++ b/tests/topotests/bgp_gr_restart_retain_routes/test_bgp_gr_restart_retain_routes.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Test if routes are retained during BGP restarts. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step, stop_router + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_gr_restart_retain_routes(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 neighbors 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_check_bgp_retained_routes(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json")) + expected = {"paths": [{"stale": True}]} + return topotest.json_cmp(output, expected) + + def _bgp_check_kernel_retained_routes(): + output = json.loads(r2.cmd("ip -j route show 172.16.255.1/32 proto bgp dev r2-eth0")) + expected = [{"dst":"172.16.255.1","gateway":"192.168.255.1","metric":20}] + return topotest.json_cmp(output, expected) + + step("Initial BGP converge") + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP convergence on R2" + + step("Restart R1") + stop_router(tgen, "r1") + + step("Check if routes (BGP) are retained at R2") + test_func = functools.partial(_bgp_check_bgp_retained_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP retained routes on R2" + + step("Check if routes (Kernel) are retained at R2") + assert _bgp_check_kernel_retained_routes() is None, "Failed to retain BGP routes in kernel on R2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gshut/__init__.py b/tests/topotests/bgp_gshut/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_gshut/r1/bgp_route_1.json b/tests/topotests/bgp_gshut/r1/bgp_route_1.json new file mode 100644 index 0000000..f3921b2 --- /dev/null +++ b/tests/topotests/bgp_gshut/r1/bgp_route_1.json @@ -0,0 +1,12 @@ +{ + "prefix":"13.1.1.1\/32", + "paths":[ + { + "origin":"IGP", + "metric":0, + "locPrf":100, + "valid":true + } + ] +} + diff --git a/tests/topotests/bgp_gshut/r1/bgp_route_2.json b/tests/topotests/bgp_gshut/r1/bgp_route_2.json new file mode 100644 index 0000000..754a0ed --- /dev/null +++ b/tests/topotests/bgp_gshut/r1/bgp_route_2.json @@ -0,0 +1,17 @@ +{ + "prefix":"13.1.1.1\/32", + "paths":[ + { + "origin":"IGP", + "metric":0, + "locPrf":0, + "valid":true, + "community":{ + "string":"graceful-shutdown", + "list":[ + "gracefulShutdown" + ] + } + } + ] +} diff --git a/tests/topotests/bgp_gshut/r1/bgpd.conf b/tests/topotests/bgp_gshut/r1/bgpd.conf new file mode 100644 index 0000000..ab6f47a --- /dev/null +++ b/tests/topotests/bgp_gshut/r1/bgpd.conf @@ -0,0 +1,10 @@ +! exit1 +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.255.1 remote-as 65001 + neighbor 192.168.255.1 timers connect 10 + address-family ipv4 unicast + network 11.1.1.1/32 + exit-address-family +! diff --git a/tests/topotests/bgp_gshut/r1/zebra.conf b/tests/topotests/bgp_gshut/r1/zebra.conf new file mode 100644 index 0000000..9904bb4 --- /dev/null +++ b/tests/topotests/bgp_gshut/r1/zebra.conf @@ -0,0 +1,9 @@ +! exit1 +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_gshut/r2/bgp_sum_1.json b/tests/topotests/bgp_gshut/r2/bgp_sum_1.json new file mode 100644 index 0000000..9d8948a --- /dev/null +++ b/tests/topotests/bgp_gshut/r2/bgp_sum_1.json @@ -0,0 +1,15 @@ +{ +"ipv4Unicast":{ + "peers":{ + "192.168.254.2":{ + "remoteAs":65003, + "state":"Established" + }, + "192.168.255.2":{ + "remoteAs":65001, + "state":"Established" + } + }, + "totalPeers":2 +} +} diff --git a/tests/topotests/bgp_gshut/r2/bgp_sum_2.json b/tests/topotests/bgp_gshut/r2/bgp_sum_2.json new file mode 100644 index 0000000..7183db6 --- /dev/null +++ b/tests/topotests/bgp_gshut/r2/bgp_sum_2.json @@ -0,0 +1,15 @@ +{ +"ipv4Unicast":{ + "peers":{ + "192.168.252.2":{ + "remoteAs":65005, + "state":"Established" + }, + "192.168.253.2":{ + "remoteAs":65004, + "state":"Established" + } + }, + "totalPeers":2 +} +} diff --git a/tests/topotests/bgp_gshut/r2/bgpd.conf b/tests/topotests/bgp_gshut/r2/bgpd.conf new file mode 100644 index 0000000..b0ca4e6 --- /dev/null +++ b/tests/topotests/bgp_gshut/r2/bgpd.conf @@ -0,0 +1,20 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.254.2 remote-as 65003 + neighbor 192.168.255.2 timers connect 10 + neighbor 192.168.254.2 timers connect 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! +router bgp 65001 vrf vrf1 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.253.2 remote-as 65004 + neighbor 192.168.253.2 timers connect 10 + neighbor 192.168.252.2 remote-as 65005 + neighbor 192.168.252.2 timers connect 10 +! diff --git a/tests/topotests/bgp_gshut/r2/zebra.conf b/tests/topotests/bgp_gshut/r2/zebra.conf new file mode 100644 index 0000000..0da0501 --- /dev/null +++ b/tests/topotests/bgp_gshut/r2/zebra.conf @@ -0,0 +1,13 @@ +! spine +interface r2-eth0 + ip address 192.168.255.1/30 +! +interface r2-eth1 + ip address 192.168.254.1/30 +! +interface r2-eth2 vrf vrf1 + ip address 192.168.253.1/30 +! +interface r2-eth3 vrf vrf1 + ip address 192.168.252.1/30 +! diff --git a/tests/topotests/bgp_gshut/r3/bgp_route_1.json b/tests/topotests/bgp_gshut/r3/bgp_route_1.json new file mode 100644 index 0000000..94de01a --- /dev/null +++ b/tests/topotests/bgp_gshut/r3/bgp_route_1.json @@ -0,0 +1,9 @@ +{ + "prefix":"11.1.1.1\/32", + "paths":[ + { + "origin":"IGP", + "valid":true + } + ] +} diff --git a/tests/topotests/bgp_gshut/r3/bgp_route_2.json b/tests/topotests/bgp_gshut/r3/bgp_route_2.json new file mode 100644 index 0000000..f918265 --- /dev/null +++ b/tests/topotests/bgp_gshut/r3/bgp_route_2.json @@ -0,0 +1,16 @@ +{ + "prefix":"11.1.1.1\/32", + "paths":[ + { + "origin":"IGP", + "locPrf":0, + "valid":true, + "community":{ + "string":"graceful-shutdown", + "list":[ + "gracefulShutdown" + ] + } + } + ] +} diff --git a/tests/topotests/bgp_gshut/r3/bgpd.conf b/tests/topotests/bgp_gshut/r3/bgpd.conf new file mode 100644 index 0000000..5d7c0cd --- /dev/null +++ b/tests/topotests/bgp_gshut/r3/bgpd.conf @@ -0,0 +1,11 @@ +! +router bgp 65003 + no bgp ebgp-requires-policy + no bgp network import-check + timers bgp 3 9 + neighbor 192.168.254.1 remote-as 65001 + neighbor 192.168.254.1 timers connect 10 + address-family ipv4 unicast + network 13.1.1.1/32 + exit-address-family +! diff --git a/tests/topotests/bgp_gshut/r3/zebra.conf b/tests/topotests/bgp_gshut/r3/zebra.conf new file mode 100644 index 0000000..f490d97 --- /dev/null +++ b/tests/topotests/bgp_gshut/r3/zebra.conf @@ -0,0 +1,9 @@ +! exit2 +interface lo + ip address 172.16.254.254/32 +! +interface r3-eth0 + ip address 192.168.254.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_gshut/r4/bgpd.conf b/tests/topotests/bgp_gshut/r4/bgpd.conf new file mode 100644 index 0000000..375f383 --- /dev/null +++ b/tests/topotests/bgp_gshut/r4/bgpd.conf @@ -0,0 +1,11 @@ +! +router bgp 65004 + no bgp ebgp-requires-policy + no bgp network import-check + timers bgp 3 9 + neighbor 192.168.253.1 remote-as 65001 + neighbor 192.168.253.1 timers connect 10 + address-family ipv4 unicast + network 14.1.1.1/32 + exit-address-family +! diff --git a/tests/topotests/bgp_gshut/r4/zebra.conf b/tests/topotests/bgp_gshut/r4/zebra.conf new file mode 100644 index 0000000..baba04c --- /dev/null +++ b/tests/topotests/bgp_gshut/r4/zebra.conf @@ -0,0 +1,9 @@ +! exit2 +interface lo + ip address 172.16.253.254/32 +! +interface r4-eth0 + ip address 192.168.253.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_gshut/r5/bgp_route_1.json b/tests/topotests/bgp_gshut/r5/bgp_route_1.json new file mode 100644 index 0000000..4e6fd79 --- /dev/null +++ b/tests/topotests/bgp_gshut/r5/bgp_route_1.json @@ -0,0 +1,9 @@ +{ + "prefix":"14.1.1.1\/32", + "paths":[ + { + "origin":"IGP", + "valid":true + } + ] +} diff --git a/tests/topotests/bgp_gshut/r5/bgp_route_2.json b/tests/topotests/bgp_gshut/r5/bgp_route_2.json new file mode 100644 index 0000000..980d8de --- /dev/null +++ b/tests/topotests/bgp_gshut/r5/bgp_route_2.json @@ -0,0 +1,16 @@ +{ + "prefix":"14.1.1.1\/32", + "paths":[ + { + "origin":"IGP", + "locPrf":0, + "valid":true, + "community":{ + "string":"graceful-shutdown", + "list":[ + "gracefulShutdown" + ] + } + } + ] +} diff --git a/tests/topotests/bgp_gshut/r5/bgpd.conf b/tests/topotests/bgp_gshut/r5/bgpd.conf new file mode 100644 index 0000000..15b49f5 --- /dev/null +++ b/tests/topotests/bgp_gshut/r5/bgpd.conf @@ -0,0 +1,7 @@ +! +router bgp 65005 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.252.1 remote-as 65001 + neighbor 192.168.252.1 timers connect 10 +! diff --git a/tests/topotests/bgp_gshut/r5/zebra.conf b/tests/topotests/bgp_gshut/r5/zebra.conf new file mode 100644 index 0000000..c4cbd52 --- /dev/null +++ b/tests/topotests/bgp_gshut/r5/zebra.conf @@ -0,0 +1,9 @@ +! exit1 +interface lo + ip address 172.16.252.254/32 +! +interface r5-eth0 + ip address 192.168.252.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_gshut/test_bgp_gshut.py b/tests/topotests/bgp_gshut/test_bgp_gshut.py new file mode 100644 index 0000000..61a0fe6 --- /dev/null +++ b/tests/topotests/bgp_gshut/test_bgp_gshut.py @@ -0,0 +1,343 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_gshut.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Vivek Venkatraman +# + +""" +Test the ability to initiate and stop BGP graceful shutdown. +Test both the vrf-specific and global configuration and operation. + +r1 +| +r2----r3 +| \ +| \ +r4 r5 + + +r2 is UUT and peers with r1 and r3 in default bgp instance and +with r4 and r5 in vrf vrf1. +r1-r2 peering is iBGP and the other peerings are eBGP. + +Check r2 initial convergence in default table +Define update-delay with max-delay in the default bgp instance on r2 +Shutdown peering on r1 toward r2 so that delay timers can be exercised +Clear bgp neighbors on r2 and then check for the 'in progress' indicator +Check that r2 only installs route learned from r4 after the max-delay timer expires +Define update-delay with max-delay and estabish-wait and check json output showing set +Clear neighbors on r2 and check that r3 installs route from r4 after establish-wait time +Remove update-delay timer on r2 to verify that it goes back to normal behavior +Clear neighbors on r2 and check that route install time on r2 does not delay +Define global bgp update-delay with max-delay and establish-wait on r2 +Check that r2 default instance and vrf1 have the max-delay and establish set +Clear neighbors on r2 and check route-install time is after the establish-wait timer + +Note that the keepalive/hold times were changed to 3/9 and the connect retry timer +to 10 to improve the odds the convergence timing in this test case is useful in the +event of packet loss. +""" + +import os +import re +import sys +import json +import pytest +import platform +from functools import partial + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 6): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r5"]) + + +def _run_cmd_and_check(router, cmd, results_file, retries=100, intvl=0.5): + json_file = "{}/{}".format(CWD, results_file) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, router, cmd, expected) + return topotest.run_and_expect(test_func, None, retries, intvl) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + krel = platform.release() + if topotest.version_cmp(krel, "4.5") < 0: + tgen.errors = "Linux kernel version of at least 4.5 needed for bgp-gshut tests" + pytest.skip(tgen.errors) + + # Configure vrf and its slaves in the kernel on r2 + r2 = tgen.gears["r2"] + r2.run("ip link add vrf1 type vrf table 1000") + r2.run("ip link set vrf1 up") + r2.run("ip link set r2-eth2 master vrf1") + r2.run("ip link set r2-eth3 master vrf1") + + # Load FRR config and initialize all routers + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + # Basic peering test to see if things are ok + _, result = _run_cmd_and_check(r2, "show ip bgp summary json", "r2/bgp_sum_1.json") + assertmsg = "R2: Basic sanity test after init failed -- global peerings not up" + assert result is None, assertmsg + + _, result = _run_cmd_and_check( + r2, "show ip bgp vrf vrf1 summary json", "r2/bgp_sum_2.json" + ) + assertmsg = "R2: Basic sanity test after init failed -- VRF peerings not up" + assert result is None, assertmsg + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_gshut(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + r4 = tgen.gears["r4"] + r5 = tgen.gears["r5"] + + # Verify initial route states + logger.info("\nVerify initial route states") + + _, result = _run_cmd_and_check( + r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json" + ) + assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params" + assert result is None, assertmsg + + _, result = _run_cmd_and_check( + r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json" + ) + assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params" + assert result is None, assertmsg + + _, result = _run_cmd_and_check( + r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_1.json" + ) + assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params" + assert result is None, assertmsg + + logger.info("\nInitial route states are as expected") + + # "Test #1: Enable BGP-wide graceful-shutdown on R2 and check routes on peers" + logger.info( + "\nTest #1: Enable BGP-wide graceful-shutdown on R2 and check routes on peers" + ) + + r2.vtysh_cmd( + """ + configure terminal + bgp graceful-shutdown + """ + ) + + # R1, R3 and R5 should see routes from R2 with GSHUT. In addition, + # R1 should see LOCAL_PREF of 0 + _, result = _run_cmd_and_check( + r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_2.json" + ) + assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params" + assert result is None, assertmsg + + _, result = _run_cmd_and_check( + r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_2.json" + ) + assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params" + assert result is None, assertmsg + + _, result = _run_cmd_and_check( + r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_2.json" + ) + assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params" + assert result is None, assertmsg + + logger.info( + "\nTest #1: Successful, routes have GSHUT and/or LPREF of 0 as expected" + ) + + # "Test #2: Turn off BGP-wide graceful-shutdown on R2 and check routes on peers" + logger.info( + "\nTest #2: Turn off BGP-wide graceful-shutdown on R2 and check routes on peers" + ) + + r2.vtysh_cmd( + """ + configure terminal + no bgp graceful-shutdown + """ + ) + + # R1, R3 and R5 should see routes from R2 with their original attributes + _, result = _run_cmd_and_check( + r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json" + ) + assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params" + assert result is None, assertmsg + + _, result = _run_cmd_and_check( + r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json" + ) + assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params" + assert result is None, assertmsg + + _, result = _run_cmd_and_check( + r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_1.json" + ) + assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params" + assert result is None, assertmsg + + logger.info( + "\nTest #2: Successful, routes have their original attributes with default LPREF and without GSHUT" + ) + + # "Test #3: Enable graceful-shutdown on R2 only in VRF1 and check routes on peers" + logger.info( + "\nTest #3: Enable graceful-shutdown on R2 only in VRF1 and check routes on peers" + ) + + r2.vtysh_cmd( + """ + configure terminal + router bgp 65001 vrf vrf1 + bgp graceful-shutdown + """ + ) + + # R1 and R3 should see no change to their routes + _, result = _run_cmd_and_check( + r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json" + ) + assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params" + assert result is None, assertmsg + + _, result = _run_cmd_and_check( + r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json" + ) + assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params" + assert result is None, assertmsg + + # R5 should see routes from R2 with GSHUT. + _, result = _run_cmd_and_check( + r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_2.json" + ) + assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params" + assert result is None, assertmsg + + logger.info("\nTest #3: Successful, only VRF peers like R5 see routes with GSHUT") + + # "Test #4: Try to enable BGP-wide graceful-shutdown on R2 while it is configured in VRF1" + logger.info( + "\nTest #4: Try to enable BGP-wide graceful-shutdown on R2 while it is configured in VRF1" + ) + + ret = r2.vtysh_cmd( + """ + configure terminal + bgp graceful-shutdown + """ + ) + + # This should fail + assertmsg = "R2: BGP-wide graceful-shutdown config not rejected even though it is enabled in VRF1" + assert ( + re.search("global graceful-shutdown not permitted", ret) is not None + ), assertmsg + + logger.info( + "\nTest #4: Successful, BGP-wide graceful-shutdown rejected as it is enabled in VRF" + ) + + # "Test #5: Turn off graceful-shutdown on R2 in VRF1 and check routes on peers" + logger.info( + "\nTest #5: Turn off graceful-shutdown on R2 in VRF1 and check routes on peers" + ) + + r2.vtysh_cmd( + """ + configure terminal + router bgp 65001 vrf vrf1 + no bgp graceful-shutdown + """ + ) + + # R1 and R3 should see no change to their routes + _, result = _run_cmd_and_check( + r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json" + ) + assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params" + assert result is None, assertmsg + + _, result = _run_cmd_and_check( + r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json" + ) + assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params" + assert result is None, assertmsg + + # R5 should see routes from R2 with original attributes. + _, result = _run_cmd_and_check( + r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_1.json" + ) + assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params" + assert result is None, assertmsg + + logger.info( + "\nTest #5: Successful, routes have their original attributes with default LPREF and without GSHUT" + ) + + # tgen.mininet_cli() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gshut_topo1/ebgp_gshut_topo1.json b/tests/topotests/bgp_gshut_topo1/ebgp_gshut_topo1.json new file mode 100644 index 0000000..dcd5af8 --- /dev/null +++ b/tests/topotests/bgp_gshut_topo1/ebgp_gshut_topo1.json @@ -0,0 +1,221 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.1.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + "static_routes":[ + { + "network":"100.0.10.1/32", + "no_of_ip":5, + "next_hop":"Null0" + }, + { + "network":"1::1/128", + "no_of_ip":5, + "next_hop":"Null0" + }] + }, + "r2": { + "links": { + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": {} + } + }, + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": {} + } + }, + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_gshut_topo1/ibgp_gshut_topo1.json b/tests/topotests/bgp_gshut_topo1/ibgp_gshut_topo1.json new file mode 100644 index 0000000..464e362 --- /dev/null +++ b/tests/topotests/bgp_gshut_topo1/ibgp_gshut_topo1.json @@ -0,0 +1,221 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.1.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + "static_routes":[ + { + "network":"100.0.10.1/32", + "no_of_ip":5, + "next_hop":"Null0" + }, + { + "network":"1::1/128", + "no_of_ip":5, + "next_hop":"Null0" + }] + }, + "r2": { + "links": { + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": {} + } + }, + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": {} + } + }, + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py b/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py new file mode 100644 index 0000000..5ec9db0 --- /dev/null +++ b/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py @@ -0,0 +1,593 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# + + +""" +Following tests are covered to test ecmp functionality on BGP GSHUT. +1. Verify graceful-shutdown functionality with eBGP peers +2. Verify graceful-shutdown functionality when daemons + bgpd/zebra/staticd and frr services are restarted with eBGP peers +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + verify_rib, + check_address_types, + reset_config_on_routers, + step, + get_frr_ipv6_linklocal, + kill_router_daemons, + start_router_daemons, + stop_router, + start_router, + create_route_maps, + create_bgp_community_lists, + required_linux_kernel_version, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, + verify_bgp_attributes, +) +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +# Global variables +NETWORK = {"ipv4": "100.0.10.1/32", "ipv6": "1::1/128"} +NEXT_HOP_IP_1 = {"ipv4": "10.0.2.1", "ipv6": "fd00:0:0:1::1"} +NEXT_HOP_IP_2 = {"ipv4": "10.0.4.2", "ipv6": "fd00:0:0:3::2"} +PREFERRED_NEXT_HOP = "link_local" +BGP_CONVERGENCE = False + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global ADDR_TYPES + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.16") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/ebgp_gshut_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +########################### +# Local APIs +########################### + + +def next_hop_per_address_family( + tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP +): + """ + This function returns link_local or global next_hop per address-family + """ + + intferface = topo["routers"][peer]["links"]["{}".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in preferred_next_hop: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +########################### +# TESTCASES +########################### + + +def test_verify_graceful_shutdown_functionality_with_eBGP_peers_p0(request): + """ + Verify graceful-shutdown functionality with eBGP peers + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + step("Done in base config: Configure base config as per the topology") + step("Base config should be up, verify using BGP convergence") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step("Done in base config: Advertise prefixes from R1") + step("Verify BGP routes are received at R3 with best path from R3 to R1") + + for addr_type in ADDR_TYPES: + dut = "r3" + next_hop1 = next_hop_per_address_family( + tgen, "r3", "r1", addr_type, NEXT_HOP_IP_1 + ) + next_hop2 = next_hop_per_address_family( + tgen, "r3", "r4", addr_type, NEXT_HOP_IP_2 + ) + + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2] + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop1) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("On R1 configure:") + step("Create standard bgp community-list to permit graceful-shutdown:") + input_dict_1 = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "GSHUT", + "value": "graceful-shutdown", + } + ] + } + } + + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route-map to set community GSHUT in OUT direction") + + input_dict_2 = { + "r1": { + "route_maps": { + "GSHUT-OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": {"community": {"num": "graceful-shutdown"}}, + } + ] + } + } + } + + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "GSHUT-OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "GSHUT-OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step( + "FRR is setting local-pref to 0 by-default on receiver GSHUT community, " + "below step is not needed, but keeping for reference" + ) + step( + "On R3, apply route-map IN direction to match GSHUT community " + "and set local-preference to 0." + ) + + step( + "Verify BGP convergence on R3 and ensure all the neighbours state " + "is established" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP routes on R3:") + step("local pref for routes coming from R1 is set to 0.") + + for addr_type in ADDR_TYPES: + rmap_dict = { + "r1": { + "route_maps": { + "GSHUT-OUT": [{"set": {"locPrf": 0}}], + } + } + } + + static_routes = [NETWORK[addr_type]] + result = verify_bgp_attributes( + tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Ensure that best path is selected from R4 to R3.") + + for addr_type in ADDR_TYPES: + dut = "r3" + next_hop1 = next_hop_per_address_family( + tgen, "r3", "r1", addr_type, NEXT_HOP_IP_1 + ) + next_hop2 = next_hop_per_address_family( + tgen, "r3", "r4", addr_type, NEXT_HOP_IP_2 + ) + + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2] + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop2) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_restarting_zebra_bgpd_staticd_frr_with_eBGP_peers_p0(request): + """ + Verify graceful-shutdown functionality when daemons bgpd/zebra/staticd and + frr services are restarted with eBGP peers + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + step("Done in base config: Configure base config as per the topology") + step("Base config should be up, verify using BGP convergence") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step("Done in base config: Advertise prefixes from R1") + step("Verify BGP routes are received at R3 with best path from R3 to R1") + + for addr_type in ADDR_TYPES: + dut = "r3" + next_hop1 = next_hop_per_address_family( + tgen, "r3", "r1", addr_type, NEXT_HOP_IP_1 + ) + next_hop2 = next_hop_per_address_family( + tgen, "r3", "r4", addr_type, NEXT_HOP_IP_2 + ) + + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2] + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop1) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("On R1 configure:") + step("Create standard bgp community-list to permit graceful-shutdown:") + input_dict_1 = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "GSHUT", + "value": "graceful-shutdown", + } + ] + } + } + + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route-map to set community GSHUT in OUT direction") + + input_dict_2 = { + "r1": { + "route_maps": { + "GSHUT-OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": {"community": {"num": "graceful-shutdown"}}, + } + ] + } + } + } + + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "GSHUT-OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "GSHUT-OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step( + "FRR is setting local-pref to 0 by-default on receiver GSHUT community, " + "below step is not needed, but keeping for reference" + ) + step( + "On R3, apply route-map IN direction to match GSHUT community " + "and set local-preference to 0." + ) + + step( + "Verify BGP convergence on R3 and ensure all the neighbours state " + "is established" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP routes on R3:") + step("local pref for routes coming from R1 is set to 0.") + + for addr_type in ADDR_TYPES: + rmap_dict = {"r1": {"route_maps": {"GSHUT-OUT": [{"set": {"locPrf": 0}}]}}} + + static_routes = [NETWORK[addr_type]] + result = verify_bgp_attributes( + tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Ensure that best path is selected from R4 to R3.") + + for addr_type in ADDR_TYPES: + dut = "r3" + next_hop1 = next_hop_per_address_family( + tgen, "r3", "r1", addr_type, NEXT_HOP_IP_1 + ) + next_hop2 = next_hop_per_address_family( + tgen, "r3", "r4", addr_type, NEXT_HOP_IP_2 + ) + + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2] + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop2) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Restart daemons and frr services") + + for daemon in ["bgpd", "zebra", "staticd", "frr"]: + if daemon != "frr": + kill_router_daemons(tgen, "r3", ["staticd"]) + start_router_daemons(tgen, "r3", ["staticd"]) + else: + stop_router(tgen, "r3") + start_router(tgen, "r3") + + step( + "Verify BGP convergence on R3 and ensure all the neighbours state " + "is established" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify BGP routes on R3:") + step("local pref for routes coming from R1 is set to 0.") + + for addr_type in ADDR_TYPES: + rmap_dict = {"r1": {"route_maps": {"GSHUT-OUT": [{"set": {"locPrf": 0}}]}}} + + static_routes = [NETWORK[addr_type]] + result = verify_bgp_attributes( + tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Ensure that best path is selected from R4 to R3.") + + for addr_type in ADDR_TYPES: + dut = "r3" + next_hop1 = next_hop_per_address_family( + tgen, "r3", "r1", addr_type, NEXT_HOP_IP_1 + ) + next_hop2 = next_hop_per_address_family( + tgen, "r3", "r4", addr_type, NEXT_HOP_IP_2 + ) + + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2] + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop2) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py b/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py new file mode 100644 index 0000000..1e0f726 --- /dev/null +++ b/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py @@ -0,0 +1,640 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# + + +""" +Following tests are covered to test ecmp functionality on BGP GSHUT. +1. Verify graceful-shutdown functionality with iBGP peers +2. Verify graceful-shutdown functionality after + deleting/re-adding route-map with iBGP peers +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + verify_rib, + check_address_types, + reset_config_on_routers, + step, + get_frr_ipv6_linklocal, + create_route_maps, + create_bgp_community_lists, + required_linux_kernel_version, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, + verify_bgp_attributes, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +# Global variables +NETWORK = {"ipv4": "100.0.10.1/32", "ipv6": "1::1/128"} +NEXT_HOP_IP_1 = {"ipv4": "10.0.3.1", "ipv6": "fd00:0:0:3::1"} +NEXT_HOP_IP_2 = {"ipv4": "10.0.4.1", "ipv6": "fd00:0:0:2::1"} +PREFERRED_NEXT_HOP = "link_local" +BGP_CONVERGENCE = False + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global ADDR_TYPES + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.16") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/ibgp_gshut_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +########################### +# Local APIs +########################### + + +def next_hop_per_address_family( + tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP +): + """ + This function returns link_local or global next_hop per address-family + """ + + intferface = topo["routers"][peer]["links"]["{}".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in preferred_next_hop: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +########################### +# TESTCASES +########################### + + +def test_verify_graceful_shutdown_functionality_with_iBGP_peers_p0(request): + """ + Verify graceful-shutdown functionality with iBGP peers + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + step("Done in base config: Configure base config as per the topology") + step("Base config should be up, verify using BGP convergence") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step("Done in base config: Advertise prefixes from R1") + step("Verify BGP routes are received at R4 with best path from R3 to R1") + + for addr_type in ADDR_TYPES: + dut = "r4" + next_hop1 = next_hop_per_address_family( + tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1 + ) + next_hop2 = next_hop_per_address_family( + tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2 + ) + + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2] + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2] + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("On R1 configure:") + step("Create standard bgp community-list to permit graceful-shutdown:") + input_dict_1 = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "GSHUT", + "value": "graceful-shutdown", + } + ] + } + } + + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route-map to set community GSHUT in OUT direction") + + input_dict_2 = { + "r1": { + "route_maps": { + "GSHUT-OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": {"community": {"num": "graceful-shutdown"}}, + } + ] + } + } + } + + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "GSHUT-OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "GSHUT-OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step( + "FRR is setting local-pref to 0 by-default on receiver GSHUT community, " + "below step is not needed, but keeping for reference" + ) + step( + "On R3, apply route-map IN direction to match GSHUT community " + "and set local-preference to 0." + ) + + step( + "Verify BGP convergence on R4 and ensure all the neighbours state " + "is established" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP routes on R4:") + step("local pref for routes coming from R1 is set to 0.") + + for addr_type in ADDR_TYPES: + rmap_dict = { + "r1": { + "route_maps": { + "GSHUT-OUT": [{"set": {"locPrf": 0}}], + } + } + } + + static_routes = [NETWORK[addr_type]] + result = verify_bgp_attributes( + tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Ensure that best path is selected from R4 to R3.") + + for addr_type in ADDR_TYPES: + dut = "r4" + next_hop1 = next_hop_per_address_family( + tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1 + ) + next_hop2 = next_hop_per_address_family( + tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2 + ) + + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2] + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop1) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_deleting_re_adding_route_map_with_iBGP_peers_p0(request): + """ + Verify graceful-shutdown functionality after deleting/re-adding route-map + with iBGP peers + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + step("Done in base config: Configure base config as per the topology") + step("Base config should be up, verify using BGP convergence") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step("Done in base config: Advertise prefixes from R1") + step("Verify BGP routes are received at R4 with best path from R3 to R1") + + for addr_type in ADDR_TYPES: + dut = "r4" + next_hop1 = next_hop_per_address_family( + tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1 + ) + next_hop2 = next_hop_per_address_family( + tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2 + ) + + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2] + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2] + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("On R1 configure:") + step("Create standard bgp community-list to permit graceful-shutdown:") + input_dict_1 = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "GSHUT", + "value": "graceful-shutdown", + } + ] + } + } + + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route-map to set community GSHUT in OUT direction") + + input_dict_2 = { + "r1": { + "route_maps": { + "GSHUT-OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": {"community": {"num": "graceful-shutdown"}}, + } + ] + } + } + } + + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "GSHUT-OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "GSHUT-OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step( + "FRR is setting local-pref to 0 by-default on receiver GSHUT community, " + "below step is not needed, but keeping for reference" + ) + step( + "On R3, apply route-map IN direction to match GSHUT community " + "and set local-preference to 0." + ) + + step( + "Verify BGP convergence on R4 and ensure all the neighbours state " + "is established" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP routes on R4:") + step("local pref for routes coming from R1 is set to 0.") + + for addr_type in ADDR_TYPES: + rmap_dict = { + "r1": { + "route_maps": { + "GSHUT-OUT": [{"set": {"locPrf": 0}}], + } + } + } + + static_routes = [NETWORK[addr_type]] + result = verify_bgp_attributes( + tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Ensure that best path is selected from R4 to R3.") + + for addr_type in ADDR_TYPES: + dut = "r4" + next_hop1 = next_hop_per_address_family( + tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1 + ) + next_hop2 = next_hop_per_address_family( + tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2 + ) + + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2] + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop1) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Delete route-map from R1") + del_rmap_dict = { + "r1": { + "route_maps": { + "GSHUT-OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "community": {"num": "graceful-shutdown", "delete": True} + }, + } + ] + } + } + } + + result = create_route_maps(tgen, del_rmap_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify BGP convergence on R3 and ensure that all neighbor state " + "is established" + ) + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP routes on R4:") + step("Ensure that best path is selected from R1->R3") + + for addr_type in ADDR_TYPES: + dut = "r4" + next_hop1 = next_hop_per_address_family( + tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1 + ) + next_hop2 = next_hop_per_address_family( + tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2 + ) + + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop=[next_hop1]) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=[next_hop1]) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Re-add route-map in R1") + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify BGP convergence on R3 and ensure all the neighbours state " + "is established" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP routes on R4:") + step("local pref for routes coming from R1 is set to 0.") + + for addr_type in ADDR_TYPES: + rmap_dict = {"r1": {"route_maps": {"GSHUT-OUT": [{"set": {"locPrf": 0}}]}}} + + static_routes = [NETWORK[addr_type]] + result = verify_bgp_attributes( + tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Ensure that best path is selected from R4 to R3.") + + for addr_type in ADDR_TYPES: + dut = "r4" + next_hop1 = next_hop_per_address_family( + tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1 + ) + next_hop2 = next_hop_per_address_family( + tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2 + ) + + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2] + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop1) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_instance_del_test/__init__.py b/tests/topotests/bgp_instance_del_test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_instance_del_test/ce1 b/tests/topotests/bgp_instance_del_test/ce1 new file mode 120000 index 0000000..0924eb5 --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/ce1 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/ce1 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/ce2 b/tests/topotests/bgp_instance_del_test/ce2 new file mode 120000 index 0000000..8c7a677 --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/ce2 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/ce2 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/ce3 b/tests/topotests/bgp_instance_del_test/ce3 new file mode 120000 index 0000000..0abb8e5 --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/ce3 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/ce3 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/ce4 b/tests/topotests/bgp_instance_del_test/ce4 new file mode 120000 index 0000000..ddee1ef --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/ce4 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/ce4 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/customize.py b/tests/topotests/bgp_instance_del_test/customize.py new file mode 120000 index 0000000..99fcf39 --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/customize.py @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/customize.py \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/r1 b/tests/topotests/bgp_instance_del_test/r1 new file mode 120000 index 0000000..16babfa --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/r1 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/r1 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/r2 b/tests/topotests/bgp_instance_del_test/r2 new file mode 120000 index 0000000..e25b932 --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/r2 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/r2 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/r3 b/tests/topotests/bgp_instance_del_test/r3 new file mode 120000 index 0000000..0d7c189 --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/r3 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/r3 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/r4 b/tests/topotests/bgp_instance_del_test/r4 new file mode 120000 index 0000000..2d667d3 --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/r4 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/r4 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/scripts b/tests/topotests/bgp_instance_del_test/scripts new file mode 120000 index 0000000..c46bf1f --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/scripts @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/scripts \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py b/tests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py new file mode 100755 index 0000000..ddb5fe5 --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright (c) 2018, LabN Consulting, L.L.C. +# Authored by Lou Berger +# + +import os +import sys +import pytest + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")) + +from lib.ltemplate import * + + +pytestmark = [pytest.mark.bgpd, pytest.mark.ldpd, pytest.mark.ospfd] + + +def test_check_linux_vrf(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/check_linux_vrf.py", False, CliOnFail, CheckFunc) + + +def test_adjacencies(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/adjacencies.py", False, CliOnFail, CheckFunc) + + +def SKIP_test_add_routes(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/add_routes.py", False, CliOnFail, CheckFunc) + + +def test_check_routes(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/check_routes.py", False, CliOnFail, CheckFunc) + + +# manual data path setup test - remove once have bgp/zebra vrf path working +def test_check_linux_mpls(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/check_linux_mpls.py", False, CliOnFail, CheckFunc) + + +def test_del_bgp_instances(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/del_bgp_instances.py", False, CliOnFail, CheckFunc) + + +if __name__ == "__main__": + retval = pytest.main(["-s"]) + sys.exit(retval) diff --git a/tests/topotests/bgp_ipv4_class_e_peer/__init__.py b/tests/topotests/bgp_ipv4_class_e_peer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_ipv4_class_e_peer/r1/bgpd.conf b/tests/topotests/bgp_ipv4_class_e_peer/r1/bgpd.conf new file mode 100644 index 0000000..bf0a68e --- /dev/null +++ b/tests/topotests/bgp_ipv4_class_e_peer/r1/bgpd.conf @@ -0,0 +1,12 @@ +! +allow-reserved-ranges +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 240.0.0.2 remote-as external + neighbor 240.0.0.2 timers 1 3 + neighbor 240.0.0.2 timers connect 1 + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_ipv4_class_e_peer/r1/zebra.conf b/tests/topotests/bgp_ipv4_class_e_peer/r1/zebra.conf new file mode 100644 index 0000000..d4ac46f --- /dev/null +++ b/tests/topotests/bgp_ipv4_class_e_peer/r1/zebra.conf @@ -0,0 +1,11 @@ +! +allow-reserved-ranges +! +interface lo + ip address 172.16.255.1/32 +! +interface r1-eth0 + ip address 240.0.0.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ipv4_class_e_peer/r2/bgpd.conf b/tests/topotests/bgp_ipv4_class_e_peer/r2/bgpd.conf new file mode 100644 index 0000000..7d08963 --- /dev/null +++ b/tests/topotests/bgp_ipv4_class_e_peer/r2/bgpd.conf @@ -0,0 +1,9 @@ +! +allow-reserved-ranges +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 240.0.0.1 remote-as external + neighbor 240.0.0.1 timers 1 3 + neighbor 240.0.0.1 timers connect 1 +! diff --git a/tests/topotests/bgp_ipv4_class_e_peer/r2/zebra.conf b/tests/topotests/bgp_ipv4_class_e_peer/r2/zebra.conf new file mode 100644 index 0000000..f0a350f --- /dev/null +++ b/tests/topotests/bgp_ipv4_class_e_peer/r2/zebra.conf @@ -0,0 +1,8 @@ +! +allow-reserved-ranges +! +interface r2-eth0 + ip address 240.0.0.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ipv4_class_e_peer/test_bgp_ipv4_class_e_peer.py b/tests/topotests/bgp_ipv4_class_e_peer/test_bgp_ipv4_class_e_peer.py new file mode 100644 index 0000000..c7cb213 --- /dev/null +++ b/tests/topotests/bgp_ipv4_class_e_peer/test_bgp_ipv4_class_e_peer.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# bgp_ipv4_class_e_peer.py +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Check if the peering works by using IPv4 Class E IP ranges, and if +we don't treat next-hop as martian in such a case. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_ipv4_class_e_peer(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 240.0.0.1 json")) + expected = { + "240.0.0.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_next_hop_ipv4_class_e(): + output = json.loads( + router.vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json") + ) + expected = { + "paths": [ + { + "valid": True, + "nexthops": [ + { + "ip": "240.0.0.1", + "accessible": True, + } + ], + } + ] + } + return topotest.json_cmp(output, expected) + + step("Initial BGP converge") + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP convergence on R2" + + step("Check if IPv4 BGP peering works with Class E IP ranges") + test_func = functools.partial(_bgp_next_hop_ipv4_class_e) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see 172.16.255.1/32 via 240.0.0.1 on R2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json new file mode 100644 index 0000000..7f928b9 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json @@ -0,0 +1,85 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link5": {"ipv4": "auto", "ipv6": "auto"}} + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link5": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link0": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "activate": "ipv4", + "capability": "extended-nexthop" + } + } + } + } + } + } + }}}, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link0": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "200", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "activate": "ipv4", + "capability": "extended-nexthop" + } + } + }, + "r3": {"dest_link": {"r2": {}}}} + } + } + }}}, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "200", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + }}}, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}} + }}} diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json new file mode 100644 index 0000000..8e0f448 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json @@ -0,0 +1,95 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link5": {"ipv4": "auto", "ipv6": "auto"}} + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link5": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link0": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + } + } + } + } + }}}, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link0": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "200", + "default_ipv4_unicast": "False", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": {"dest_link": {"r2": {"activate": "ipv4"}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + }, + "r3": {"dest_link": {"r2": {}}}} + } + }}}}, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "300", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + }}}, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}} + }}}}} diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json new file mode 100644 index 0000000..72d3a93 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json @@ -0,0 +1,97 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link5": {"ipv4": "auto", "ipv6": "auto"}} + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link5": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link0": {"ipv4": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "activate": "ipv4", + "capability": "extended-nexthop", + "neighbor_type": "unnumbered" + } + } + } + } + } + } + }}}, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link0": {"ipv4": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "200", + "default_ipv4_unicast": "False", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": {"dest_link": {"r2": {"activate": "ipv4"}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "activate": "ipv4", + "capability": "extended-nexthop", + "neighbor_type": "unnumbered" + } + } + }, + "r3": {"dest_link": {"r2": {}}}} + } + }}}}, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "300", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + }}}, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}} + }}}}} diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json new file mode 100644 index 0000000..a7ea0c8 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json @@ -0,0 +1,95 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link5": {"ipv4": "auto", "ipv6": "auto"}} + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link5": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link0": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + } + } + } + } + }}}, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link0": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": {"dest_link": {"r2": {"activate": "ipv4"}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + }, + "r3": {"dest_link": {"r2": {}}}} + } + }}}}, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "300", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + }}}, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}} + }}}}} diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json new file mode 100644 index 0000000..5e90d6b --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json @@ -0,0 +1,97 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto"}, + "r1-link2": {"ipv4": "auto"}, + "r1-link3": {"ipv4": "auto"}, + "r1-link4": {"ipv4": "auto"}, + "r1-link5": {"ipv4": "auto"}} + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0-link1": {"ipv4": "auto"}, + "r0-link2": {"ipv4": "auto"}, + "r0-link3": {"ipv4": "auto"}, + "r0-link4": {"ipv4": "auto"}, + "r0-link5": {"ipv4": "auto"}, + "r2-link0": {"ipv4": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "neighbor_type": "unnumbered", + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + } + } + } + } + }}}, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link0": {"ipv4": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": {"dest_link": {"r2": {"activate": "ipv4"}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "neighbor_type": "unnumbered", + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + }, + "r3": {"dest_link": {"r2": {}}}} + } + }}}}, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "300", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + }}}, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto"}}, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}} + }}}}} diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py new file mode 100644 index 0000000..1873fb0 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py @@ -0,0 +1,950 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# + + +"""RFC5549 Automation.""" +import os +import sys +import time +import pytest +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + get_frr_ipv6_linklocal, + write_test_footer, + verify_rib, + create_static_routes, + check_address_types, + reset_config_on_routers, + step, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, +) +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +topo = None + +# Global variables +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +NO_OF_RTES = 2 +NETWORK_CMD_IP = "1.0.1.17/32" +ADDR_TYPES = check_address_types() +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + + +----+ + | R4 | + | | + +--+-+ + | ipv4 nbr + no bgp ebgp/ibgp | + | ebgp/ibgp + +----+ 5links +----+ 8links +--+-+ +----+ + |R0 +----------+ R1 +------------+ R2 | ipv6 nbr |R3 | + | +----------+ +------------+ +-------------+ | + +----+ +----+ ipv6 nbr +----+ +----+ +""" + +TESTCASES = """ +1. Verify Ipv4 route next hop is changed when advertised using +next hop -self command +2. Verify IPv4 route advertised to peer when IPv6 BGP session established + using peer-group +3. Verify IPv4 routes received with IPv6 nexthop are getting advertised + to another IBGP peer without changing the nexthop +4. Verify IPv4 routes advertised with correct nexthop when nexthop +unchange is configure on EBGP peers + """ + + +def setup_module(mod): + """Set up the pytest environment.""" + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/rfc5549_ebgp_ibgp_nbr.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a particular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a particular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +# ################################## +# Test cases start here. +# ################################## +def test_ibgp_to_ibgp_p1(request): + """ + + Test Capability extended nexthop. + + Verify IPv4 routes received with IPv6 nexthop are getting advertised to + another IBGP peer without changing the nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + global topo + topo23 = deepcopy(topo) + build_config_from_json(tgen, topo23, save_bkup=False) + + step("Configure IPv6 EBGP session between R1 and R2 with " "global IPv6 address") + step("Configure IPv6 IBGP session betn R2 & R3 using IPv6 global address") + step("Enable capability extended-nexthop on both the IPv6 BGP peers") + step("Activate same IPv6 nbr from IPv4 unicast family") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + + # verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family. + bgp_convergence = verify_bgp_convergence(tgen, topo23) + assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format( + bgp_convergence + ) + + step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo23, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + gllip = get_llip("r1", "r2-link0") + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "activate": "ipv4", + "capability": "extended-nexthop", + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r3 = { + "r3": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "activate": "ipv4", + "capability": "extended-nexthop", + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 routes installed on R3 with global address without " + "changing the nexthop ( nexthop should IPv6 link local which is" + " received from R1)" + ) + gipv6 = get_glipv6("r1", "r2-link0") + dut = "r3" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gipv6, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gipv6 + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + write_test_footer(tc_name) + + +def test_ext_nh_cap_red_static_network_ibgp_peer_p1(request): + """ + + Test Extended capability next hop, with ibgp peer. + + Verify IPv4 routes advertise using "redistribute static" and + "network command" are received on EBGP peer with IPv6 nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step( + " Configure IPv6 EBGP session between R1 & R2 with global IPv6 address" + " Enable capability extended-nexthop on the nbr from both the routers" + " Activate same IPv6 nbr from IPv4 unicast family" + ) + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "capability": "extended-nexthop", + "activate": "ipv4", + "next_hop_self": True, + "activate": "ipv4", + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r3 = { + "r3": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "capability": "extended-nexthop", + "activate": "ipv4", + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "default_ipv4_unicast": "False", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + gllip = get_llip("r1", "r2-link0") + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": gllip, + } + ] + } + } + + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + gllip = get_glipv6("r2", "r3") + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r3" + protocol = "bgp" + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": gllip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=gllip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_bgp_peer_group_p1(request): + """ + Test extended capability next hop with peer groups. + + Verify IPv4 routes received with IPv6 nexthop are getting advertised to + another IBGP peer without changing the nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + global topo + topo1 = deepcopy(topo) + step("Configure IPv6 EBGP session between R1 and R2 with " "global IPv6 address") + step("Configure IPv6 IBGP session betn R2 & R3 using IPv6 global address") + step("Enable capability extended-nexthop on both the IPv6 BGP peers") + step("Activate same IPv6 nbr from IPv4 unicast family") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "default_ipv4_unicast": "False", + "peer-group": { + "rfc5549": {"capability": "extended-nexthop", "remote-as": "200"} + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "activate": "ipv4", + "capability": "extended-nexthop", + "peer-group": "rfc5549", + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "default_ipv4_unicast": "False", + "peer-group": { + "rfc5549": {"capability": "extended-nexthop", "remote-as": "100"} + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "capability": "extended-nexthop", + "activate": "ipv4", + "peer-group": "rfc5549", + } + } + }, + "r3": {"dest_link": {"r2": {}}}, + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r3 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format( + bgp_convergence + ) + + step(" Configure 2 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + gllip = get_llip("r1", "r2-link0") + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "default_ipv4_unicast": "False", + "peer-group": { + "rfc5549": {"capability": "extended-nexthop", "remote-as": "200"} + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "activate": "ipv4", + "capability": "extended-nexthop", + "peer-group": "rfc5549", + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "default_ipv4_unicast": "False", + "peer-group": { + "rfc5549": {"capability": "extended-nexthop", "remote-as": "100"} + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "capability": "extended-nexthop", + "activate": "ipv4", + "peer-group": "rfc5549", + } + } + }, + "r3": {"dest_link": {"r2": {}}}, + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r3 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format( + bgp_convergence + ) + + step(" Configure 2 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + gllip = get_llip("r1", "r2-link0") + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py new file mode 100644 index 0000000..47cc378 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py @@ -0,0 +1,617 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# + + +"""RFC5549 Automation.""" +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + get_frr_ipv6_linklocal, + verify_rib, + create_static_routes, + check_address_types, + reset_config_on_routers, + step, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +topo = None + +# Global variables +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +NO_OF_RTES = 2 +NETWORK_CMD_IP = "1.0.1.17/32" +ADDR_TYPES = check_address_types() +BGP_CONVERGENCE_TIMEOUT = 10 +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + +----+ + | R4 | + | | + +--+-+ + | ipv4 nbr + no bgp ebgp | + | ebgp/ibgp + +----+ 5links +----+ +--+-+ +----+ + |R0 +----------+ R1 | | R2 | ipv6 nbr |R3 | + | +----------+ +------------+ +-------------+ | + +----+ +----+ ipv6 nbr +----+ +----+ +""" + +TESTCASES = """ +TC6. Verify BGP speaker advertise IPv4 route to peer only if "extended + nexthop capability" is negotiated +TC7. Verify ipv4 route nexthop updated dynamically when in route-map is + applied on receiving BGP peer +TC8. Verify IPv4 routes advertise using "redistribute static" and "network + command" are received on EBGP peer with IPv6 nexthop +TC10. Verify IPv4 routes are deleted after un-configuring of "network +command" and "redistribute static knob" +TC18. Verify IPv4 routes installed with correct nexthop after deactivate + and activate neighbor from address family +TC19. Verify IPv4 route ping is working fine and nexhop installed in kernel + as IPv4 link-local address +TC24. Verify IPv4 prefix-list routes advertised to peer when prefix -list + applied in out direction +TC27. Verify IPv4 routes are intact after BGPd process restart +TC30. Verify Ipv4 route installed with correct next hop when same route + is advertised via IPV4 and IPv6 BGP peers +TC32. Verify IPv4 route received with IPv6 nexthop can be advertised to + another IPv4 BGP peers + """ + + +def setup_module(mod): + """Set up the pytest environment.""" + global topo, ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/rfc5549_ebgp_nbr.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment.""" + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a particular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a particular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ext_nh_cap_red_static_network_ebgp_peer_tc8_p0(request): + """ + + Test exted capability nexthop with route map in. + + Verify IPv4 routes advertise using "redistribute static" and + "network command" are received on EBGP peer with IPv6 nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step("Configure IPv6 EBGP session between R1 and R2 with global" " IPv6 address") + reset_config_on_routers(tgen) + + step( + "Enable capability extended-nexthop on the nbr from both the " + " routers Activate same IPv6 nbr from IPv4 unicast family" + ) + step( + " Configure 2 IPv4 static " + "routes on R1 (nexthop for static route exists on different " + "link of R0" + ) + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv6"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv6"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "True", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + }, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + glip = get_llip("r1", "r2-link0") + assert glip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 and IPv6 routes advertised using static and network command " + "are received on R2 BGP & routing table , verify using show ip bgp " + "show ip route for IPv4 routes and show bgp ipv6,show ipv6 routes " + "for IPv6 routes ." + ) + + dut = "r2" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type][0], + "no_of_ip": 2, + "next_hop": glip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, addr_type, dut, verify_nh_for_static_rtes, next_hop=glip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_rib + ) + result = verify_rib( + tgen, + addr_type, + dut, + verify_nh_for_static_rtes, + next_hop=glip, + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify IPv4 routes are installed with IPv6 global nexthop of R1" + " R1 to R2 connected link" + ) + + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": glip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + +def test_ext_nh_cap_remove_red_static_network_ebgp_peer_tc10_p1(request): + """ + + Test exted capability nexthop with route map in. + + Verify IPv4 routes are deleted after un-configuring of + network command and redistribute static knob + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step( + "Configure IPv6 EBGP session between R1 and R2 with global IPv6" + " address Enable capability extended-nexthop on the nbr from both" + " the routers , Activate same IPv6 nbr from IPv4 unicast family" + ) + step( + " Configure 2 IPv4 static routes " + " on R1 nexthop for static route exists on different link of R0" + ) + reset_config_on_routers(tgen) + + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Advertise static routes from IPv4 unicast family and IPv6 unicast" + " family respectively from R1. Configure loopback on R1 with IPv4 " + "address Advertise loobak from IPv4 unicast family using network " + "command from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "True", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 and IPv6 routes advertised using static and network command are" + " received on R2 BGP and routing table , verify using show ip bgp" + " show ip route for IPv4 routes and show bgp, show ipv6 routes" + " for IPv6 routes ." + ) + + glipv6 = get_llip("r1", "r2-link0") + assert glipv6 is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "advertise_networks": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": get_glipv6, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=get_glipv6 + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, + "ipv4", + dut, + verify_nh_for_static_rtes, + next_hop=get_glipv6, + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify IPv4 routes are installed with IPv6 global nexthop of R1 " + " R1 to R2 connected link" + ) + verify_nh_for_nw_cmd_rtes = { + "r1": { + "advertise_networks": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": glipv6, + } + ] + } + } + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glipv6, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + "ipv6": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": glipv6, + } + ] + } + } + + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glipv6, expected=False + ) + assert ( + bgp_rib is not True + ), "Testcase {} : Failed \n Error: Routes still" " present in BGP rib".format( + tc_name + ) + result = verify_rib( + tgen, + "ipv4", + dut, + verify_nh_for_static_rtes, + next_hop=glipv6, + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: Routes " "still present in RIB".format(tc_name) + + step( + "After removing IPv4 routes from redistribute static those routes" + " are removed from R2, after re-advertising routes which are " + " advertised using network are still present in the on R2 with " + " IPv6 global nexthop, verify using show ip bgp and show ip routes" + ) + + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": glipv6, + } + ] + } + } + + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glipv6, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": NETWORK_CMD_IP, + "no_of_network": 1, + "delete": True, + } + ] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_rib( + tgen, + "ipv4", + dut, + verify_nh_for_nw_cmd_rtes, + next_hop=glipv6, + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Error: Routes still present in BGP rib".format( + tc_name + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py new file mode 100644 index 0000000..395ef2d --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py @@ -0,0 +1,766 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# + + +"""RFC5549 Automation.""" +import os +import sys +import time +import pytest +import functools +import json + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + + +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +from lib.common_config import ( + write_test_header, + start_topology, + write_test_footer, + start_router, + stop_router, + verify_rib, + create_static_routes, + check_address_types, + reset_config_on_routers, + step, + get_frr_ipv6_linklocal, +) +from lib.topolog import logger +from lib.bgp import create_router_bgp, verify_bgp_convergence, verify_bgp_rib + +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +topo = None + +# Global variables +NO_OF_RTES = 2 +NETWORK_CMD_IP = "1.0.1.17/32" +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +INTF_LIST = [ + "r2-link0", + "r2-link1", + "r2-link2", + "r2-link3", + "r2-link4", + "r2-link5", + "r2-link6", + "r2-link7", +] +ADDR_TYPES = check_address_types() +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + + +----+ + | R4 | + | | + +--+-+ + | ipv4 nbr + no bgp ebgp/ibgp | + | ebgp/ibgp + +----+ 5links +----+ 8links +--+-+ +----+ + |R0 +----------+ R1 +------------+ R2 | ipv6 nbr |R3 | + | +----------+ +------------+ +-------------+ | + +----+ +----+ ipv6 nbr +----+ +----+ +""" + +TESTCASES = """ +1. Verify IPv4 routes are advertised when IPv6 EBGP loopback session + established using Unnumbered interface +2. Verify IPv4 routes are installed with correct nexthop after +shut / no shut of nexthop and BGP peer interfaces +3. Verify IPv4 routes are intact after stop and start the FRR services + """ + + +def setup_module(mod): + """Set up the pytest environment.""" + global topo, ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/rfc5549_ebgp_unnumbered_nbr.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment.""" + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a particular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a particular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +# ################################## +# Test cases start here. +# ################################## + + +def test_unnumbered_loopback_ebgp_nbr_p0(request): + """ + + Test extended capability nexthop with un numbered ebgp. + + Verify IPv4 routes are advertised when IPv6 EBGP loopback + session established using Unnumbered interface + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + + step("Configure IPv6 EBGP Unnumbered session between R1 and R2") + step("Enable capability extended-nexthop on both the IPv6 BGP peers") + step("Activate same IPv6 nbr from IPv4 unicast family") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + }, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + llip = get_llip("r1", "r2-link0") + assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, llip) + + dut = "r2" + protocol = "bgp" + for rte in range(0, NO_OF_RTES): + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK["ipv4"][rte], "no_of_ip": 1, "next_hop": llip} + ] + } + } + """ interface_list = ['r1-link0','r1-link1'] + nh_list =[] + for i in range(NO_OF_RTES): + nh_list.append(topo['routers']['r2']['links'][i][ + 'interface']) """ + bgp_rib = verify_rib( + tgen, + "ipv4", + dut, + # verify_nh_for_static_rtes, next_hop='r2-r1-eth0') + verify_nh_for_static_rtes, + next_hop=llip, + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_rib + ) + result = verify_rib( + tgen, + "ipv4", + dut, + verify_nh_for_static_rtes, + next_hop=llip, + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # verify the routes with nh as ext_nh + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip} + ] + } + } + + bgp_rib = verify_rib( + tgen, + "ipv4", + dut, + # verify_nh_for_nw_rtes, next_hop='r2-r1-eth0') + verify_nh_for_nw_rtes, + next_hop=llip, + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + # stop/start -> restart FRR router and verify + stop_router(tgen, "r1") + stop_router(tgen, "r2") + start_router(tgen, "r1") + start_router(tgen, "r2") + step( + "After stop/start of FRR services , verify session up and routes " + "came up fine ,nh is proper using show bgp & show ipv6 route on R2 " + ) + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + llip = get_llip("r1", "r2-link0") + assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": llip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, + "ipv4", + dut, + # verify_nh_for_static_rtes, next_hop='r2-r1-eth0') + verify_nh_for_static_rtes, + next_hop=llip, + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip} + ] + } + } + bgp_rib = verify_rib( + tgen, + "ipv4", + dut, + # verify_nh_for_nw_rtes, next_hop='r2-r1-eth0') + verify_nh_for_nw_rtes, + next_hop=llip, + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + write_test_footer(tc_name) + + +def test_restart_frr_p2(request): + """ + + Test extended capability nexthop , restart frr. + + Verify IPv4 routes are intact after stop and start the FRR services + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step("Configure IPv6 EBGP Unnumbered session between R1 and R2") + step("Enable capability extended-nexthop on both the IPv6 BGP peers") + step("Activate same IPv6 nbr from IPv4 unicast family") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + }, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + llip = get_llip("r1", "r2-link0") + assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": llip, + } + ] + } + } + bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip} + ] + } + } + + bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # stop/start -> restart FRR router and verify + stop_router(tgen, "r1") + stop_router(tgen, "r2") + start_router(tgen, "r1") + start_router(tgen, "r2") + + step( + "After stop/start of FRR services , verify session up and routes " + "came up fine ,nh is proper using show bgp & show ipv6 route on R2 " + ) + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + llip = get_llip("r1", "r2-link0") + assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "next_hop": llip} + ] + } + } + bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # verify the routes with nh as ext_nh + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip} + ] + } + } + bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + write_test_footer(tc_name) + + +def test_configure_gua_on_unnumbered_intf(request): + """ + Configure a global V6 address on an unnumbered interface on R1 + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + + step("Configure IPv6 EBGP Unnumbered session between R1 and R2") + step("Enable capability extended-nexthop on both the IPv6 BGP peers") + step("Activate same IPv6 nbr from IPv4 unicast family") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + }, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + r2 = tgen.gears["r2"] + + def bgp_prefix_received_gua_nh(router): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast 11.0.20.1/32 json")) + expected = { + "prefix": "11.0.20.1/32", + "paths": [ + { + "nexthops": [ + { + "ip": "5001:dead:beef::1", + "hostname": "r1", + "afi": "ipv6", + "scope": "global", + } + ] + } + ], + } + return topotest.json_cmp(output, expected) + + def bgp_prefix_received_v4_mapped_v6_nh(router): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast 11.0.20.1/32 json")) + expected = { + "prefix": "11.0.20.1/32", + "paths": [ + { + "nexthops": [ + { + "ip": "::ffff:a00:501", + "hostname": "r1", + "afi": "ipv6", + "scope": "global", + } + ] + } + ], + } + return topotest.json_cmp(output, expected) + + step("Configure a global V6 address on an unnumbered interface on R1") + output = tgen.gears["r1"].vtysh_cmd( + """ + configure terminal + interface r1-r2-eth5 + ipv6 address 5001:dead:beef::1/126 + ! + """ + ) + + # verify that r2 has received prefix with GUA as nexthop + test_func = functools.partial(bgp_prefix_received_gua_nh, r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \ + is not 5001:dead:beef::1".format( + tc_name + ) + + step("Configure a secondary global V6 address on an unnumbered interface on R1") + output = tgen.gears["r1"].vtysh_cmd( + """ + configure terminal + interface r1-r2-eth5 + ipv6 address 7771:dead:beef::1/126 + ! + """ + ) + # verify that r1 did not readvertise the prefix with secondary V6 address as the nexthop + test_func = functools.partial(bgp_prefix_received_gua_nh, r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \ + is not 5001:dead:beef::1".format( + tc_name + ) + + step("Unconfigure the secondary global V6 address from unnumbered interface on R1") + output = tgen.gears["r1"].vtysh_cmd( + """ + configure terminal + interface r1-r2-eth5 + no ipv6 address 7771:dead:beef::1/126 + ! + """ + ) + # verify that r1 still has the prefix with primary GUA as the nexthop + test_func = functools.partial(bgp_prefix_received_gua_nh, r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \ + is not 5001:dead:beef::1".format( + tc_name + ) + + step("Unconfigure the primary global V6 address from unnumbered interface on R1") + output = tgen.gears["r1"].vtysh_cmd( + """ + configure terminal + interface r1-r2-eth5 + no ipv6 address 5001:dead:beef::1/126 + ! + """ + ) + # verify that r1 has rcvd the prefix with v4-mapped-v6 address as the nexthop + test_func = functools.partial(bgp_prefix_received_v4_mapped_v6_nh, r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \ + is not ::ffff:a00:501".format( + tc_name + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py new file mode 100644 index 0000000..56152b9 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py @@ -0,0 +1,975 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# + + +"""RFC5549 Automation.""" +import os +import sys +import time +import pytest +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + addKernelRoute, + write_test_footer, + create_prefix_lists, + verify_rib, + create_static_routes, + reset_config_on_routers, + step, + create_route_maps, + get_frr_ipv6_linklocal, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +topo = None +# Global variables +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +NETWORK_CMD_IP = "1.0.1.17/32" +NO_OF_RTES = 2 +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + + +----+ + | R4 | + | | + +--+-+ + | ipv4 nbr + no bgp ebgp/ibgp | + | ebgp/ibgp + +----+ 5links +----+ +--+-+ +----+ + |R0 +----------+ R1 | | R2 | ipv6 nbr |R3 | + | +----------+ +------------+ +-------------+ | + +----+ +----+ ipv6 nbr +----+ +----+ +""" + +TESTCASES = """ +1. Verify IPv4 and IPv6 routes advertise using "redistribute static" + and "network command" are received on IBGP peer with IPv6 nexthop +2. Verify IPv4 routes are advertised and withdrawn when IPv6 IBGP session + established using loopback interface +3. Verify IPv4 routes are advertised to peer when static routes are + configured with ADMIN distance and tag option +4. Verify IPv4 routes advertised to peer when BGP session established + using link-local address + """ + + +def setup_module(mod): + """Set up the pytest environment.""" + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/rfc5549_ibgp_nbr.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment.""" + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a particular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a particular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ext_nh_cap_red_static_network_ibgp_peer_p1(request): + """ + + Test extended capability nexthop with ibgp peer. + + Verify IPv4 and IPv6 routes advertise using "redistribute static" + and "network command" are received on IBGP peer with IPv6 nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step( + "Configure IPv6 EBGP session between R1 and R2 with global IPv6" + " address Enable capability extended-nexthop on the nbr from both" + " the routers" + ) + step( + "Change ebgp to ibgp nbrs between r1 and r2 , Activate same IPv6" + " nbr from IPv4 unicast family " + ) + + step( + " Configure 5 IPv4 static routes" + " on R1 nexthop for static route exists on different link of R0" + ) + + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Advertise static routes from IPv4 unicast family and IPv6 unicast" + " family respectively from R1.Configure loopback on R1 with IPv4 addr" + " & Advertise loopback from IPv4 unicast family using network cmd " + " from R1" + ) + # this test case needs ipv6 routes to be configured + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + }, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + glip = get_llip("r1", "r2-link0") + assert glip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 and IPv6 routes advertised using static & network command are" + "received on R2 BGP and routing table , verify using show ip bgp" + "show ip route for IPv4 routes and show bgp, show ipv6 routes" + "for IPv6 routes ." + ) + + dut = "r2" + protocol = "bgp" + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": glip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify IPv4 routes are installed with IPv6 global nexthop of R1" + "R1 to R2 connected link" + ) + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": glip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ext_nh_cap_admin_dist_tag_ibgp_peer_p1(request): + """ + + Test extended capability nexthop with admin distance and route tag. + + Verify IPv4 routes are advertised to peer when static routes + are configured with ADMIN distance and tag option + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step( + "Configure IPv6 EBGP session between R1 and R2 with global IPv6" + " address Enable capability extended-nexthop on the nbr from both" + " the routers" + ) + step( + "Change ebgp to ibgp nbrs between r1 and r2 , Activate same IPv6" + " nbr from IPv4 unicast family " + ) + step( + " Configure 5 IPv4 static routes" + " on R1 nexthop for static route exists on different link of R0" + ) + count = 0 + for rte in range(0, NO_OF_RTES): + count += 1 + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + "admin_distance": 100 + count, + "tag": 4001 + count, + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Advertise static routes from IPv4 unicast family & IPv6 unicast" + " family respectively from R1.Configure loopback on R1 with IPv4 " + "address & Advertise loopback from IPv4 unicast family " + "using network cmd from R1" + ) + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}} + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + glip = get_llip("r1", "r2-link0") + assert glip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 and IPv6 routes advertised using static & network cmd are" + "received on R2 BGP and routing table , verify using show ip bgp" + "show ip route for IPv4 routes and show bgp, show ipv6 routes" + "for IPv6 routes ." + ) + + dut = "r2" + protocol = "bgp" + count = 0 + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": glip, + "admin_distance": 100 + count, + "tag": 4001 + count, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + count = 0 + for rte in range(0, NO_OF_RTES): + count += 10 + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + { + "seqid": 0 + count, + "action": "permit", + "network": NETWORK["ipv4"][rte], + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n " "Error: {}".format( + tc_name, result + ) + + # Create route map + input_dict_6 = { + "r3": { + "route_maps": { + "rmap_match_tag_1_{}".format("ipv4"): [ + { + "action": "deny", + "match": { + "ipv4": {"prefix_lists": "pf_list_1_{}".format("ipv4")} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_7 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_ibgp_loopback_nbr_p1(request): + """ + Verify Extended capability nexthop with loopback interface. + + Verify IPv4 routes are advertised and withdrawn when IPv6 IBGP + session established using loopback interface + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + global topo + topo1 = deepcopy(topo) + reset_config_on_routers(tgen) + step("Configure IPv6 global address between R1 and R2") + step( + "Configure loopback on R1 and R2 and establish EBGP session " + "between R1 and R2 over loopback global ip" + ) + step("Configure static route on R1 and R2 for loopback reachability") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + + for routerN in ["r1", "r2"]: + for addr_type in ["ipv6"]: + for bgp_neighbor in topo1["routers"][routerN]["bgp"]["address_family"][ + addr_type + ]["unicast"]["neighbor"].keys(): + # Adding ['source_link'] = 'lo' key:value pair + if bgp_neighbor == "r1" or bgp_neighbor == "r2": + topo1["routers"][routerN]["bgp"]["address_family"][addr_type][ + "unicast" + ]["neighbor"][bgp_neighbor]["dest_link"] = { + "lo": { + "source_link": "lo", + "ebgp_multihop": 2, + "capability": "extended-nexthop", + "activate": "ipv4", + } + } + # Creating configuration from JSON + build_config_from_json(tgen, topo1, save_bkup=False) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": {"r1-link0": {"deactivate": "ipv6"}} + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": {"r2-link0": {"deactivate": "ipv6"}} + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": {"r1-link0": {"deactivate": "ipv4"}} + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": {"r2-link0": {"deactivate": "ipv4"}} + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + r2_lo_v4 = topo["routers"]["r2"]["links"]["lo"]["ipv4"] + r2_lo_v6 = topo["routers"]["r2"]["links"]["lo"]["ipv6"] + r1_lo_v4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"] + r1_lo_v6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"] + r1_r2_intf = topo["routers"]["r1"]["links"]["r2-link0"]["interface"] + r2_r1_intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"] + + r1_r2_v6_nh = topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0] + r2_r1_v6_nh = topo["routers"]["r2"]["links"]["r1-link0"]["ipv6"].split("/")[0] + + ipv4_list = [("r1", r1_r2_intf, [r2_lo_v4]), ("r2", r2_r1_intf, [r1_lo_v4])] + + ipv6_list = [ + ("r1", r1_r2_intf, [r2_lo_v6], r2_r1_v6_nh), + ("r2", r2_r1_intf, [r1_lo_v6], r1_r2_v6_nh), + ] + + for dut, intf, loop_addr in ipv4_list: + result = addKernelRoute(tgen, dut, intf, loop_addr) + # assert result is True, "Testcase {}:Failed \n Error: {}". \ + # format(tc_name, result) + + for dut, intf, loop_addr, next_hop in ipv6_list: + result = addKernelRoute(tgen, dut, intf, loop_addr, next_hop) + # assert result is True, "Testcase {}:Failed \n Error: {}". \ + # format(tc_name, result) + + r2_lo_v4 = topo["routers"]["r2"]["links"]["lo"]["ipv4"] + r2_lo_v6 = topo["routers"]["r2"]["links"]["lo"]["ipv6"] + r1_lo_v4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"] + r1_lo_v6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"] + r1_r2_intf = topo["routers"]["r1"]["links"]["r2-link0"]["interface"] + r2_r1_intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"] + + r1_r2_v6_nh = topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0] + r2_r1_v6_nh = topo["routers"]["r2"]["links"]["r1-link0"]["ipv6"].split("/")[0] + + r1_r2_v4_nh = topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0] + r2_r1_v4_nh = topo["routers"]["r2"]["links"]["r1-link0"]["ipv4"].split("/")[0] + + input_dict = { + "r1": { + "static_routes": [ + {"network": r2_lo_v4, "next_hop": r2_r1_v4_nh}, + {"network": r2_lo_v6, "next_hop": r2_r1_v6_nh}, + ] + }, + "r2": { + "static_routes": [ + {"network": r1_lo_v4, "next_hop": r1_r2_v4_nh}, + {"network": r1_lo_v6, "next_hop": r1_r2_v6_nh}, + ] + }, + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Api call verify whether BGP is converged + result = verify_bgp_convergence(tgen, topo1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + configure_bgp_on_r1 = { + "r1": { + "default_ipv4_unicast": False, + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "lo": { + "activate": "ipv4", + "capability": "extended-nexthop", + } + } + } + } + } + } + } + }, + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r2 = { + "r2": { + "default_ipv4_unicast": False, + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "lo": { + "activate": "ipv4", + "capability": "extended-nexthop", + } + } + } + } + } + } + } + }, + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp convergence.") + bgp_convergence = verify_bgp_convergence(tgen, topo1) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step("Configure 2 IPv4 static" " routes on R1, Nexthop as different links of R0") + + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + gllip = (topo1["routers"]["r1"]["links"]["lo"]["ipv6"].split("/")[0]).lower() + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": gllip} + ] + } + } + bgp_rib = verify_bgp_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Remove IPv4 routes advertised using network command" + " from R1 and advertise again" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + { + "network": NETWORK_CMD_IP, + "no_of_network": 1, + "delete": True, + } + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + { + "network": NETWORK_CMD_IP, + "no_of_network": 1, + } + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "After removing IPv4 routes from network command , routes which are " + "advertised using redistribute static are still present in the on " + "R2 , verify using show ip bgp and show ip route" + ) + + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": gllip} + ] + } + } + bgp_rib = verify_bgp_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Remove IPv4 routes advertised using redistribute static" + " command from R1 and advertise again" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}} + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "After removing IPv4 routes from redistribute static , routes which" + " are advertised using network are still present in the on R2 , " + "verify using show ip bgp and show ip route" + ) + + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": gllip} + ] + } + } + bgp_rib = verify_bgp_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py new file mode 100644 index 0000000..526b267 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py @@ -0,0 +1,311 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# + + +"""RFC5549 Automation.""" +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + verify_rib, + create_static_routes, + check_address_types, + step, + reset_config_on_routers, + get_frr_ipv6_linklocal, +) +from lib.topolog import logger +from lib.bgp import create_router_bgp, verify_bgp_convergence +from lib.topojson import build_config_from_json + +# Global variables +topo = None + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK_CMD_IP = "1.0.1.17/32" +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +ADDR_TYPES = check_address_types() +NO_OF_RTES = 2 +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + +----+ + | R4 | + | | + +--+-+ + | ipv4 nbr + no bgp ebgp/ibgp | + | ebgp/ibgp + +----+ 2links +----+ 8links +--+-+ +----+ + |R0 +----------+ R1 + + R2 | ipv6 nbr |R3 | + | +----------+ +------------+ +-------------+ | + +----+ +----+ ipv6 nbr +----+ +----+ +""" + +TESTCASES = """ +1. Verify IPv4 routes are deleted after un-configuring "network command +" and "redistribute static knob" with Unnumbered IPv6 IBGP session + """ + + +def setup_module(mod): + """Set up the pytest environment.""" + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/rfc5549_ibgp_unnumbered_nbr.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment.""" + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a particular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a particular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ext_nh_cap_red_static_network_ebgp_peer_unnumbered_nbr_p1(request): + """ + + Test extended capability nexthop. + + Verify IPv4 routes advertise using "redistribute static" and + "network command" are received on EBGP peer with IPv6 nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step( + "Configure IPv6 IBGP Unnumbered session between R1 and R2 and enable " + "ipv6 nd ra-interval 10 in the interface" + ) + + step( + "Enable capability extended-nexthop" + "on the neighbor from both the routers and " + "ipv6 nd ra-interval 10 on link connected between R1 and R2" + ) + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format( + bgp_convergence + ) + + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Advertise static routes from IPv4 unicast family and IPv6 unicast " + "family respectively from R1 " + "Configure loopback on R1 with IPv4 address Advertise loopback " + "from IPv4 unicast family using network cmd from R1 " + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + llip = get_llip("r1", "r2-link0") + assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + " IPv4 and IPv6 routes advertised using static and network command are" + " received on R2 BGP and routing table , verify using show ip bgp" + " show ip route for IPv4 routes and show bgp show ipv6 routes" + " for IPv6 routes ." + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": llip, + } + ] + } + } + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": llip, + } + ] + } + } + + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv6_ll_peering/__init__.py b/tests/topotests/bgp_ipv6_ll_peering/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf b/tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf new file mode 100644 index 0000000..724cbf8 --- /dev/null +++ b/tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65001 + bgp router-id 10.0.0.1 + no bgp ebgp-requires-policy + neighbor fe80:1::2 remote-as external + neighbor fe80:1::2 timers 3 10 + neighbor fe80:1::2 interface r1-eth0 diff --git a/tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf b/tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf new file mode 100644 index 0000000..4e93d4f --- /dev/null +++ b/tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf @@ -0,0 +1,4 @@ +! +interface r1-eth0 + ipv6 address fe80:1::1/64 +! diff --git a/tests/topotests/bgp_ipv6_ll_peering/r2/bgpd.conf b/tests/topotests/bgp_ipv6_ll_peering/r2/bgpd.conf new file mode 100644 index 0000000..44f79df --- /dev/null +++ b/tests/topotests/bgp_ipv6_ll_peering/r2/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65002 + bgp router-id 10.0.0.2 + no bgp ebgp-requires-policy + neighbor fe80:1::1 remote-as external + neighbor fe80:1::1 timers 3 10 + neighbor fe80:1::1 interface r2-eth0 diff --git a/tests/topotests/bgp_ipv6_ll_peering/r2/zebra.conf b/tests/topotests/bgp_ipv6_ll_peering/r2/zebra.conf new file mode 100644 index 0000000..1e703cd --- /dev/null +++ b/tests/topotests/bgp_ipv6_ll_peering/r2/zebra.conf @@ -0,0 +1,4 @@ +! +interface r2-eth0 + ipv6 address fe80:1::2/64 +! diff --git a/tests/topotests/bgp_ipv6_ll_peering/test_bgp_ipv6_ll_peering.py b/tests/topotests/bgp_ipv6_ll_peering/test_bgp_ipv6_ll_peering.py new file mode 100644 index 0000000..ea974b5 --- /dev/null +++ b/tests/topotests/bgp_ipv6_ll_peering/test_bgp_ipv6_ll_peering.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Check if IPv6 Link-Local BGP peering works fine. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_ipv6_link_local_peering(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp summary json")) + expected = { + "ipv4Unicast": { + "peers": { + "fe80:1::2": { + "state": "Established", + } + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP convergence on R2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv6_rtadv/__init__.py b/tests/topotests/bgp_ipv6_rtadv/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf b/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf new file mode 100644 index 0000000..0918796 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 101 + bgp router-id 10.254.254.1 + no bgp ebgp-requires-policy + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r1-eth0 interface peer-group r2g + neighbor r1-eth0 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json b/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json new file mode 100644 index 0000000..73c7fbb --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json @@ -0,0 +1,40 @@ +{ + "10.254.254.2/32": [ + { + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.2/32", + "nexthops": [ + { + "interfaceName": "r1-eth0", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ], + "10.254.254.1/32": [ + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.1/32", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "lo", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json b/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json new file mode 100644 index 0000000..a50bffd --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json @@ -0,0 +1,34 @@ +{ + "2001:db8:1::/64": [ + { + "distance": 20, + "protocol": "bgp", + "metric": 0, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "interfaceName": "r1-eth0", + "active": true, + "afi": "ipv6" + } + ] + }, + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r1-eth0", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf b/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf new file mode 100644 index 0000000..1397c7f --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf @@ -0,0 +1,9 @@ +! debug zebra packet recv +! debug zebra packet send +log stdout +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf b/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf new file mode 100644 index 0000000..55d4856 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 102 + bgp router-id 10.254.254.2 + no bgp ebgp-requires-policy + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r2-eth0 interface peer-group r2g + neighbor r2-eth0 timers 3 10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json b/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json new file mode 100644 index 0000000..d4b1410 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json @@ -0,0 +1,40 @@ +{ + "10.254.254.2/32": [ + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.2/32", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "lo", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.1/32", + "nexthops": [ + { + "interfaceName": "r2-eth0", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json b/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json new file mode 100644 index 0000000..e7bfb99 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json @@ -0,0 +1,21 @@ +{ + "2001:db8:1::/64": [ + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r2-eth0", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf b/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf new file mode 100644 index 0000000..0131a11 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf @@ -0,0 +1,9 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot new file mode 100644 index 0000000..da67c29 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot @@ -0,0 +1,44 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + +} diff --git a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py new file mode 100644 index 0000000..045ac91 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_ipv6_rtadv.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# + +""" + test_bgp_ipv6_rtadv.py: Test the FRR BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 2 routers. + tgen.add_router("r1") + tgen.add_router("r2") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv4 routing tables. + logger.info("Checking IPv4 routes for convergence") + for router in tgen.routers().values(): + json_file = "{}/{}/ipv4_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip route json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check IPv6 routing tables. + logger.info("Checking IPv6 routes for convergence") + for router in tgen.routers().values(): + json_file = "{}/{}/ipv6_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ipv6 route json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/__init__.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf new file mode 100644 index 0000000..8b88534 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf @@ -0,0 +1,32 @@ +frr defaults traditional +! +hostname ce1 +password zebra +log stdout notifications +log commands +router bgp 5226 + no bgp network import-check + bgp router-id 99.0.0.1 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as 5226 + neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 + address-family ipv4 unicast + network 5.1.0.0/24 route-map rm-nh + network 5.1.1.0/24 route-map rm-nh + neighbor 192.168.1.1 activate + exit-address-family +! +access-list al-any permit any +! +route-map rm-nh permit 10 + match ip address al-any + set ip next-hop 99.0.0.1 + set local-preference 123 + set metric 98 + set large-community 12:34:56 + set extcommunity rt 89:123 + set community 0:67 +! + +end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/zebra.conf new file mode 100644 index 0000000..46831bb --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname ce1 +! +interface lo + ip address 99.0.0.1/32 +! +interface ce1-eth0 + description to r1 + ip address 192.168.1.2/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf new file mode 100644 index 0000000..2accaae --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf @@ -0,0 +1,32 @@ +frr defaults traditional +! +hostname ce2 +password zebra +log stdout notifications +log commands +router bgp 5226 + no bgp network import-check + bgp router-id 99.0.0.2 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as 5226 + neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 + address-family ipv4 unicast + network 5.1.0.0/24 route-map rm-nh + network 5.1.1.0/24 route-map rm-nh + neighbor 192.168.1.1 activate + exit-address-family +! +access-list al-any permit any +! +route-map rm-nh permit 10 + match ip address al-any + set ip next-hop 99.0.0.2 + set local-preference 100 + set metric 100 + set large-community 12:34:56 + set extcommunity rt 89:123 + set community 0:67 +! + +end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/zebra.conf new file mode 100644 index 0000000..fb4d8cc --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname ce2 +! +interface lo + ip address 99.0.0.2/32 +! +interface ce2-eth0 + description to r3 + ip address 192.168.1.2/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf new file mode 100644 index 0000000..42eff1b --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf @@ -0,0 +1,32 @@ +frr defaults traditional +! +hostname ce3 +password zebra +log stdout notifications +log commands +router bgp 5226 + no bgp network import-check + bgp router-id 99.0.0.3 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as 5226 + neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 + address-family ipv4 unicast + network 5.1.2.0/24 route-map rm-nh + network 5.1.3.0/24 route-map rm-nh + neighbor 192.168.1.1 activate + exit-address-family +! +access-list al-any permit any +! +route-map rm-nh permit 10 + match ip address al-any + set ip next-hop 99.0.0.3 + set local-preference 50 + set metric 200 + set large-community 12:34:56 + set extcommunity rt 89:123 + set community 0:67 +! + +end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/zebra.conf new file mode 100644 index 0000000..77a1163 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname ce3 +! +interface lo + ip address 99.0.0.3/32 +! +interface ce3-eth0 + description to r4 + ip address 192.168.1.2/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/customize.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/customize.py new file mode 100644 index 0000000..41114b6 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/customize.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright (c) 2017 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +r""" +customize.py: Simple FRR MPLS L3VPN test topology + + | + +----+----+ + | ce1 | + | 99.0.0.1| CE Router + +----+----+ + 192.168.1. | .2 ce1-eth0 + | .1 r1-eth4 + +---------+ + | r1 | + | 1.1.1.1 | PE Router + +----+----+ + | .1 r1-eth0 + | + ~~~~~~~~~~~~~ + ~~ sw0 ~~ + ~~ 10.0.1.0/24 ~~ + ~~~~~~~~~~~~~ + |10.0.1.0/24 + | + | .2 r2-eth0 + +----+----+ + | r2 | + | 2.2.2.2 | P router + +--+---+--+ + r2-eth2 .2 | | .2 r2-eth1 + ______/ \______ + / \ + ~~~~~~~~~~~~~ ~~~~~~~~~~~~~ +~~ sw2 ~~ ~~ sw1 ~~ +~~ 10.0.3.0/24 ~~ ~~ 10.0.2.0/24 ~~ + ~~~~~~~~~~~~~ ~~~~~~~~~~~~~ + | / | + \ _________/ | + \ / \ +r3-eth1 .3 | | .3 r3-eth0 | .4 r4-eth0 + +----+--+---+ +----+----+ + | r3 | | r4 | + | 3.3.3.3 | | 4.4.4.4 | PE Routers + +-----------+ +---------+ + 192.168.1. | .1 192.168.1. | .1 rX-eth4 + | .2 | .2 ceX-eth0 + +-----+-----+ +----+-----+ + | ce2 | | ce3 | + | 99.0.0.2 | | 99.0.0.3 | CE Routers + +-----+-----+ +----+-----+ + | | + +""" + +import os + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import get_topogen +from lib.topolog import logger +from lib.ltemplate import ltemplateRtrCmd + +# Required to instantiate the topology builder class. + + +CWD = os.path.dirname(os.path.realpath(__file__)) +# test name based on directory +TEST = os.path.basename(CWD) + + +def build_topo(tgen): + "Build function" + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # Create P/PE routers + tgen.add_router("r1") + # check for mpls + if tgen.hasmpls != True: + logger.info("MPLS not available, tests will be skipped") + return + for routern in range(2, 5): + tgen.add_router("r{}".format(routern)) + # Create CE routers + for routern in range(1, 4): + tgen.add_router("ce{}".format(routern)) + + # CE/PE links + tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "ce1-eth0", "r1-eth4") + tgen.add_link(tgen.gears["ce2"], tgen.gears["r3"], "ce2-eth0", "r3-eth4") + tgen.add_link(tgen.gears["ce3"], tgen.gears["r4"], "ce3-eth0", "r4-eth4") + + # Create a switch with just one router connected to it to simulate a + # empty network. + switch = {} + switch[0] = tgen.add_switch("sw0") + switch[0].add_link(tgen.gears["r1"], nodeif="r1-eth0") + switch[0].add_link(tgen.gears["r2"], nodeif="r2-eth0") + + switch[1] = tgen.add_switch("sw1") + switch[1].add_link(tgen.gears["r2"], nodeif="r2-eth1") + switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth0") + switch[1].add_link(tgen.gears["r4"], nodeif="r4-eth0") + + switch[1] = tgen.add_switch("sw2") + switch[1].add_link(tgen.gears["r2"], nodeif="r2-eth2") + switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth1") + + +def ltemplatePreRouterStartHook(): + cc = ltemplateRtrCmd() + tgen = get_topogen() + logger.info("pre router-start hook") + # check for mpls + if tgen.hasmpls != True: + logger.info("MPLS not available, skipping setup") + return False + logger.info("setup mpls input") + return True + + +def ltemplatePostRouterStartHook(): + logger.info("post router-start hook") + return True diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf new file mode 100644 index 0000000..a564da9 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf @@ -0,0 +1,42 @@ +frr defaults traditional +! +hostname r1 +password zebra +log stdout notifications +log commands +router bgp 5226 + bgp router-id 1.1.1.1 + bgp cluster-id 1.1.1.1 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as 5226 + neighbor 192.168.1.2 update-source 192.168.1.1 + neighbor 192.168.1.2 route-reflector-client + neighbor 192.168.1.2 timers 3 10 + neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 update-source 1.1.1.1 + neighbor 2.2.2.2 timers 3 10 +! + address-family ipv4 unicast + redistribute vnc-direct + neighbor 192.168.1.2 activate + neighbor 192.168.1.2 next-hop-self + no neighbor 2.2.2.2 activate + exit-address-family +! + address-family ipv4 vpn + neighbor 2.2.2.2 activate + exit-address-family +! + vrf-policy cust1 + label 101 + rd 10:1 + rt both 52:100 + nexthop 192.168.1.1 + exit-vrf-policy +! + vnc export bgp mode group-nve + vnc export bgp group-nve group cust1 + vnc redistribute mode resolve-nve + vnc redistribute ipv4 bgp-direct + ! +end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ldpd.conf new file mode 100644 index 0000000..f7f2714 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ldpd.conf @@ -0,0 +1,23 @@ +hostname r1 +log file ldpd.log +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 1.1.1.1 + ! + address-family ipv4 + discovery transport-address 1.1.1.1 + ! + interface r1-eth0 + ! + ! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf new file mode 100644 index 0000000..460a8fb --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf @@ -0,0 +1,12 @@ +hostname r1 +log file ospfd.log +! +router ospf + router-id 1.1.1.1 + network 0.0.0.0/4 area 0 + redistribute static +! +int r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/zebra.conf new file mode 100644 index 0000000..767e17e --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/zebra.conf @@ -0,0 +1,27 @@ +log file zebra.log +! +hostname r1 +! +interface lo + mpls + ip address 1.1.1.1/32 +! +interface r1-eth0 + description to sw0 + mpls + ip address 10.0.1.1/24 + no link-detect +! +interface r1-eth4 + description to ce1 + mpls + ip address 192.168.1.1/24 + no link-detect +! +ip route 99.0.0.1/32 192.168.1.2 +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf new file mode 100644 index 0000000..3167306 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf @@ -0,0 +1,33 @@ +frr defaults traditional +! +hostname r2 +password zebra +log stdout notifications +log commands +router bgp 5226 + bgp router-id 2.2.2.2 + bgp cluster-id 2.2.2.2 + no bgp ebgp-requires-policy + neighbor 1.1.1.1 remote-as 5226 + neighbor 1.1.1.1 update-source 2.2.2.2 + neighbor 1.1.1.1 timers 3 10 + neighbor 3.3.3.3 remote-as 5226 + neighbor 3.3.3.3 update-source 2.2.2.2 + neighbor 3.3.3.3 timers 3 10 + neighbor 4.4.4.4 remote-as 5226 + neighbor 4.4.4.4 update-source 2.2.2.2 + neighbor 4.4.4.4 timers 3 10 + address-family ipv4 unicast + no neighbor 1.1.1.1 activate + no neighbor 3.3.3.3 activate + no neighbor 4.4.4.4 activate + exit-address-family + address-family ipv4 vpn + neighbor 1.1.1.1 activate + neighbor 1.1.1.1 route-reflector-client + neighbor 3.3.3.3 activate + neighbor 3.3.3.3 route-reflector-client + neighbor 4.4.4.4 activate + neighbor 4.4.4.4 route-reflector-client + exit-address-family +end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ldpd.conf new file mode 100644 index 0000000..c4056e0 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ldpd.conf @@ -0,0 +1,25 @@ +hostname r2 +log file ldpd.log +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 2.2.2.2 + ! + address-family ipv4 + discovery transport-address 2.2.2.2 + ! + interface r2-eth0 + ! + interface r2-eth1 + ! + ! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf new file mode 100644 index 0000000..dbed618 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf @@ -0,0 +1,19 @@ +hostname r2 +log file ospfd.log +! +router ospf + router-id 2.2.2.2 + network 0.0.0.0/0 area 0 +! +int r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/zebra.conf new file mode 100644 index 0000000..829ac96 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/zebra.conf @@ -0,0 +1,31 @@ +log file zebra.log +! +hostname r2 +! +interface lo + mpls + ip address 2.2.2.2/32 +! +interface r2-eth0 + description to sw0 + mpls + ip address 10.0.1.2/24 + no link-detect +! +interface r2-eth1 + description to sw1 + mpls + ip address 10.0.2.2/24 + no link-detect +! +interface r2-eth2 + description to sw2 + mpls + ip address 10.0.3.2/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf new file mode 100644 index 0000000..445da08 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf @@ -0,0 +1,41 @@ +frr defaults traditional +! +hostname r3 +password zebra +log stdout notifications +log commands +router bgp 5226 + bgp router-id 3.3.3.3 + bgp cluster-id 3.3.3.3 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as 5226 + neighbor 192.168.1.2 update-source 192.168.1.2 + neighbor 192.168.1.2 route-reflector-client + neighbor 192.168.1.2 timers 3 10 + neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 update-source 3.3.3.3 + neighbor 2.2.2.2 timers 3 10 +! + address-family ipv4 unicast + redistribute vnc-direct + neighbor 192.168.1.2 activate + neighbor 192.168.1.2 next-hop-self + no neighbor 2.2.2.2 activate + exit-address-family + address-family ipv4 vpn + neighbor 2.2.2.2 activate + exit-address-family +! + vrf-policy cust1 + label 103 + rd 10:3 + rt both 52:100 + nexthop 192.168.1.1 + exit-vrf-policy +! + vnc export bgp mode group-nve + vnc export bgp group-nve group cust1 + vnc redistribute mode resolve-nve + vnc redistribute ipv4 bgp-direct +! +end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ldpd.conf new file mode 100644 index 0000000..48956cb --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ldpd.conf @@ -0,0 +1,23 @@ +hostname r3 +log file ldpd.log +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 3.3.3.3 + ! + address-family ipv4 + discovery transport-address 3.3.3.3 + ! + interface r3-eth0 + ! + ! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf new file mode 100644 index 0000000..0e64ed6 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf @@ -0,0 +1,17 @@ +hostname r3 +password 1 +log file ospfd.log +! +router ospf + router-id 3.3.3.3 + network 0.0.0.0/4 area 0 + redistribute static +! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/zebra.conf new file mode 100644 index 0000000..916dabf --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/zebra.conf @@ -0,0 +1,32 @@ +log file zebra.log +! +hostname r3 +! +interface lo + mpls + ip address 3.3.3.3/32 +! +interface r3-eth0 + description to sw1 + mpls + ip address 10.0.2.3/24 + no link-detect +! +interface r3-eth1 + description to sw2 + ip address 10.0.3.3/24 + no link-detect +! +interface r3-eth4 + description to ce2 + mpls + ip address 192.168.1.1/24 + no link-detect +! +ip route 99.0.0.2/32 192.168.1.2 +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf new file mode 100644 index 0000000..1941352 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf @@ -0,0 +1,41 @@ +frr defaults traditional +! +hostname r4 +password zebra +log stdout notifications +log commands +router bgp 5226 + bgp router-id 4.4.4.4 + bgp cluster-id 4.4.4.4 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as 5226 + neighbor 192.168.1.2 update-source 192.168.1.1 + neighbor 192.168.1.2 route-reflector-client + neighbor 192.168.1.2 timers 3 10 + neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 update-source 4.4.4.4 + neighbor 2.2.2.2 timers 3 10 +! + address-family ipv4 unicast + redistribute vnc-direct + neighbor 192.168.1.2 activate + neighbor 192.168.1.2 next-hop-self + no neighbor 2.2.2.2 activate + exit-address-family + address-family ipv4 vpn + neighbor 2.2.2.2 activate + exit-address-family +! + vrf-policy cust1 + label 104 + rd 10:4 + rt both 52:100 + nexthop 192.168.1.1 + exit-vrf-policy +! + vnc export bgp mode group-nve + vnc export bgp group-nve group cust1 + vnc redistribute mode resolve-nve + vnc redistribute ipv4 bgp-direct +! +end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ldpd.conf new file mode 100644 index 0000000..1d04aa0 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ldpd.conf @@ -0,0 +1,23 @@ +hostname r4 +log file ldpd.log +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 4.4.4.4 + ! + address-family ipv4 + discovery transport-address 4.4.4.4 + ! + interface r4-eth0 + ! + ! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf new file mode 100644 index 0000000..89e37df --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf @@ -0,0 +1,12 @@ +hostname r4 +log file ospfd.log +! +router ospf + router-id 4.4.4.4 + network 0.0.0.0/4 area 0 + redistribute static +! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/zebra.conf new file mode 100644 index 0000000..e08ac86 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/zebra.conf @@ -0,0 +1,26 @@ +log file zebra.log +! +hostname r4 +! +interface lo + mpls + ip address 4.4.4.4/32 +! +interface r4-eth0 + description to sw1 + mpls + ip address 10.0.2.4/24 + no link-detect +! +interface r4-eth4 + description to ce3 + mpls + ip address 192.168.1.1/24 + no link-detect +! +ip route 99.0.0.3/32 192.168.1.2 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py new file mode 100644 index 0000000..0deb181 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py @@ -0,0 +1,193 @@ +from lib.lutil import luCommand + +luCommand( + "r1", 'vtysh -c "show bgp next"', "99.0.0.. valid", "wait", "See CE static NH" +) +luCommand( + "r3", 'vtysh -c "show bgp next"', "99.0.0.. valid", "wait", "See CE static NH" +) +luCommand( + "r4", 'vtysh -c "show bgp next"', "99.0.0.. valid", "wait", "See CE static NH" +) +luCommand("r1", 'vtysh -c "show bgp ipv4 uni"', "i5.*i5", "wait", "See CE routes") +luCommand("r3", 'vtysh -c "show bgp ipv4 uni"', "i5.*i5", "wait", "See CE routes") +luCommand("r4", 'vtysh -c "show bgp ipv4 uni"', "i5.*i5", "wait", "See CE routes") +luCommand("ce1", 'vtysh -c "show bgp ipv4 uni 5.1.0.0/24"', "", "none", "See CE routes") +luCommand("r1", 'vtysh -c "show bgp ipv4 uni 5.1.0.0/24"', "", "none", "See CE routes") +luCommand("ce2", 'vtysh -c "show bgp ipv4 uni 5.1.0.0/24"', "", "none", "See CE routes") +luCommand("r3", 'vtysh -c "show bgp ipv4 uni 5.1.0.0/24"', "", "none", "See CE routes") +luCommand("ce3", 'vtysh -c "show bgp ipv4 uni 5.1.2.0/24"', "", "none", "See CE routes") +luCommand("r4", 'vtysh -c "show bgp ipv4 uni 5.1.2.0/24"', "", "none", "See CE routes") + +luCommand( + "r1", 'vtysh -c "add vrf cust1 prefix 99.0.0.1/32"', ".", "none", "IP Address" +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "99.0.0.1", + "wait", + "Local Registration", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations imported"', + "2 out of 2 imported", + "wait", + "Imported Registrations", +) +luCommand( + "r3", + 'vtysh -c "show bgp ipv4 vpn"', + "i99.0.0.1/32", + "wait", + "See R1s static address", +) +luCommand( + "r4", + 'vtysh -c "show bgp ipv4 vpn"', + "i99.0.0.1/32", + "wait", + "See R1s static address", +) +luCommand( + "r3", 'vtysh -c "show bgp ipv4 vpn rd 10:1"', "i5.*i5", "wait", "See R1s imports" +) +luCommand( + "r4", 'vtysh -c "show bgp ipv4 vpn rd 10:1"', "i5.*i5", "wait", "See R1s imports" +) + +luCommand( + "r3", 'vtysh -c "add vrf cust1 prefix 99.0.0.2/32"', ".", "none", "IP Address" +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations local"', + "99.0.0.2", + "wait", + "Local Registration", +) +have2ndImports = luCommand( + "r3", + 'vtysh -c "show vnc registrations imported"', + "2 out of 2 imported", + "none", + "Imported Registrations", + 2, +) +if have2ndImports: + luCommand( + "r3", + 'vtysh -c "show vnc registrations imported"', + "2 out of 2 imported", + "pass", + "Imported Registrations", + ) +luCommand( + "r1", + 'vtysh -c "show bgp ipv4 vpn"', + "i99.0.0.2/32", + "wait", + "See R3s static address", +) +luCommand( + "r4", + 'vtysh -c "show bgp ipv4 vpn"', + "i99.0.0.2/32", + "wait", + "See R3s static address", +) +if have2ndImports: + luCommand( + "r1", + 'vtysh -c "show bgp ipv4 vpn rd 10:3"', + "i5.*i5", + "none", + "See R3s imports", + ) + luCommand( + "r4", + 'vtysh -c "show bgp ipv4 vpn rd 10:3"', + "i5.*i5", + "none", + "See R3s imports", + ) + +luCommand( + "r4", 'vtysh -c "add vrf cust1 prefix 99.0.0.3/32"', ".", "none", "IP Address" +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations local"', + "99.0.0.3", + "wait", + "Local Registration", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations imported"', + "2 out of 2 imported", + "wait", + "Imported Registrations", +) +luCommand( + "r1", + 'vtysh -c "show bgp ipv4 vpn"', + "i99.0.0.3/32", + "wait", + "See R4s static address", +) +luCommand( + "r3", + 'vtysh -c "show bgp ipv4 vpn"', + "i99.0.0.3/32", + "wait", + "See R4s static address", +) +luCommand( + "r1", 'vtysh -c "show bgp ipv4 vpn rd 10:4"', "i5.*i5", "wait", "See R4s imports" +) +luCommand( + "r3", 'vtysh -c "show bgp ipv4 vpn rd 10:4"', "i5.*i5", "wait", "See R4s imports" +) + + +luCommand( + "r1", + 'vtysh -c "show vnc registrations remote"', + "5.1.2.0/24 .*5.1.3.0/24", + "wait", + "R4s registrations", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations remote"', + "5.1.2.0/24 .*5.1.3.0/24", + "wait", + "R4s registrations", +) +if have2ndImports: + luCommand( + "r1", + 'vtysh -c "show vnc registrations remote"', + "5.1.0.0/24 .*5.1.1.0/24", + "wait", + "Remote registrations", + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations remote"', + "5.1.0.0/24 .*5.1.1.0/24", + "wait", + "Remote registrations", + ) +luCommand( + "r4", + 'vtysh -c "show vnc registrations remote"', + "5.1.0.0/24 .*5.1.1.0/24", + "wait", + "Remote registrations", +) +luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none") diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py new file mode 100644 index 0000000..6cd92e2 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py @@ -0,0 +1,71 @@ +from lib.lutil import luCommand + +luCommand("ce1", "ping 192.168.1.1 -c 1", " 0. packet loss", "pass", "CE->PE ping") +luCommand("ce2", "ping 192.168.1.1 -c 1", " 0. packet loss", "pass", "CE->PE ping") +luCommand("ce3", "ping 192.168.1.1 -c 1", " 0. packet loss", "pass", "CE->PE ping") +luCommand("ce1", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180) +luCommand("ce2", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180) +luCommand("ce3", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180) +luCommand( + "r1", 'vtysh -c "show ip route ospf"', "2.2.2.2", "wait", "OSPF Route has Arrived", 60) +luCommand( + "r1", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r3", 'vtysh -c "show ip route ospf"', "2.2.2.2", "wait", "OSPF Route has Arrived", 60) +luCommand( + "r3", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) + +luCommand( + "r4", 'vtysh -c "show ip route ospf"', "2.2.2.2", "wait", "OSPF Route has Arrived", 60) +luCommand( + "r4", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r2", + 'vtysh -c "show bgp summary"', + " 00:0.* 00:0.* 00:0", + "wait", + "Core adjacencies up", + 180, +) +luCommand( + "r1", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180 +) +luCommand( + "r3", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180 +) +luCommand( + "r4", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180 +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf all summary"', + " 00:0.* 00:0", + "pass", + "All adjacencies up", +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf all summary"', + " 00:0.* 00:0", + "pass", + "All adjacencies up", +) +luCommand( + "r4", + 'vtysh -c "show bgp vrf all summary"', + " 00:0.* 00:0", + "pass", + "All adjacencies up", +) +luCommand( + "r1", "ping 3.3.3.3 -c 1", " 0. packet loss", "wait", "PE->PE3 (loopback) ping" +) +luCommand( + "r1", "ping 4.4.4.4 -c 1", " 0. packet loss", "wait", "PE->PE4 (loopback) ping" +) +luCommand( + "r4", "ping 3.3.3.3 -c 1", " 0. packet loss", "wait", "PE->PE3 (loopback) ping" +) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/check_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/check_routes.py new file mode 100644 index 0000000..af39a95 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/check_routes.py @@ -0,0 +1,55 @@ +from lib.lutil import luCommand + +luCommand( + "ce1", + 'vtysh -c "show bgp ipv4 uni"', + "7 routes and 7", + "wait", + "Local and remote routes", +) +luCommand( + "ce2", + 'vtysh -c "show bgp ipv4 uni"', + "7 routes and 9", + "wait", + "Local and remote routes", +) +luCommand( + "ce3", + 'vtysh -c "show bgp ipv4 uni"', + "7 routes and 7", + "wait", + "Local and remote routes", +) +luCommand( + "r1", 'vtysh -c "show bgp ipv4 uni"', "7 routes and 9", "pass", "Unicast SAFI" +) +luCommand( + "r2", + 'vtysh -c "show bgp ipv4 uni"', + "No BGP prefixes displayed", + "pass", + "Unicast SAFI", +) +luCommand( + "r3", 'vtysh -c "show bgp ipv4 uni"', "7 routes and 9", "pass", "Unicast SAFI" +) +luCommand( + "r4", 'vtysh -c "show bgp ipv4 uni"', "7 routes and 9", "pass", "Unicast SAFI" +) +have2ndImports = luCommand( + "r3", + 'vtysh -c "show vnc registrations imported"', + "2 out of 2 imported", + "none", + "Imported Registrations", + 2, +) +if have2ndImports: + num = "9 routes and 9" +else: + num = "7 routes and 7" +luCommand("r1", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI") +luCommand("r2", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI") +luCommand("r3", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI") +luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI") diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/cleanup_all.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/cleanup_all.py new file mode 100644 index 0000000..38eac14 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/cleanup_all.py @@ -0,0 +1,114 @@ +from lib.lutil import luCommand + +luCommand( + "r1", + 'vtysh -c "clear vrf cust1 prefix 99.0.0.1/32"', + ".", + "none", + "Cleared VRF route", +) +luCommand( + "r3", + 'vtysh -c "clear vrf cust1 prefix 99.0.0.2/32"', + ".", + "none", + "Cleared VRF route", +) +luCommand( + "r4", + 'vtysh -c "clear vrf cust1 prefix 99.0.0.3/32"', + ".", + "none", + "Cleared VRF route", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "99.0.0.1", + "fail", + "Local Registration cleared", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations local"', + "99.0.0.2", + "fail", + "Local Registration cleared", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations local"', + "99.0.0.3", + "fail", + "Local Registration cleared", +) +luCommand( + "r1", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Unicast SAFI updated", +) +luCommand( + "r2", + 'vtysh -c "show bgp ipv4 uni"', + "No BGP prefixes displayed", + "pass", + "Unicast SAFI", +) +luCommand( + "r3", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Unicast SAFI updated", +) +luCommand( + "r4", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Unicast SAFI updated", +) +luCommand( + "ce1", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Local and remote routes", +) +luCommand( + "ce2", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Local and remote routes", +) +luCommand( + "ce3", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Local and remote routes", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations remote"', + "Prefix ", + "fail", + "Remote Registration cleared", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations remote"', + "Prefix ", + "fail", + "Remote Registration cleared", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations remote"', + "Prefix ", + "fail", + "Remote Registration cleared", +) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/test_bgp_l3vpn_to_bgp_direct.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/test_bgp_l3vpn_to_bgp_direct.py new file mode 100755 index 0000000..7930f09 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/test_bgp_l3vpn_to_bgp_direct.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright (c) 2018, LabN Consulting, L.L.C. +# Authored by Lou Berger +# + +import os +import sys +import pytest + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) + +from lib.ltemplate import * + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd] + + +def test_adjacencies(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/adjacencies.py", False, CliOnFail, CheckFunc) + + +def test_add_routes(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/add_routes.py", False, CliOnFail, CheckFunc) + + +def test_check_routes(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/check_routes.py", False, CliOnFail, CheckFunc) + + +def test_cleanup_all(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/cleanup_all.py", False, CliOnFail, CheckFunc) + + +if __name__ == "__main__": + retval = pytest.main(["-s"]) + sys.exit(retval) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/__init__.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf new file mode 100644 index 0000000..ae57431 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf @@ -0,0 +1,55 @@ +frr defaults traditional +! +hostname ce1 +password zebra +log stdout notifications +log commands +log file bgpd.log + +router bgp 5227 + no bgp network import-check + bgp router-id 99.0.0.1 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as 5227 + neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 + address-family ipv4 unicast + network 99.0.0.1/32 + network 5.1.0.0/24 route-map rm-nh + network 5.1.1.0/24 route-map rm-nh + redistribute sharp route-map sharp-nh + network 6.0.1.0/24 route-map rm-nh + network 6.0.2.0/24 route-map rm-nh-same + neighbor 192.168.1.1 activate + exit-address-family +! +access-list al-any permit any +! +route-map rm-nh permit 10 + match ip address al-any + set ip next-hop 99.0.0.1 + set local-preference 123 + set metric 98 + set large-community 12:34:56 + set extcommunity rt 89:123 + set community 0:67 +! +route-map sharp-nh permit 10 + match ip address al-any + set ip next-hop 99.0.0.1 + set local-preference 200 + set metric 200 + set large-community 90:12:34 + set extcommunity rt 80:987 + set community 0:65 +! +route-map rm-nh-same permit 10 + match ip address al-any + set ip next-hop 99.0.0.1 + set local-preference 100 + set metric 100 + set large-community 12:34:11 + set extcommunity rt 89:123 + set community 0:67 +! +end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/zebra.conf new file mode 100644 index 0000000..46831bb --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname ce1 +! +interface lo + ip address 99.0.0.1/32 +! +interface ce1-eth0 + description to r1 + ip address 192.168.1.2/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf new file mode 100644 index 0000000..599e2dd --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf @@ -0,0 +1,55 @@ +frr defaults traditional +! +hostname ce2 +password zebra +log stdout notifications +log commands +log file bgpd.log + +router bgp 5227 + no bgp network import-check + bgp router-id 99.0.0.2 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as 5227 + neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 + address-family ipv4 unicast + network 99.0.0.2/32 + network 5.1.0.0/24 route-map rm-nh + network 5.1.1.0/24 route-map rm-nh + redistribute sharp route-map sharp-nh + network 6.0.1.0/24 route-map rm-nh + network 6.0.2.0/24 route-map rm-nh-same + neighbor 192.168.1.1 activate + exit-address-family +! +access-list al-any permit any +! +route-map rm-nh permit 10 + match ip address al-any + set ip next-hop 99.0.0.2 + set local-preference 100 + set metric 100 + set large-community 12:34:56 + set extcommunity rt 89:123 + set community 0:67 +! +route-map sharp-nh permit 10 + match ip address al-any + set ip next-hop 99.0.0.2 + set local-preference 200 + set metric 200 + set large-community 78:90:12 + set extcommunity rt 70:456 + set community 0:66 +! +route-map rm-nh-same permit 10 + match ip address al-any + set ip next-hop 99.0.0.2 + set local-preference 100 + set metric 100 + set large-community 12:34:12 + set extcommunity rt 89:123 + set community 0:67 +! +end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/zebra.conf new file mode 100644 index 0000000..fb4d8cc --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname ce2 +! +interface lo + ip address 99.0.0.2/32 +! +interface ce2-eth0 + description to r3 + ip address 192.168.1.2/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf new file mode 100644 index 0000000..e316de5 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf @@ -0,0 +1,45 @@ +frr defaults traditional +! +hostname ce3 +password zebra +log stdout notifications +log commands +log file bgpd.log + +router bgp 5227 + no bgp network import-check + bgp router-id 99.0.0.3 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as 5227 + neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 + address-family ipv4 unicast + network 99.0.0.3/32 + network 5.1.2.0/24 route-map rm-nh + network 5.1.3.0/24 route-map rm-nh + network 6.0.1.0/24 route-map rm-nh + network 6.0.2.0/24 route-map rm-nh-same + neighbor 192.168.1.1 activate + exit-address-family +! +access-list al-any permit any +! +route-map rm-nh permit 10 + match ip address al-any + set ip next-hop 99.0.0.3 + set local-preference 50 + set metric 200 + set large-community 12:34:56 + set extcommunity rt 89:123 + set community 0:67 +! +route-map rm-nh-same permit 10 + match ip address al-any + set ip next-hop 99.0.0.3 + set local-preference 100 + set metric 100 + set large-community 12:34:13 + set extcommunity rt 89:123 + set community 0:67 +! +end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/zebra.conf new file mode 100644 index 0000000..77a1163 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname ce3 +! +interface lo + ip address 99.0.0.3/32 +! +interface ce3-eth0 + description to r4 + ip address 192.168.1.2/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf new file mode 100644 index 0000000..60d9e93 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf @@ -0,0 +1,45 @@ +frr defaults traditional +! +hostname ce4 +password zebra +log stdout notifications +log commands +log file bgpd.log + +router bgp 5228 vrf ce4-cust2 + no bgp network import-check + bgp router-id 99.0.0.4 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as 5228 + neighbor 192.168.2.1 update-source 192.168.2.2 + neighbor 192.168.2.1 timers 3 10 + address-family ipv4 unicast + network 99.0.0.4/32 + network 5.4.2.0/24 route-map rm-nh + network 5.4.3.0/24 route-map rm-nh + network 6.0.1.0/24 route-map rm-nh + network 6.0.2.0/24 route-map rm-nh-same + neighbor 192.168.2.1 activate + exit-address-family +! +access-list al-any permit any +! +route-map rm-nh permit 10 + match ip address al-any + set ip next-hop 99.0.0.4 + set local-preference 50 + set metric 200 + set large-community 12:34:56 + set extcommunity rt 89:123 + set community 0:67 +! +route-map rm-nh-same permit 10 + match ip address al-any + set ip next-hop 99.0.0.4 + set local-preference 100 + set metric 100 + set large-community 12:34:14 + set extcommunity rt 89:123 + set community 0:67 +! +end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf new file mode 100644 index 0000000..e55c9e7 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname ce4 +! +interface ce4-cust2 + ip address 99.0.0.4/32 +! +interface ce4-eth0 + description to r4 + ip address 192.168.2.2/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py new file mode 100644 index 0000000..0ac5350 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright (c) 2017 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +r""" +customize.py: Simple FRR MPLS L3VPN test topology + + | + +----+----+ + | ce1 | + | 99.0.0.1| CE Router + +----+----+ + 192.168.1. | .2 ce1-eth0 + | .1 r1-eth4 + +---------+ + | r1 | + | 1.1.1.1 | PE Router + +----+----+ + | .1 r1-eth0 + | + ~~~~~~~~~~~~~ + ~~ sw0 ~~ + ~~ 10.0.1.0/24 ~~ + ~~~~~~~~~~~~~ + |10.0.1.0/24 + | + | .2 r2-eth0 + +----+----+ + | r2 | + | 2.2.2.2 | P router + +--+---+--+ + r2-eth2 .2 | | .2 r2-eth1 + ______/ \______ + / \ + ~~~~~~~~~~~~~ ~~~~~~~~~~~~~ +~~ sw2 ~~ ~~ sw1 ~~ +~~ 10.0.3.0/24 ~~ ~~ 10.0.2.0/24 ~~ + ~~~~~~~~~~~~~ ~~~~~~~~~~~~~ + | / | + \ _________/ | + \ / \ +r3-eth1 .3 | | .3 r3-eth0 | .4 r4-eth0 + +----+--+---+ +----+----+ + | r3 | | r4 | r4-eth5 + | 3.3.3.3 | | 4.4.4.4 |-------+ PE Routers + +-----------+ +---------+ | +192.168.1.1 |r3.eth4 192.168.1.1 | r4-eth4 |192.168.2.1 + .2 | ceX-eth0 .2 | | .2 + +-----+-----+ +----+-----+ +----+-----+ + | ce2 | | ce3 | | ce4 | + | 99.0.0.2 | | 99.0.0.3 | | 99.0.0.4 | CE Routers + +-----+-----+ +----+-----+ +----+-----+ + | | | + +""" + +import os +import platform + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import get_topogen +from lib.topolog import logger +from lib.ltemplate import ltemplateRtrCmd + +# Required to instantiate the topology builder class. + + +CWD = os.path.dirname(os.path.realpath(__file__)) +# test name based on directory +TEST = os.path.basename(CWD) + + +def build_topo(tgen): + "Build function" + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # Create P/PE routers + # check for mpls + tgen.add_router("r1") + if tgen.hasmpls != True: + logger.info("MPLS not available, tests will be skipped") + return + mach = platform.machine() + krel = platform.release() + if mach[:1] == "a" and topotest.version_cmp(krel, "4.11") < 0: + logger.info("Need Kernel version 4.11 to run on arm processor") + return + for routern in range(2, 5): + tgen.add_router("r{}".format(routern)) + # Create CE routers + for routern in range(1, 5): + tgen.add_router("ce{}".format(routern)) + + # CE/PE links + tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "ce1-eth0", "r1-eth4") + tgen.add_link(tgen.gears["ce2"], tgen.gears["r3"], "ce2-eth0", "r3-eth4") + tgen.add_link(tgen.gears["ce3"], tgen.gears["r4"], "ce3-eth0", "r4-eth4") + tgen.add_link(tgen.gears["ce4"], tgen.gears["r4"], "ce4-eth0", "r4-eth5") + + # Create a switch with just one router connected to it to simulate a + # empty network. + switch = {} + switch[0] = tgen.add_switch("sw0") + switch[0].add_link(tgen.gears["r1"], nodeif="r1-eth0") + switch[0].add_link(tgen.gears["r2"], nodeif="r2-eth0") + + switch[1] = tgen.add_switch("sw1") + switch[1].add_link(tgen.gears["r2"], nodeif="r2-eth1") + switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth0") + switch[1].add_link(tgen.gears["r4"], nodeif="r4-eth0") + + switch[1] = tgen.add_switch("sw2") + switch[1].add_link(tgen.gears["r2"], nodeif="r2-eth2") + switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth1") + + +def ltemplatePreRouterStartHook(): + cc = ltemplateRtrCmd() + krel = platform.release() + tgen = get_topogen() + logger.info("pre router-start hook, kernel=" + krel) + + # check for mpls + if tgen.hasmpls != True: + logger.info("MPLS not available, skipping setup") + return False + # trace errors/unexpected output + cc.resetCounts() + # configure r2 mpls interfaces + intfs = ["lo", "r2-eth0", "r2-eth1", "r2-eth2"] + for intf in intfs: + cc.doCmd(tgen, "r2", "echo 1 > /proc/sys/net/mpls/conf/{}/input".format(intf)) + + # configure cust1 VRFs & MPLS + rtrs = ["r1", "r3", "r4"] + cmds = [ + "ip link add {0}-cust1 type vrf table 10", + "ip ru add oif {0}-cust1 table 10", + "ip ru add iif {0}-cust1 table 10", + "ip link set dev {0}-cust1 up", + ] + for rtr in rtrs: + for cmd in cmds: + cc.doCmd(tgen, rtr, cmd.format(rtr)) + cc.doCmd(tgen, rtr, "ip link set dev {0}-eth4 master {0}-cust1".format(rtr)) + intfs = [rtr + "-cust1", "lo", rtr + "-eth0", rtr + "-eth4"] + for intf in intfs: + cc.doCmd( + tgen, rtr, "echo 1 > /proc/sys/net/mpls/conf/{}/input".format(intf) + ) + logger.info( + "setup {0} vrf {0}-cust1, {0}-eth4. enabled mpls input.".format(rtr) + ) + # configure cust2 VRFs & MPLS + rtrs = ["r4"] + cmds = [ + "ip link add {0}-cust2 type vrf table 20", + "ip ru add oif {0}-cust2 table 20", + "ip ru add iif {0}-cust2 table 20", + "ip link set dev {0}-cust2 up", + ] + for rtr in rtrs: + for cmd in cmds: + cc.doCmd(tgen, rtr, cmd.format(rtr)) + cc.doCmd(tgen, rtr, "ip link set dev {0}-eth5 master {0}-cust2".format(rtr)) + intfs = [rtr + "-cust2", rtr + "-eth5"] + for intf in intfs: + cc.doCmd( + tgen, rtr, "echo 1 > /proc/sys/net/mpls/conf/{}/input".format(intf) + ) + logger.info( + "setup {0} vrf {0}-cust2, {0}-eth5. enabled mpls input.".format(rtr) + ) + # put ce4-eth0 into a VRF (no default instance!) + rtrs = ["ce4"] + cmds = [ + "ip link add {0}-cust2 type vrf table 20", + "ip ru add oif {0}-cust2 table 20", + "ip ru add iif {0}-cust2 table 20", + "ip link set dev {0}-cust2 up", + ] + for rtr in rtrs: + for cmd in cmds: + cc.doCmd(tgen, rtr, cmd.format(rtr)) + cc.doCmd(tgen, rtr, "ip link set dev {0}-eth0 master {0}-cust2".format(rtr)) + if cc.getOutput() != 0: + InitSuccess = False + logger.info( + "Unexpected output seen ({} times, tests will be skipped".format( + cc.getOutput() + ) + ) + else: + rtrs = ["r1", "r3", "r4", "ce4"] + for rtr in rtrs: + logger.info("{} configured".format(rtr)) + cc.doCmd(tgen, rtr, "ip -d link show type vrf") + cc.doCmd(tgen, rtr, "ip link show") + InitSuccess = True + logger.info("VRF config successful!") + return InitSuccess + + +def ltemplatePostRouterStartHook(): + logger.info("post router-start hook") + return True diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf new file mode 100644 index 0000000..72211fe --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf @@ -0,0 +1,55 @@ +frr defaults traditional + +hostname r1 +password zebra +log stdout notifications +log commands + +log file bgpd.log + +#debug bgp vpn leak-to-vrf +#debug bgp vpn leak-from-vrf +#debug bgp vpn label +#debug bgp updates out + +router bgp 5226 + bgp router-id 1.1.1.1 + bgp cluster-id 1.1.1.1 + no bgp ebgp-requires-policy + neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 update-source 1.1.1.1 + neighbor 2.2.2.2 timers 3 10 + + address-family ipv4 unicast + no neighbor 2.2.2.2 activate + exit-address-family + + address-family ipv4 vpn + neighbor 2.2.2.2 activate + exit-address-family + + +router bgp 5227 vrf r1-cust1 + + bgp router-id 192.168.1.1 + no bgp ebgp-requires-policy + + neighbor 192.168.1.2 remote-as 5227 + neighbor 192.168.1.2 update-source 192.168.1.1 + neighbor 192.168.1.2 timers 3 10 + + address-family ipv4 unicast + neighbor 192.168.1.2 activate + neighbor 192.168.1.2 next-hop-self + + label vpn export 101 + rd vpn export 10:1 + rt vpn both 52:100 + + import vpn + export vpn + exit-address-family + + +! +end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ldpd.conf new file mode 100644 index 0000000..168b2d4 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ldpd.conf @@ -0,0 +1,24 @@ +hostname r1 +log file ldpd.log +password zebra +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 1.1.1.1 + ! + address-family ipv4 + discovery transport-address 1.1.1.1 + ! + interface r1-eth0 + ! + ! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf new file mode 100644 index 0000000..460a8fb --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf @@ -0,0 +1,12 @@ +hostname r1 +log file ospfd.log +! +router ospf + router-id 1.1.1.1 + network 0.0.0.0/4 area 0 + redistribute static +! +int r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf new file mode 100644 index 0000000..221bc7a --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf @@ -0,0 +1,25 @@ +log file zebra.log + +hostname r1 +password zebra + +#debug zebra packet + +interface lo + ip address 1.1.1.1/32 + +interface r1-eth0 + description to sw0 + ip address 10.0.1.1/24 + no link-detect + +interface r1-eth4 + description to ce1 + ip address 192.168.1.1/24 + no link-detect + +ip forwarding + + +line vty + diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf new file mode 100644 index 0000000..edb3b69 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf @@ -0,0 +1,35 @@ +frr defaults traditional + +hostname r2 +password zebra +log stdout notifications +log commands +log file bgpd.log + +router bgp 5226 + bgp router-id 2.2.2.2 + bgp cluster-id 2.2.2.2 + no bgp ebgp-requires-policy + neighbor 1.1.1.1 remote-as 5226 + neighbor 1.1.1.1 update-source 2.2.2.2 + neighbor 1.1.1.1 timers 3 10 + neighbor 3.3.3.3 remote-as 5226 + neighbor 3.3.3.3 update-source 2.2.2.2 + neighbor 3.3.3.3 timers 3 10 + neighbor 4.4.4.4 remote-as 5226 + neighbor 4.4.4.4 update-source 2.2.2.2 + neighbor 4.4.4.4 timers 3 10 + address-family ipv4 unicast + no neighbor 1.1.1.1 activate + no neighbor 3.3.3.3 activate + no neighbor 4.4.4.4 activate + exit-address-family + address-family ipv4 vpn + neighbor 1.1.1.1 activate + neighbor 1.1.1.1 route-reflector-client + neighbor 3.3.3.3 activate + neighbor 3.3.3.3 route-reflector-client + neighbor 4.4.4.4 activate + neighbor 4.4.4.4 route-reflector-client + exit-address-family +end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ldpd.conf new file mode 100644 index 0000000..847be20 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ldpd.conf @@ -0,0 +1,26 @@ +hostname r2 +log file ldpd.log +password zebra +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 2.2.2.2 + ! + address-family ipv4 + discovery transport-address 2.2.2.2 + ! + interface r2-eth0 + ! + interface r2-eth1 + ! + ! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf new file mode 100644 index 0000000..dbed618 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf @@ -0,0 +1,19 @@ +hostname r2 +log file ospfd.log +! +router ospf + router-id 2.2.2.2 + network 0.0.0.0/0 area 0 +! +int r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/zebra.conf new file mode 100644 index 0000000..dc4ef7e --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/zebra.conf @@ -0,0 +1,28 @@ +log file zebra.log + +hostname r2 +password zebra +! +interface lo + ip address 2.2.2.2/32 +! +interface r2-eth0 + description to sw0 + ip address 10.0.1.2/24 + no link-detect +! +interface r2-eth1 + description to sw1 + ip address 10.0.2.2/24 + no link-detect +! +interface r2-eth2 + description to sw2 + ip address 10.0.3.2/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf new file mode 100644 index 0000000..6c9640e --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf @@ -0,0 +1,48 @@ +frr defaults traditional + +hostname r3 +password zebra +log stdout notifications +log commands +log file bgpd.log + +#debug bgp vpn label +router bgp 5226 + bgp router-id 3.3.3.3 + bgp cluster-id 3.3.3.3 + no bgp ebgp-requires-policy + neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 update-source 3.3.3.3 + neighbor 2.2.2.2 timers 3 10 + + address-family ipv4 unicast + no neighbor 2.2.2.2 activate + exit-address-family + + address-family ipv4 vpn + neighbor 2.2.2.2 activate + exit-address-family + +router bgp 5227 vrf r3-cust1 + + bgp router-id 192.168.1.1 + no bgp ebgp-requires-policy + + neighbor 192.168.1.2 remote-as 5227 + neighbor 192.168.1.2 update-source 192.168.1.1 + neighbor 192.168.1.2 timers 3 10 + + address-family ipv4 unicast + neighbor 192.168.1.2 activate + neighbor 192.168.1.2 next-hop-self + + label vpn export 103 + rd vpn export 10:3 + rt vpn both 52:100 + + import vpn + export vpn + exit-address-family + + +end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ldpd.conf new file mode 100644 index 0000000..a620739 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ldpd.conf @@ -0,0 +1,24 @@ +hostname r3 +password zebra +log file ldpd.log +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 3.3.3.3 + ! + address-family ipv4 + discovery transport-address 3.3.3.3 + ! + interface r3-eth0 + ! + ! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf new file mode 100644 index 0000000..0e64ed6 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf @@ -0,0 +1,17 @@ +hostname r3 +password 1 +log file ospfd.log +! +router ospf + router-id 3.3.3.3 + network 0.0.0.0/4 area 0 + redistribute static +! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/zebra.conf new file mode 100644 index 0000000..9ffc84f --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/zebra.conf @@ -0,0 +1,29 @@ +log file zebra.log + +hostname r3 +password zebra +! +interface lo + ip address 3.3.3.3/32 +! +interface r3-eth0 + description to sw1 + ip address 10.0.2.3/24 + no link-detect +! +interface r3-eth1 + description to sw2 + ip address 10.0.3.3/24 + no link-detect +! +interface r3-eth4 + description to ce2 + ip address 192.168.1.1/24 + no link-detect +! +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf new file mode 100644 index 0000000..ed76ed3 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf @@ -0,0 +1,72 @@ +frr defaults traditional + +hostname r4 +password zebra +log stdout notifications +log commands +log file bgpd.log + +#debug bgp vpn label +#debug bgp nht +#debug bgp zebra + +router bgp 5226 + bgp router-id 4.4.4.4 + bgp cluster-id 4.4.4.4 + no bgp ebgp-requires-policy + neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 update-source 4.4.4.4 + neighbor 2.2.2.2 timers 3 10 + + address-family ipv4 unicast + no neighbor 2.2.2.2 activate + exit-address-family + + address-family ipv4 vpn + neighbor 2.2.2.2 activate + exit-address-family + +router bgp 5227 vrf r4-cust1 + + bgp router-id 192.168.1.1 + no bgp ebgp-requires-policy + + neighbor 192.168.1.2 remote-as 5227 + neighbor 192.168.1.2 update-source 192.168.1.1 + neighbor 192.168.1.2 timers 3 10 + + address-family ipv4 unicast + neighbor 192.168.1.2 activate + neighbor 192.168.1.2 next-hop-self + + label vpn export 1041 + rd vpn export 10:41 + rt vpn both 52:100 + + import vpn + export vpn + exit-address-family + +router bgp 5228 vrf r4-cust2 + + bgp router-id 192.168.2.1 + no bgp ebgp-requires-policy + + neighbor 192.168.2.2 remote-as 5228 + neighbor 192.168.2.2 update-source 192.168.2.1 + neighbor 192.168.2.2 timers 3 10 + + address-family ipv4 unicast + neighbor 192.168.2.2 activate + neighbor 192.168.2.2 next-hop-self + + label vpn export 1042 + rd vpn export 10:42 + # note RT same as r4-cust1 for inter-vrf route leaking + rt vpn both 52:100 + + import vpn + export vpn + exit-address-family + +end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ldpd.conf new file mode 100644 index 0000000..617d3a7 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ldpd.conf @@ -0,0 +1,24 @@ +hostname r4 +password zebra +log file ldpd.log +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 4.4.4.4 + ! + address-family ipv4 + discovery transport-address 4.4.4.4 + ! + interface r4-eth0 + ! + ! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf new file mode 100644 index 0000000..89e37df --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf @@ -0,0 +1,12 @@ +hostname r4 +log file ospfd.log +! +router ospf + router-id 4.4.4.4 + network 0.0.0.0/4 area 0 + redistribute static +! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/zebra.conf new file mode 100644 index 0000000..4f01a27 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/zebra.conf @@ -0,0 +1,28 @@ +log file zebra.log + +hostname r4 +password zebra +! +interface lo + ip address 4.4.4.4/32 +! +interface r4-eth0 + description to sw1 + ip address 10.0.2.4/24 + no link-detect +! +interface r4-eth4 + description to ce3 + ip address 192.168.1.1/24 + no link-detect +! +interface r4-eth5 + description to ce4 + ip address 192.168.2.1/24 + no link-detect +! +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/add_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/add_routes.py new file mode 100644 index 0000000..375bca8 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/add_routes.py @@ -0,0 +1,59 @@ +from lib.lutil import luCommand + +luCommand( + "r1", 'vtysh -c "add vrf r1-cust1 prefix 99.0.0.1/32"', ".", "none", "IP Address" +) +luCommand( + "r3", 'vtysh -c "add vrf r3-cust1 prefix 99.0.0.2/32"', ".", "none", "IP Address" +) +luCommand( + "r4", 'vtysh -c "add vrf r4-cust1 prefix 99.0.0.3/32"', ".", "none", "IP Address" +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "99.0.0.1", + "pass", + "Local Registration", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations local"', + "99.0.0.2", + "pass", + "Local Registration", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations local"', + "99.0.0.3", + "pass", + "Local Registration", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations remote"', + "4 out of 4", + "wait", + "Remote Registration", + 10, +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations remote"', + "6 out of 6", + "wait", + "Remote Registration", + 10, +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations remote"', + "4 out of 4", + "wait", + "Remote Registration", + 10, +) +luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none") diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py new file mode 100644 index 0000000..f514575 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py @@ -0,0 +1,64 @@ +from lib.lutil import luCommand + +luCommand("ce1", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180) +luCommand("ce2", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180) +luCommand("ce3", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180) +luCommand( + "ce4", 'vtysh -c "show bgp vrf all summary"', " 00:0", "wait", "Adjacencies up", 180 +) +luCommand( + "r1", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r3", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r4", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r2", + 'vtysh -c "show bgp summary"', + " 00:0.* 00:0.* 00:0", + "wait", + "Core adjacencies up", + 180, +) +luCommand( + "r1", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180 +) +luCommand( + "r3", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180 +) +luCommand( + "r4", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180 +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf all summary"', + " 00:0.* 00:0", + "pass", + "All adjacencies up", +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf all summary"', + " 00:0.* 00:0", + "pass", + "All adjacencies up", +) +luCommand( + "r4", + 'vtysh -c "show bgp vrf all summary"', + " 00:0.* 00:0.* 00:0", + "pass", + "All adjacencies up", +) +luCommand( + "r1", "ping 3.3.3.3 -c 1", " 0. packet loss", "wait", "PE->PE3 (loopback) ping" +) +luCommand( + "r1", "ping 4.4.4.4 -c 1", " 0. packet loss", "wait", "PE->PE4 (loopback) ping" +) +luCommand( + "r4", "ping 3.3.3.3 -c 1", " 0. packet loss", "wait", "PE->PE3 (loopback) ping" +) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py new file mode 100644 index 0000000..91a7adf --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py @@ -0,0 +1,83 @@ +from lib.lutil import luCommand, luLast +from lib import topotest + +ret = luCommand( + "r2", + "ip -M route show", + r"\d*(?= via inet 10.0.2.4 dev r2-eth1)", + "wait", + "See mpls route to r4", +) +found = luLast() + +if ret != False and found != None: + label4r4 = found.group(0) + luCommand("r2", "ip -M route show", ".", "pass", "See %s as label to r4" % label4r4) + ret = luCommand( + "r2", + "ip -M route show", + r"\d*(?= via inet 10.0.1.1 dev r2-eth0)", + "wait", + "See mpls route to r1", + ) + found = luLast() + +if ret != False and found != None: + label4r1 = found.group(0) + luCommand("r2", "ip -M route show", ".", "pass", "See %s as label to r1" % label4r1) + + luCommand( + "r1", + "ip route show vrf r1-cust1", + "99.0.0.4", + "pass", + "VRF->MPLS PHP route installed", + ) + luCommand( + "r4", + "ip route show vrf r4-cust2", + "99.0.0.1", + "pass", + "VRF->MPLS PHP route installed", + ) + + luCommand("r1", "ip -M route show", "101", "pass", "MPLS->VRF route installed") + luCommand("r4", "ip -M route show", "1041", "pass", "MPLS->VRF1 route installed") + luCommand("r4", "ip -M route show", "1042", "pass", "MPLS->VRF2 route installed") + + luCommand( + "ce1", + "ping 99.0.0.4 -I 99.0.0.1 -c 1", + " 0. packet loss", + "wait", + "CE->CE (loopback) ping - l3vpn+zebra case", + ) + # skip due to VRF weirdness + # luCommand('ce4', 'ping 99.0.0.1 -I 99.0.0.4 -c 1', + # ' 0. packet loss','wait','CE->CE (loopback) ping - l3vpn+zebra case') + + luCommand( + "ce1", + "ping 99.0.0.4 -I 99.0.0.1 -c 1", + " 0. packet loss", + "wait", + "CE->CE (loopback) ping", + ) + # luCommand('ce4', 'ping 99.0.0.1 -I 99.0.0.4 -c 1', + # ' 0. packet loss','wait','CE->CE (loopback) ping') + + luCommand("r3", "ip -M route show", "103", "pass", "MPLS->VRF route installed") + luCommand( + "ce2", + "ping 99.0.0.3 -I 99.0.0.2 -c 1", + " 0. packet loss", + "wait", + "CE2->CE3 (loopback) ping", + ) + luCommand( + "ce3", + "ping 99.0.0.4 -I 99.0.0.3 -c 1", + " 0. packet loss", + "wait", + "CE3->CE4 (loopback) ping", + ) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py new file mode 100644 index 0000000..75158b1 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py @@ -0,0 +1,74 @@ +from lib.lutil import luCommand + +rtrs = ["r1", "r3", "r4"] +for rtr in rtrs: + luCommand( + rtr, + "ip link show type vrf {}-cust1".format(rtr), + "cust1: .*UP", + "pass", + "VRF cust1 intf up", + ) + luCommand( + rtr, + "ip add show vrf {}-cust1".format(rtr), + "r..eth4.*UP", + "pass", + "VRF cust1 IP intf up", + ) + luCommand( + rtr, + "ip add show vrf {}-cust1".format(rtr), + "192.168", + "pass", + "VRF cust1 IP config", + ) + luCommand( + rtr, + "ip route show vrf {}-cust1".format(rtr), + "192.168...0/24 dev r.-eth", + "pass", + "VRF cust1 interface route", + ) +luCommand("r4", "ip link show type vrf r4-cust2", "cust2: .*UP", "pass", "VRF cust2 up") +luCommand( + "r4", + "ip add show vrf r4-cust2", + "r..eth5.*UP.* 192.168", + "pass", + "VRF cust1 IP config", +) +luCommand( + "r4", + "ip route show vrf r4-cust2", + "192.168...0/24 dev r.-eth", + "pass", + "VRF cust2 interface route", +) +rtrs = ["ce1", "ce2", "ce3"] +for rtr in rtrs: + luCommand( + rtr, + "ip route show", + "192.168...0/24 dev ce.-eth0", + "pass", + "CE interface route", + ) + luCommand(rtr, "ping 192.168.1.1 -c 1", " 0. packet loss", "wait", "CE->PE ping") +luCommand( + "ce4", "ip link show type vrf ce4-cust2", "cust2: .*UP", "pass", "VRF cust2 up" +) +luCommand( + "ce4", + "ip route show vrf ce4-cust2", + "192.168...0/24 dev ce.-eth0", + "pass", + "CE interface route", +) +luCommand( + "ce4", + "ping 192.168.2.1 -c 1 -I ce4-cust2", + " 0. packet loss", + "wait", + "CE4->PE4 ping", +) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py new file mode 100644 index 0000000..1e2758c --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py @@ -0,0 +1,878 @@ +from lib.lutil import luCommand +from lib.bgprib import bgpribRequireVpnRoutes, bgpribRequireUnicastRoutes + +######################################################################## +# CE routers: contain routes they originate +######################################################################## +# +# mininet CLI commands +# ce1 vtysh -c "show bgp ipv4 uni" +# ce2 vtysh -c "show bgp ipv4 uni" +# ce3 vtysh -c "show bgp ipv4 uni" +# ce4 vtysh -c "show bgp ipv4 uni" + +want = [ + {"p": "5.1.0.0/24", "n": "99.0.0.1"}, + {"p": "5.1.1.0/24", "n": "99.0.0.1"}, + {"p": "6.0.1.0/24", "n": "99.0.0.1"}, + {"p": "6.0.2.0/24", "n": "99.0.0.1"}, + {"p": "99.0.0.1/32", "n": "0.0.0.0"}, +] +bgpribRequireUnicastRoutes("ce1", "ipv4", "", "Cust 1 routes in ce1", want) + +want = [ + {"p": "5.1.0.0/24", "n": "99.0.0.2"}, + {"p": "5.1.1.0/24", "n": "99.0.0.2"}, + {"p": "6.0.1.0/24", "n": "99.0.0.2"}, + {"p": "6.0.2.0/24", "n": "99.0.0.2"}, + {"p": "99.0.0.2/32", "n": "0.0.0.0"}, +] +bgpribRequireUnicastRoutes("ce2", "ipv4", "", "Cust 2 routes in ce1", want) + +want = [ + {"p": "5.1.2.0/24", "n": "99.0.0.3"}, + {"p": "5.1.3.0/24", "n": "99.0.0.3"}, + {"p": "6.0.1.0/24", "n": "99.0.0.3"}, + {"p": "6.0.2.0/24", "n": "99.0.0.3"}, + {"p": "99.0.0.3/32", "n": "0.0.0.0"}, +] +bgpribRequireUnicastRoutes("ce3", "ipv4", "", "Cust 3 routes in ce1", want) + +want = [ + {"p": "5.4.2.0/24", "n": "99.0.0.4"}, + {"p": "5.4.3.0/24", "n": "99.0.0.4"}, + {"p": "6.0.1.0/24", "n": "99.0.0.4"}, + {"p": "6.0.2.0/24", "n": "99.0.0.4"}, + {"p": "99.0.0.4/32", "n": "0.0.0.0"}, +] +bgpribRequireUnicastRoutes("ce4", "ipv4", "ce4-cust2", "Cust 4 routes in ce1", want) + + +######################################################################## +# PE routers: VRFs contain routes from locally-attached customer nets +######################################################################## +# +# r1 vtysh -c "show bgp vrf r1-cust1 ipv4" +# +want_r1_cust1_routes = [ + {"p": "5.1.0.0/24", "n": "99.0.0.1"}, + {"p": "5.1.1.0/24", "n": "99.0.0.1"}, + {"p": "6.0.1.0/24", "n": "99.0.0.1"}, + {"p": "6.0.2.0/24", "n": "99.0.0.1"}, + {"p": "99.0.0.1/32", "n": "192.168.1.2"}, +] +bgpribRequireUnicastRoutes( + "r1", "ipv4", "r1-cust1", "Customer 1 routes in r1 vrf", want_r1_cust1_routes +) + +want_r3_cust1_routes = [ + {"p": "5.1.0.0/24", "n": "99.0.0.2"}, + {"p": "5.1.1.0/24", "n": "99.0.0.2"}, + {"p": "6.0.1.0/24", "n": "99.0.0.2"}, + {"p": "6.0.2.0/24", "n": "99.0.0.2"}, + {"p": "99.0.0.2/32", "n": "192.168.1.2"}, +] +bgpribRequireUnicastRoutes( + "r3", "ipv4", "r3-cust1", "Customer 1 routes in r3 vrf", want_r3_cust1_routes +) + +want_r4_cust1_routes = [ + {"p": "5.1.2.0/24", "n": "99.0.0.3"}, + {"p": "5.1.3.0/24", "n": "99.0.0.3"}, + {"p": "6.0.1.0/24", "n": "99.0.0.3"}, + {"p": "6.0.2.0/24", "n": "99.0.0.3"}, + {"p": "99.0.0.3/32", "n": "192.168.1.2"}, +] +bgpribRequireUnicastRoutes( + "r4", "ipv4", "r4-cust1", "Customer 1 routes in r4 vrf", want_r4_cust1_routes +) + +want_r4_cust2_routes = [ + {"p": "5.4.2.0/24", "n": "99.0.0.4"}, + {"p": "5.4.3.0/24", "n": "99.0.0.4"}, + {"p": "6.0.1.0/24", "n": "99.0.0.4"}, + {"p": "6.0.2.0/24", "n": "99.0.0.4"}, + {"p": "99.0.0.4/32", "n": "192.168.2.2"}, +] +bgpribRequireUnicastRoutes( + "r4", "ipv4", "r4-cust2", "Customer 2 routes in r4 vrf", want_r4_cust2_routes +) + +######################################################################## +# PE routers: core unicast routes are empty +######################################################################## + +luCommand( + "r1", + 'vtysh -c "show bgp ipv4 uni"', + "No BGP prefixes displayed", + "pass", + "Core Unicast SAFI clean", +) +luCommand( + "r2", + 'vtysh -c "show bgp ipv4 uni"', + "No BGP prefixes displayed", + "pass", + "Core Unicast SAFI clean", +) +luCommand( + "r3", + 'vtysh -c "show bgp ipv4 uni"', + "No BGP prefixes displayed", + "pass", + "Core Unicast SAFI clean", +) +luCommand( + "r4", + 'vtysh -c "show bgp ipv4 uni"', + "No BGP prefixes displayed", + "pass", + "Core Unicast SAFI clean", +) + +######################################################################## +# PE routers: local ce-originated routes are leaked to vpn +######################################################################## + +# nhzero is for the new code that sets nh of locally-leaked routes to 0 +# nhzero = 1 +nhzero = 0 + +if nhzero: + luCommand( + "r1", + 'vtysh -c "show bgp ipv4 vpn"', + "Distinguisher: *10:1.*5.1.0.0/24 *0.0.0.0 .*5.1.1.0/24 *0.0.0.0 .*99.0.0.1/32 *0.0.0.0 ", + "pass", + "vrf->vpn routes", + ) + luCommand( + "r3", + 'vtysh -c "show bgp ipv4 vpn"', + "Distinguisher: *10:3.*5.1.0.0/24 *0.0.0.0 .*5.1.1.0/24 *0.0.0.0 .*99.0.0.2/32 *0.0.0.0 ", + "pass", + "vrf->vpn routes", + ) + want = [ + {"rd": "10:41", "p": "5.1.2.0/24", "n": "0.0.0.0"}, + {"rd": "10:41", "p": "5.1.3.0/24", "n": "0.0.0.0"}, + {"rd": "10:41", "p": "99.0.0.3/32", "n": "0.0.0.0"}, + {"rd": "10:42", "p": "5.4.2.0/24", "n": "0.0.0.0"}, + {"rd": "10:42", "p": "5.4.3.0/24", "n": "0.0.0.0"}, + {"rd": "10:42", "p": "99.0.0.4/32", "n": "0.0.0.0"}, + ] + bgpribRequireVpnRoutes("r4", "vrf->vpn routes", want) + +else: + luCommand( + "r1", + 'vtysh -c "show bgp ipv4 vpn"', + r"Distinguisher: *10:1.*5.1.0.0/24 *99.0.0.1\b.*5.1.1.0/24 *99.0.0.1\b.*6.0.1.0/24 *99.0.0.1\b.*6.0.2.0/24 *99.0.0.1\b.*99.0.0.1/32 *192.168.1.2\b", + "pass", + "vrf->vpn routes", + ) + luCommand( + "r3", + 'vtysh -c "show bgp ipv4 vpn"', + r"Distinguisher: *10:3.*5.1.0.0/24 *99.0.0.2\b.*5.1.1.0/24 *99.0.0.2\b.*6.0.1.0/24 *99.0.0.2\b.*6.0.2.0/24 *99.0.0.2\b.*99.0.0.2/32 *192.168.1.2\b", + "pass", + "vrf->vpn routes", + ) + want = [ + {"rd": "10:41", "p": "5.1.2.0/24", "n": "99.0.0.3"}, + {"rd": "10:41", "p": "5.1.3.0/24", "n": "99.0.0.3"}, + {"rd": "10:41", "p": "6.0.1.0/24", "n": "99.0.0.3"}, + {"rd": "10:41", "p": "6.0.2.0/24", "n": "99.0.0.3"}, + {"rd": "10:41", "p": "99.0.0.3/32", "n": "192.168.1.2"}, + {"rd": "10:42", "p": "5.4.2.0/24", "n": "99.0.0.4"}, + {"rd": "10:42", "p": "5.4.3.0/24", "n": "99.0.0.4"}, + {"rd": "10:42", "p": "6.0.1.0/24", "n": "99.0.0.4"}, + {"rd": "10:42", "p": "6.0.2.0/24", "n": "99.0.0.4"}, + {"rd": "10:42", "p": "99.0.0.4/32", "n": "192.168.2.2"}, + ] + bgpribRequireVpnRoutes("r4", "vrf->vpn routes", want) + +######################################################################## +# PE routers: exporting vrfs set MPLS vrf labels in kernel +######################################################################## + +luCommand( + "r1", 'vtysh -c "show mpls table"', " 101 *BGP *r1-cust1", "pass", "vrf labels" +) +luCommand( + "r3", 'vtysh -c "show mpls table"', " 103 *BGP *r3-cust1", "pass", "vrf labels" +) +luCommand( + "r4", + 'vtysh -c "show mpls table"', + " 1041 *BGP *r4-cust1 .*1042 *BGP *r4-cust2", + "pass", + "vrf labels", +) + +######################################################################## +# Core VPN router: all customer routes +######################################################################## + +want_rd_routes = [ + {"rd": "10:1", "p": "5.1.0.0/24", "n": "1.1.1.1"}, + {"rd": "10:1", "p": "5.1.0.0/24", "n": "1.1.1.1"}, + {"rd": "10:1", "p": "99.0.0.1/32", "n": "1.1.1.1"}, + {"rd": "10:3", "p": "5.1.0.0/24", "n": "3.3.3.3"}, + {"rd": "10:3", "p": "5.1.0.0/24", "n": "3.3.3.3"}, + {"rd": "10:3", "p": "99.0.0.2/32", "n": "3.3.3.3"}, + {"rd": "10:41", "p": "5.1.2.0/24", "n": "4.4.4.4"}, + {"rd": "10:41", "p": "5.1.3.0/24", "n": "4.4.4.4"}, + {"rd": "10:41", "p": "99.0.0.3/32", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "5.4.2.0/24", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "5.4.3.0/24", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "99.0.0.4/32", "n": "4.4.4.4"}, +] +bgpribRequireVpnRoutes("r2", "Customer routes in provider vpn core", want_rd_routes) + +######################################################################## +# PE routers: VPN routes from remote customers +######################################################################## +# +# r1 vtysh -c "show bgp ipv4 vpn" +# +want_r1_remote_vpn_routes = [ + {"rd": "10:3", "p": "5.1.0.0/24", "n": "3.3.3.3"}, + {"rd": "10:3", "p": "5.1.1.0/24", "n": "3.3.3.3"}, + {"rd": "10:3", "p": "99.0.0.2/32", "n": "3.3.3.3"}, + {"rd": "10:41", "p": "5.1.2.0/24", "n": "4.4.4.4"}, + {"rd": "10:41", "p": "5.1.3.0/24", "n": "4.4.4.4"}, + {"rd": "10:41", "p": "99.0.0.3/32", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "5.4.2.0/24", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "5.4.3.0/24", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "99.0.0.4/32", "n": "4.4.4.4"}, +] +bgpribRequireVpnRoutes( + "r1", "Remote Customer routes in R1 vpn", want_r1_remote_vpn_routes +) + +want_r3_remote_vpn_routes = [ + {"rd": "10:1", "p": "5.1.0.0/24", "n": "1.1.1.1"}, + {"rd": "10:1", "p": "5.1.1.0/24", "n": "1.1.1.1"}, + {"rd": "10:1", "p": "99.0.0.1/32", "n": "1.1.1.1"}, + {"rd": "10:41", "p": "5.1.2.0/24", "n": "4.4.4.4"}, + {"rd": "10:41", "p": "5.1.3.0/24", "n": "4.4.4.4"}, + {"rd": "10:41", "p": "99.0.0.3/32", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "5.4.2.0/24", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "5.4.3.0/24", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "99.0.0.4/32", "n": "4.4.4.4"}, +] +bgpribRequireVpnRoutes( + "r3", "Remote Customer routes in R3 vpn", want_r3_remote_vpn_routes +) + +want_r4_remote_vpn_routes = [ + {"rd": "10:1", "p": "5.1.0.0/24", "n": "1.1.1.1"}, + {"rd": "10:1", "p": "5.1.1.0/24", "n": "1.1.1.1"}, + {"rd": "10:1", "p": "99.0.0.1/32", "n": "1.1.1.1"}, + {"rd": "10:3", "p": "5.1.0.0/24", "n": "3.3.3.3"}, + {"rd": "10:3", "p": "5.1.1.0/24", "n": "3.3.3.3"}, + {"rd": "10:3", "p": "99.0.0.2/32", "n": "3.3.3.3"}, +] +bgpribRequireVpnRoutes( + "r4", "Remote Customer routes in R4 vpn", want_r4_remote_vpn_routes +) + + +# r1 vtysh -c "show bgp vrf r1-cust1 ipv4" +######################################################################## +# PE routers: VRFs contain routes from remote customer nets +######################################################################## +# First let's spot check and ensure that some of the routes +# have showed up and been best path'd +# After the first two are good. It's probably ok +# to look at the rest of the routes in the vrf +luCommand( + "r1", + 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 5.1.0.0/24"', + "2 available, best", + "wait", + "Ensure 5.1.0.0 shows up on r1", + 10, +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 5.1.1.0/24"', + "2 available, best", + "wait", + "Ensure 5.1.1.0 shows up on r1", + 10, +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 5.1.2.0/24"', + "1 available, best", + "wait", + "Ensure 5.1.2.0 shows up on r1", + 10, +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 5.1.3.0/24"', + "1 available, best", + "wait", + "Ensure 5.1.3.0 shows up on r1", + 10, +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 5.4.2.0/24"', + "1 available, best", + "wait", + "Ensure 5.4.2.0 shows up on r1", + 10, +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 5.4.2.0/24"', + "1 available, best", + "wait", + "Ensure 5.4.3.0 shows up on r1", + 10, +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 6.0.1.0/24"', + "4 available, best", + "wait", + "Ensure 6.0.1.0 shows up on r1", + 10, +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 6.0.2.0/24"', + "4 available, best", + "wait", + "Ensure 6.0.2.0 shows up on r1", + 10, +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 99.0.0.1/32"', + "1 available, best", + "wait", + "Ensure 99.0.0.1 shows up on r1", + 10, +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 99.0.0.2/32"', + "1 available, best", + "wait", + "Ensure 99.0.0.2 shows up on r1", + 10, +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 99.0.0.3/32"', + "1 available, best", + "wait", + "Ensure 99.0.0.3 shows up on r1", + 10, +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 99.0.0.4/32"', + "1 available, best", + "wait", + "Ensure 99.0.0.4 shows up on r1", + 10, +) +want_r1_remote_cust1_routes = [ + {"p": "5.1.0.0/24", "n": "3.3.3.3", "bp": False}, + {"p": "5.1.0.0/24", "n": "99.0.0.1", "bp": True}, + {"p": "5.1.1.0/24", "n": "3.3.3.3", "bp": False}, + {"p": "5.1.1.0/24", "n": "99.0.0.1", "bp": True}, + {"p": "5.1.2.0/24", "n": "4.4.4.4"}, + {"p": "5.1.3.0/24", "n": "4.4.4.4"}, + {"p": "5.4.2.0/24", "n": "4.4.4.4"}, + {"p": "5.4.3.0/24", "n": "4.4.4.4"}, + {"p": "6.0.1.0/24", "n": "3.3.3.3", "bp": False}, + {"p": "6.0.1.0/24", "n": "4.4.4.4", "bp": False}, + {"p": "6.0.1.0/24", "n": "99.0.0.1", "bp": True}, + {"p": "6.0.2.0/24", "n": "3.3.3.3", "bp": False}, + {"p": "6.0.2.0/24", "n": "4.4.4.4", "bp": False}, + {"p": "6.0.2.0/24", "n": "99.0.0.1", "bp": True}, + {"p": "99.0.0.1/32", "n": "192.168.1.2", "bp": True}, + {"p": "99.0.0.2/32", "n": "3.3.3.3"}, + {"p": "99.0.0.3/32", "n": "4.4.4.4"}, + {"p": "99.0.0.4/32", "n": "4.4.4.4"}, +] +bgpribRequireUnicastRoutes( + "r1", + "ipv4", + "r1-cust1", + "Customer 1 routes in r1 vrf (2)", + want_r1_remote_cust1_routes, + debug=False, +) + + +luCommand( + "r3", + 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 5.1.0.0/24"', + "2 available, best", + "wait", + "Ensure 5.1.0.0 shows up r3", + 10, +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 5.1.1.0/24"', + "2 available, best", + "wait", + "Ensure 5.1.1.0 shows up on r3", + 10, +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 5.1.2.0/24"', + "1 available, best", + "wait", + "Ensure 5.1.2.0 shows up on r3", + 10, +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 5.1.3.0/24"', + "1 available, best", + "wait", + "Ensure 5.1.3.0 shows up on r3", + 10, +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 5.4.3.0/24"', + "1 available, best", + "wait", + "Ensure 5.4.3.0 shows up on r3", + 10, +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 5.4.3.0/24"', + "1 available, best", + "wait", + "Ensure 5.4.3.0 shows up on r3", + 10, +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 5.4.3.0/24"', + "1 available, best", + "wait", + "Ensure 5.4.3.0 shows up on r3", + 10, +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 6.0.1.0/24"', + "4 available, best", + "wait", + "Ensure 6.0.1.0 shows up on r3", + 10, +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 6.0.2.0/24"', + "4 available, best", + "wait", + "Ensure 6.0.2.0 shows up on r3", + 10, +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 99.0.0.1/32"', + "1 available, best", + "wait", + "Ensure 99.0.0.1 shows up on r3", + 10, +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 99.0.0.3/32"', + "1 available, best", + "wait", + "Ensure 99.0.0.3 shows up on r3", + 10, +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 99.0.0.4/32"', + "1 available, best", + "wait", + "Ensure 99.0.0.4 shows up on r3", + 10, +) +want_r3_remote_cust1_routes = [ + {"p": "5.1.0.0/24", "n": "1.1.1.1", "bp": True}, + {"p": "5.1.0.0/24", "n": "99.0.0.2", "bp": False}, + {"p": "5.1.1.0/24", "n": "1.1.1.1", "bp": True}, + {"p": "5.1.1.0/24", "n": "99.0.0.2", "bp": False}, + {"p": "5.1.2.0/24", "n": "4.4.4.4", "bp": True}, + {"p": "5.1.3.0/24", "n": "4.4.4.4", "bp": True}, + {"p": "5.4.2.0/24", "n": "4.4.4.4", "bp": True}, + {"p": "5.4.3.0/24", "n": "4.4.4.4", "bp": True}, + {"p": "6.0.1.0/24", "n": "1.1.1.1", "bp": True}, + {"p": "6.0.1.0/24", "n": "4.4.4.4", "bp": False}, + {"p": "6.0.1.0/24", "n": "99.0.0.2", "bp": False}, + {"p": "6.0.2.0/24", "n": "1.1.1.1", "bp": False}, + {"p": "6.0.2.0/24", "n": "4.4.4.4", "bp": False}, + {"p": "6.0.2.0/24", "n": "99.0.0.2", "bp": True}, + {"p": "99.0.0.1/32", "n": "1.1.1.1", "bp": True}, + {"p": "99.0.0.3/32", "n": "4.4.4.4", "bp": True}, + {"p": "99.0.0.4/32", "n": "4.4.4.4", "bp": True}, +] +bgpribRequireUnicastRoutes( + "r3", + "ipv4", + "r3-cust1", + "Customer 1 routes in r3 vrf (2)", + want_r3_remote_cust1_routes, + debug=False, +) + +luCommand( + "r4", + 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 5.1.0.0/24"', + "2 available, best", + "wait", + "Ensure 5.1.0.0 shows up on r4", + 10, +) +luCommand( + "r4", + 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 5.1.1.0/24"', + "2 available, best", + "wait", + "Ensure 5.1.1.0 shows up on r4", + 10, +) +luCommand( + "r4", + 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 6.0.1.0/24"', + "4 available, best", + "wait", + "Ensure 6.0.1.0 shows up on r4", + 10, +) +luCommand( + "r4", + 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 6.0.2.0/24"', + "4 available, best", + "wait", + "Ensure 6.0.2.0 shows up on r4", + 10, +) +luCommand( + "r4", + 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 99.0.0.1/32"', + "1 available, best", + "wait", + "Ensure 99.0.0.1 shows up on r4", + 10, +) +luCommand( + "r4", + 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 99.0.0.2/32"', + "1 available, best", + "wait", + "Ensure 99.0.0.2 shows up on r4", + 10, +) +luCommand( + "r4", + 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 99.0.0.3/32"', + "1 available, best", + "wait", + "Ensure 99.0.0.3 shows up on r4", + 10, +) +luCommand( + "r4", + 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 99.0.0.4/32"', + "1 available, best", + "wait", + "Ensure 99.0.0.4 shows up on r4", + 10, +) +want_r4_remote_cust1_routes = [ + {"p": "5.1.0.0/24", "n": "1.1.1.1", "bp": True}, + {"p": "5.1.0.0/24", "n": "3.3.3.3", "bp": False}, + {"p": "5.1.1.0/24", "n": "1.1.1.1", "bp": True}, + {"p": "5.1.1.0/24", "n": "3.3.3.3", "bp": False}, + {"p": "6.0.1.0/24", "n": "1.1.1.1", "bp": True}, + {"p": "6.0.1.0/24", "n": "3.3.3.3", "bp": False}, + {"p": "6.0.1.0/24", "n": "99.0.0.3", "bp": False}, + {"p": "6.0.1.0/24", "n": "99.0.0.4", "bp": False}, + {"p": "6.0.2.0/24", "n": "1.1.1.1", "bp": False}, + {"p": "6.0.2.0/24", "n": "3.3.3.3", "bp": False}, + {"p": "6.0.2.0/24", "n": "99.0.0.3", "bp": True}, + {"p": "6.0.2.0/24", "n": "99.0.0.4", "bp": False}, + {"p": "99.0.0.1/32", "n": "1.1.1.1", "bp": True}, + {"p": "99.0.0.2/32", "n": "3.3.3.3", "bp": True}, + {"p": "99.0.0.3/32", "n": "192.168.1.2", "bp": True}, + {"p": "99.0.0.4/32", "n": "192.168.2.2", "bp": True}, +] +bgpribRequireUnicastRoutes( + "r4", + "ipv4", + "r4-cust1", + "Customer 1 routes in r4 vrf (2)", + want_r4_remote_cust1_routes, + debug=False, +) + +want_r4_remote_cust2_routes = [ + {"p": "5.1.0.0/24", "n": "1.1.1.1", "bp": True}, + {"p": "5.1.0.0/24", "n": "3.3.3.3", "bp": False}, + {"p": "5.1.1.0/24", "n": "1.1.1.1", "bp": True}, + {"p": "5.1.1.0/24", "n": "3.3.3.3", "bp": False}, + {"p": "6.0.1.0/24", "n": "1.1.1.1", "bp": True}, + {"p": "6.0.1.0/24", "n": "3.3.3.3", "bp": False}, + {"p": "6.0.1.0/24", "n": "99.0.0.3", "bp": False}, + {"p": "6.0.1.0/24", "n": "99.0.0.4", "bp": False}, + {"p": "6.0.2.0/24", "n": "1.1.1.1", "bp": False}, + {"p": "6.0.2.0/24", "n": "3.3.3.3", "bp": False}, + {"p": "6.0.2.0/24", "n": "99.0.0.3", "bp": True}, + {"p": "6.0.2.0/24", "n": "99.0.0.4", "bp": False}, + {"p": "99.0.0.1/32", "n": "1.1.1.1", "bp": True}, + {"p": "99.0.0.2/32", "n": "3.3.3.3", "bp": True}, + {"p": "99.0.0.3/32", "n": "192.168.1.2", "bp": True}, + {"p": "99.0.0.4/32", "n": "192.168.2.2", "bp": True}, +] +bgpribRequireUnicastRoutes( + "r4", + "ipv4", + "r4-cust2", + "Customer 2 routes in r4 vrf (2)", + want_r4_remote_cust2_routes, + debug=False, +) + + +######################################################################### +# CE routers: contain routes from remote customer nets +######################################################################### +# ce1 vtysh -c "show bgp ipv4 uni" +# r1 vtysh -c "show bgp vrf r1-cust1 ipv4" +# r1 vtysh -c "show bgp vrf r1-cust1 ipv4 5.1.2.0/24" + +luCommand( + "ce1", + 'vtysh -c "show bgp ipv4 uni"', + "12 routes and 12", + "wait", + "Local and remote routes", + 10, +) +want = [ + {"p": "5.1.0.0/24", "n": "99.0.0.1", "bp": True}, + {"p": "5.1.1.0/24", "n": "99.0.0.1", "bp": True}, + {"p": "5.1.2.0/24", "n": "192.168.1.1", "bp": True}, + {"p": "5.1.3.0/24", "n": "192.168.1.1", "bp": True}, + {"p": "5.4.2.0/24", "n": "192.168.1.1", "bp": True}, + {"p": "5.4.3.0/24", "n": "192.168.1.1", "bp": True}, + {"p": "6.0.1.0/24", "n": "99.0.0.1", "bp": True}, + {"p": "6.0.2.0/24", "n": "99.0.0.1", "bp": True}, +] +bgpribRequireUnicastRoutes( + "ce1", "ipv4", "", "Cust 1 routes from remote", want, debug=False +) + +luCommand( + "ce2", + 'vtysh -c "show bgp ipv4 uni"', + "12 routes and 15", + "wait", + "Local and remote routes", + 10, +) +want = [ + {"p": "5.1.0.0/24", "n": "192.168.1.1", "bp": False}, + {"p": "5.1.0.0/24", "n": "99.0.0.2", "bp": True}, + {"p": "5.1.1.0/24", "n": "192.168.1.1", "bp": False}, + {"p": "5.1.1.0/24", "n": "99.0.0.2", "bp": True}, + {"p": "5.1.2.0/24", "n": "192.168.1.1", "bp": True}, + {"p": "5.1.3.0/24", "n": "192.168.1.1", "bp": True}, + {"p": "5.4.2.0/24", "n": "192.168.1.1", "bp": True}, + {"p": "5.4.3.0/24", "n": "192.168.1.1", "bp": True}, + {"p": "6.0.1.0/24", "n": "192.168.1.1", "bp": False}, + {"p": "6.0.1.0/24", "n": "99.0.0.2", "bp": True}, + {"p": "6.0.2.0/24", "n": "99.0.0.2", "bp": True}, +] +bgpribRequireUnicastRoutes( + "ce2", "ipv4", "", "Cust 1 routes from remote", want, debug=False +) + +# human readable output for debugging +luCommand("r4", 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni"') +luCommand("r4", 'vtysh -c "show bgp vrf r4-cust2 ipv4 uni"') +luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"') +luCommand("r4", 'vtysh -c "show ip route vrf r4-cust1"') +luCommand("r4", 'vtysh -c "show ip route vrf r4-cust2"') + +luCommand( + "ce3", + 'vtysh -c "show bgp ipv4 uni"', + "12 routes and 13", + "wait", + "Local and remote routes", + 10, +) +# Requires bvl-bug-degenerate-no-label fix (FRR PR #2053) +want = [ + {"p": "5.1.0.0/24", "n": "192.168.1.1", "bp": True}, + {"p": "5.1.1.0/24", "n": "192.168.1.1", "bp": True}, + {"p": "5.4.2.0/24", "n": "192.168.1.1", "bp": True}, + {"p": "5.4.3.0/24", "n": "192.168.1.1", "bp": True}, + {"p": "6.0.1.0/24", "n": "192.168.1.1", "bp": False}, + {"p": "6.0.1.0/24", "n": "99.0.0.3", "bp": True}, + {"p": "6.0.2.0/24", "n": "99.0.0.3", "bp": True}, +] +bgpribRequireUnicastRoutes( + "ce3", "ipv4", "", "Cust 1 routes from remote", want, debug=False +) + +luCommand( + "ce4", + 'vtysh -c "show bgp vrf ce4-cust2 ipv4 uni"', + "12 routes and 14", + "wait", + "Local and remote routes", + 10, +) +want = [ + {"p": "5.1.0.0/24", "n": "192.168.2.1", "bp": True}, + {"p": "5.1.1.0/24", "n": "192.168.2.1", "bp": True}, + {"p": "5.1.2.0/24", "n": "192.168.2.1", "bp": True}, + {"p": "5.1.3.0/24", "n": "192.168.2.1", "bp": True}, + {"p": "6.0.1.0/24", "n": "192.168.2.1", "bp": False}, + {"p": "6.0.2.0/24", "n": "192.168.2.1", "bp": False}, + {"p": "6.0.1.0/24", "n": "99.0.0.4", "bp": True}, + {"p": "6.0.2.0/24", "n": "99.0.0.4", "bp": True}, +] +bgpribRequireUnicastRoutes( + "ce4", "ipv4", "ce4-cust2", "Cust 2 routes from remote", want, debug=False +) + +# verify details of exported/imported routes +luCommand( + "ce1", + 'vtysh -c "show bgp ipv4 uni 6.0.1.0"', + "1 available.*192.168.1.1.*99.0.0.1.*Community: 0:67.*Extended Community: RT:89:123.*Large Community: 12:34:56", + "pass", + "Redundant route 1 details", +) +luCommand( + "ce2", + 'vtysh -c "show bgp ipv4 uni 6.0.1.0"', + "2 available, best .*192.168.1.1.* Local.* 192.168.1.1 from 192.168.1.1 .192.168.1.1" + + ".* Origin IGP, metric 98, localpref 123, valid, internal" + + ".* Community: 0:67.* Extended Community: RT:52:100 RT:89:123.* Large Community: 12:34:56", + "pass", + "Redundant route 1 details", +) +luCommand( + "ce2", + 'vtysh -c "show bgp ipv4 uni 6.0.1.0"', + "2 available, best .*192.168.1.1.* Local.* 99.0.0.2 from 0.0.0.0 .99.0.0.2" + + ".* Origin IGP, metric 100, localpref 100, weight 32768, valid, sourced, local, best .Weight" + + ".* Community: 0:67.* Extended Community: RT:89:123.* Large Community: 12:34:56", + "pass", + "Redundant route 1 details", +) +luCommand( + "ce3", + 'vtysh -c "show bgp ipv4 uni 6.0.1.0"', + "2 available, best " + ".* Local.* 99.0.0.3 from 0.0.0.0 .99.0.0.3" + + ".* Origin IGP, metric 200, localpref 50, weight 32768, valid, sourced, local, best .Weight" + + ".* Community: 0:67.* Extended Community: RT:89:123.* Large Community: 12:34:56", + "pass", + "Redundant route 1 details", +) +luCommand( + "ce3", + 'vtysh -c "show bgp ipv4 uni 6.0.1.0"', + "2 available, best " + ".* Local.* 192.168.1.1 from 192.168.1.1 .192.168.1.1" + + ".* Origin IGP, metric 98, localpref 123, valid, internal" + + ".* Community: 0:67.* Extended Community: RT:52:100 RT:89:123.* Large Community: 12:34:56", + "pass", + "Redundant route 1 details", +) +luCommand( + "ce4", + 'vtysh -c "show bgp vrf ce4-cust2 ipv4 6.0.1.0 json"', + ( + '{"paths":[' + + '{"aspath":{"string":"Local"},"origin":"IGP","metric":200,"locPrf":50,' + + '"weight":32768,"valid":true,"sourced":true,"local":true,' + + '"bestpath":{"overall":true,"selectionReason":"Weight"},' + + '"community":{"string":"0:67"},"extendedCommunity":{"string":"RT:89:123"},' + + '"largeCommunity":{"string":"12:34:56"},' + + '"peer":{"peerId":"0.0.0.0","routerId":"99.0.0.4"}},' + + '{"aspath":{"string":"Local"},"origin":"IGP","metric":98,"locPrf":123,' + + '"valid":true,' + + '"community":{"string":"0:67"},"extendedCommunity":{' + + '"string":"RT:52:100 RT:89:123"},"largeCommunity":{"string":"12:34:56"},' + + '"peer":{"peerId":"192.168.2.1","routerId":"192.168.2.1"}}' + + "]}" + ), + "jsoncmp_pass", + "Redundant route 1 details", +) + +luCommand( + "ce1", + 'vtysh -c "show bgp ipv4 uni 6.0.2.0"', + "1 available, best .*192.168.1.1.* Local.* 99.0.0.1 from 0.0.0.0 .99.0.0.1" + + ".* Origin IGP, metric 100, localpref 100, weight 32768, valid, sourced, local, best .First path received" + + ".* Community: 0:67.* Extended Community: RT:89:123.* Large Community: 12:34:11", + "pass", + "Route 2 details", +) +luCommand( + "ce2", + 'vtysh -c "show bgp ipv4 uni 6.0.2.0"', + "1 available, best .*192.168.1.1.* Local.* 99.0.0.2 from 0.0.0.0 .99.0.0.2" + + ".* Origin IGP, metric 100, localpref 100, weight 32768, valid, sourced, local, best .First path received" + + ".* Community: 0:67.* Extended Community: RT:89:123.* Large Community: 12:34:12", + "pass", + "Route 2 details", +) +luCommand( + "ce3", + 'vtysh -c "show bgp ipv4 uni 6.0.2.0"', + "1 available, best .*192.168.1.1.* Local.* 99.0.0.3 from 0.0.0.0 .99.0.0.3" + + ".* Origin IGP, metric 100, localpref 100, weight 32768, valid, sourced, local, best .First path received" + + ".* Community: 0:67.* Extended Community: RT:89:123.* Large Community: 12:34:13", + "pass", + "Route 2 details", +) +luCommand( + "ce4", + 'vtysh -c "show bgp vrf ce4-cust2 ipv4 6.0.2.0"', + "2 available, best .*192.168.2.1.* Local.* 192.168.2.1 from 192.168.2.1 .192.168.2.1" + + ".* Origin IGP, metric 100, localpref 100, valid, internal" + + ".* Community: 0:67.* Extended Community: RT:52:100 RT:89:123.* Large Community: 12:34:13", + "pass", + "Redundant route 2 details", +) +luCommand( + "ce4", + 'vtysh -c "show bgp vrf ce4-cust2 ipv4 6.0.2.0"', + "2 available, best .*192.168.2.1.* Local.* 99.0.0.4 from 0.0.0.0 .99.0.0.4" + + ".* Origin IGP, metric 100, localpref 100, weight 32768, valid, sourced, local, best .Weight" + + ".* Community: 0:67.* Extended Community: RT:89:123.* Large Community: 12:34:14", + "pass", + "Redundant route 2 details", +) +# done diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/cleanup_all.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/cleanup_all.py new file mode 100644 index 0000000..a27b178 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/cleanup_all.py @@ -0,0 +1,120 @@ +from lib.lutil import luCommand + +luCommand( + "r1", + 'vtysh -c "clear vrf r1-cust1 prefix 99.0.0.1/32"', + ".", + "none", + "Cleared VRF route", +) +luCommand( + "r3", + 'vtysh -c "clear vrf r3-cust1 prefix 99.0.0.2/32"', + ".", + "none", + "Cleared VRF route", +) +luCommand( + "r4", + 'vtysh -c "clear vrf r3-cust1 prefix 99.0.0.3/32"', + ".", + "none", + "Cleared VRF route", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "99.0.0.1", + "fail", + "Local Registration cleared", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations local"', + "99.0.0.2", + "fail", + "Local Registration cleared", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations local"', + "99.0.0.3", + "fail", + "Local Registration cleared", +) +luCommand( + "r1", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Unicast SAFI updated", + 10, +) +luCommand( + "r2", + 'vtysh -c "show bgp ipv4 uni"', + "No BGP prefixes displayed", + "pass", + "Unicast SAFI", +) +luCommand( + "r3", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Unicast SAFI updated", + 10, +) +luCommand( + "r4", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Unicast SAFI updated", + 10, +) +luCommand( + "ce1", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Local and remote routes", + 10, +) +luCommand( + "ce2", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Local and remote routes", + 10, +) +luCommand( + "ce3", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Local and remote routes", + 10, +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations remote"', + "Prefix ", + "fail", + "Remote Registration cleared", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations remote"', + "Prefix ", + "fail", + "Remote Registration cleared", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations remote"', + "Prefix ", + "fail", + "Remote Registration cleared", +) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/del_bgp_instances.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/del_bgp_instances.py new file mode 100644 index 0000000..fcbc3df --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/del_bgp_instances.py @@ -0,0 +1,30 @@ +from lib.lutil import luCommand + +luCommand( + "r1", + '/usr/lib/frr/vtysh -c "conf ter" -c "no router bgp 5227 vrf r1-cust1" -c "no router bgp 5226"', + ".", + "none", + "Cleared bgp instances", +) +luCommand( + "r2", + '/usr/lib/frr/vtysh -c "conf ter" -c "no router bgp 5226"', + ".", + "none", + "Cleared bgp instances", +) +luCommand( + "r3", + '/usr/lib/frr/vtysh -c "conf ter" -c "no router bgp 5227 vrf r3-cust1" -c "no router bgp 5226"', + ".", + "none", + "Cleared bgp instances", +) +luCommand( + "r4", + '/usr/lib/frr/vtysh -c "conf ter" -c "no router bgp 5228 vrf r4-cust2" -c "no router bgp 5227 vrf r4-cust1" -c "no router bgp 5226"', + ".", + "none", + "Cleared bgp instances", +) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/notification_check.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/notification_check.py new file mode 100644 index 0000000..73cd08f --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/notification_check.py @@ -0,0 +1,22 @@ +from lib.lutil import luCommand, luLast + +rtrs = ["ce1", "ce2", "ce3", "r1", "r2", "r3", "r4"] +for rtr in rtrs: + ret = luCommand( + rtr, + 'vtysh -c "show bgp neigh"', + "Notification received .([A-Za-z0-9/ ]*)", + "none", + "collect neighbor stats", + ) + found = luLast() + if ret != False and found != None: + val = found.group(1) + ret = luCommand( + rtr, + 'vtysh -c "show bgp neigh"', + "Notification received", + "fail", + "Notify RXed! {}".format(val), + ) +# done diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py new file mode 100644 index 0000000..36be926 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py @@ -0,0 +1,87 @@ +from lib.lutil import luCommand, luLast + +ret = luCommand( + "ce1", + 'vtysh -c "show ip route" | grep -c \\ 10\\.\\*/32', + "(.*)", + "pass", + "Looking for sharp routes", +) +found = luLast() +if ret != False and found != None: + num = int(found.group()) + luCommand( + "ce3", 'vtysh -c "show bgp sum"', ".", "pass", "See %s sharp routes" % num + ) + if num > 0: + rtrs = ["ce1", "ce2", "ce3"] + for rtr in rtrs: + luCommand( + rtr, + 'vtysh -c "show bgp ipv4 uni" | grep Display', + ".", + "none", + "BGP routes pre remove", + ) + luCommand( + rtr, + "ip route show | cat -n | tail", + ".", + "none", + "Linux routes pre remove", + ) + wait = 2 * num / 500 + luCommand( + "ce1", + 'vtysh -c "sharp remove routes 10.0.0.0 {}"'.format(num), + ".", + "none", + "Removing {} routes".format(num), + ) + luCommand( + "ce2", + 'vtysh -c "sharp remove routes 10.0.0.0 {}"'.format(num), + ".", + "none", + "Removing {} routes".format(num), + ) + for rtr in rtrs: + luCommand( + rtr, + 'vtysh -c "show bgp ipv4 uni" | grep Display', + " 12 route", + "wait", + "BGP routes removed", + wait, + wait_time=10, + ) + luCommand( + rtr, + 'vtysh -c "show bgp ipv4 uni"', + ".", + "none", + "BGP routes post remove", + ) + for rtr in rtrs: + luCommand( + rtr, + "ip route show | grep -c \\^10\\.", + "^0$", + "wait", + "Linux routes removed", + wait, + wait_time=10, + ) + luCommand(rtr, "ip route show", ".", "none", "Linux routes post remove") + rtrs = ["r1", "r3", "r4"] + for rtr in rtrs: + luCommand( + rtr, + "ip route show vrf {}-cust1 | grep -c \\^10\\.".format(rtr), + "^0$", + "wait", + "VRF route removed", + wait, + wait_time=10, + ) +# done diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py new file mode 100644 index 0000000..46993c7 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py @@ -0,0 +1,253 @@ +from lib.lutil import luCommand, luLast + +num = 50000 +b = int(num / (256 * 256)) +if b > 0: + r = num - b * (256 * 256) +else: + r = num +c = int(r / 256) +if c > 0: + d = r - c * 256 - 1 +else: + d = r +wait = 2 * num / 1000 +mem_z = {} +mem_b = {} +rtrs = ["ce1", "ce2", "ce3", "r1", "r2", "r3", "r4"] +for rtr in rtrs: + mem_z[rtr] = {"value": 0, "units": "unknown"} + mem_b[rtr] = {"value": 0, "units": "unknown"} + ret = luCommand( + rtr, + 'vtysh -c "show memory"', + r"zebra: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*) .*bgpd: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*)", + "none", + "collect bgpd memory stats", + ) + found = luLast() + if ret != False and found != None: + mem_z[rtr] = {"value": int(found.group(1)), "units": found.group(2)} + mem_b[rtr] = {"value": int(found.group(3)), "units": found.group(4)} + +luCommand( + "ce1", 'vtysh -c "show mem"', "qmem sharpd", "none", "check if sharpd running" +) +doSharp = False +found = luLast() +if ret != False and found != None: + if len(found.group()): + doSharp = True + +if doSharp != True: + luCommand( + "ce1", + 'vtysh -c "sharp data nexthop"', + ".", + "pass", + "sharpd NOT running, skipping test", + ) +else: + luCommand( + "ce1", + 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.1 {}"'.format(num), + "", + "pass", + "Adding {} routes".format(num), + ) + luCommand( + "ce2", + 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.2 {}"'.format(num), + "", + "pass", + "Adding {} routes".format(num), + ) + luCommand( + "ce1", + 'vtysh -c "show ip route summ" | grep "sharp" | cut -d " " -f 33', + str(num), + "wait", + "See all sharp routes in rib on ce1", + wait, + wait_time=10, + ) + luCommand( + "ce2", + 'vtysh -c "show ip route summ" | grep "sharp" | cut -d " " -f 33', + str(num), + "wait", + "See all sharp routes in rib on ce2", + wait, + wait_time=10, + ) + + rtrs = ["ce1", "ce2", "ce3"] + for rtr in rtrs: + luCommand( + rtr, + 'vtysh -c "show bgp ipv4 uni 10.{}.{}.{}"'.format(b, c, d), + "Last update:", + "wait", + "RXed last route, 10.{}.{}.{}".format(b, c, d), + wait, + wait_time=10, + ) + luCommand( + rtr, + 'vtysh -c "show bgp ipv4 uni" | grep -c 10\\.\\*/32', + str(num), + "wait", + "See all sharp routes in BGP", + wait, + wait_time=10, + ) + luCommand( + "r1", + 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 10.{}.{}.{}"'.format(b, c, d), + "99.0.0.1", + "wait", + "RXed -> 10.{}.{}.{} from CE1".format(b, c, d), + wait, + wait_time=10, + ) + luCommand( + "r3", + 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 10.{}.{}.{}"'.format(b, c, d), + "99.0.0.2", + "wait", + "RXed -> 10.{}.{}.{} from CE2".format(b, c, d), + wait, + wait_time=10, + ) + luCommand( + "r1", + 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d), + "99.0.0.1", + "wait", + "see VPN safi -> 10.{}.{}.{} from CE1".format(b, c, d), + ) + luCommand( + "r3", + 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d), + "99.0.0.2", + "wait", + "see VPN safi -> 10.{}.{}.{} from CE2".format(b, c, d), + ) + luCommand( + "r3", + 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d), + "1.1.1.1", + "wait", + "see VPN safi -> 10.{}.{}.{} from CE1".format(b, c, d), + ) + luCommand( + "r1", + 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d), + "3.3.3.3", + "wait", + "see VPN safi -> 10.{}.{}.{} from CE2".format(b, c, d), + ) + luCommand( + "r4", + 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d), + "1.1.1.1", + "wait", + "see VPN safi -> 10.{}.{}.{} from CE1".format(b, c, d), + ) + luCommand( + "r4", + 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d), + "3.3.3.3", + "wait", + "see VPN safi -> 10.{}.{}.{} from CE2".format(b, c, d), + ) + rtrs = ["ce1", "ce2", "ce3"] + for rtr in rtrs: + luCommand( + rtr, + "ip route get 10.{}.{}.{}".format(b, c, d), + "dev", + "wait", + "Route to 10.{}.{}.{} available".format(b, c, d), + wait, + wait_time=10, + ) + luCommand( + rtr, + "ip route show | grep -c \\^10\\.", + str(num), + "wait", + "See {} linux routes".format(num), + wait, + wait_time=10, + ) + + rtrs = ["r1", "r3", "r4"] + for rtr in rtrs: + luCommand( + rtr, + "ip route get vrf {}-cust1 10.{}.{}.{}".format(rtr, b, c, d), + "dev", + "wait", + "VRF route available", + wait, + wait_time=10, + ) + luCommand( + rtr, + "ip route show vrf {}-cust1 | grep -c \\^10\\.".format(rtr), + str(num), + "wait", + "See {} linux routes".format(num), + wait, + wait_time=10, + ) + rtrs = ["ce1", "ce2", "ce3", "r1", "r2", "r3", "r4"] + for rtr in rtrs: + ret = luCommand( + rtr, + 'vtysh -c "show memory"', + r"zebra: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*) .*bgpd: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*)", + "none", + "collect bgpd memory stats", + ) + found = luLast() + if ret != False and found != None: + val_z = int(found.group(1)) + if mem_z[rtr]["units"] != found.group(2): + val_z *= 1000 + delta_z = val_z - int(mem_z[rtr]["value"]) + ave_z = float(delta_z) / float(num) + + val_b = int(found.group(3)) + if mem_b[rtr]["units"] != found.group(4): + val_b *= 1000 + delta_b = val_b - int(mem_b[rtr]["value"]) + ave_b = float(delta_b) / float(num) + luCommand( + rtr, + 'vtysh -c "show thread cpu"', + ".", + "pass", + "BGPd heap: {0} {1} --> {2} {3} ({4} {1}/vpn route)".format( + mem_b[rtr]["value"], + mem_b[rtr]["units"], + found.group(3), + found.group(4), + round(ave_b, 4), + ), + ) + luCommand( + rtr, + 'vtysh -c "show thread cpu"', + ".", + "pass", + "Zebra heap: {0} {1} --> {2} {3} ({4} {1}/vpn route)".format( + mem_z[rtr]["value"], + mem_z[rtr]["units"], + found.group(1), + found.group(2), + round(ave_z, 4), + ), + ) +# done diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py new file mode 100755 index 0000000..60d959f --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright (c) 2018, LabN Consulting, L.L.C. +# Authored by Lou Berger +# + +import os +import sys +import pytest + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")) + +from lib.ltemplate import * + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd] + + +def test_check_linux_vrf(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/check_linux_vrf.py", False, CliOnFail, CheckFunc) + + +def test_adjacencies(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/adjacencies.py", False, CliOnFail, CheckFunc) + + +def test_notification_check(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/notification_check.py", False, CliOnFail, CheckFunc) + + +def SKIP_test_add_routes(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/add_routes.py", False, CliOnFail, CheckFunc) + + +def test_check_routes(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/check_routes.py", False, CliOnFail, CheckFunc) + + +# manual data path setup test - remove once have bgp/zebra vrf path working +def test_check_linux_mpls(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/check_linux_mpls.py", False, CliOnFail, CheckFunc) + + +def test_check_scale_up(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/scale_up.py", False, CliOnFail, CheckFunc) + + +def test_check_scale_down(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/scale_down.py", False, CliOnFail, CheckFunc) + + +def SKIP_test_cleanup_all(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/cleanup_all.py", False, CliOnFail, CheckFunc) + + +if __name__ == "__main__": + retval = pytest.main(["-s"]) + sys.exit(retval) diff --git a/tests/topotests/bgp_labeled_unicast_addpath/__init__.py b/tests/topotests/bgp_labeled_unicast_addpath/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r1/bgpd.conf b/tests/topotests/bgp_labeled_unicast_addpath/r1/bgpd.conf new file mode 100644 index 0000000..ae3812a --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_addpath/r1/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 65001 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 192.168.31.3 remote-as external + neighbor 192.168.31.3 timers 1 3 + neighbor 192.168.31.3 timers connect 1 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 192.168.31.3 activate + exit-address-family +! diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r1/zebra.conf b/tests/topotests/bgp_labeled_unicast_addpath/r1/zebra.conf new file mode 100644 index 0000000..fbaccda --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_addpath/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface lo + ip address 10.0.0.1/32 +! +interface r1-eth0 + ip address 192.168.31.1/24 +! diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r2/bgpd.conf b/tests/topotests/bgp_labeled_unicast_addpath/r2/bgpd.conf new file mode 100644 index 0000000..16dc1f3 --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_addpath/r2/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 65002 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 192.168.32.3 remote-as external + neighbor 192.168.32.3 timers 1 3 + neighbor 192.168.32.3 timers connect 1 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 192.168.32.3 activate + exit-address-family +! diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r2/zebra.conf b/tests/topotests/bgp_labeled_unicast_addpath/r2/zebra.conf new file mode 100644 index 0000000..8118d56 --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_addpath/r2/zebra.conf @@ -0,0 +1,7 @@ +! +interface lo + ip address 10.0.0.1/32 +! +interface r2-eth0 + ip address 192.168.32.2/24 +! diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r3/bgpd.conf b/tests/topotests/bgp_labeled_unicast_addpath/r3/bgpd.conf new file mode 100644 index 0000000..6dd8f7f --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_addpath/r3/bgpd.conf @@ -0,0 +1,35 @@ +! +router bgp 65003 + no bgp default ipv4-unicast + no bgp ebgp-requires-policy + no bgp suppress-duplicates + bgp bestpath as-path multipath-relax + neighbor 192.168.31.1 remote-as external + neighbor 192.168.31.1 timers 1 3 + neighbor 192.168.31.1 timers connect 1 + neighbor 192.168.32.2 remote-as external + neighbor 192.168.32.2 timers 1 3 + neighbor 192.168.32.2 timers connect 1 + neighbor 192.168.34.4 remote-as external + neighbor 192.168.34.4 timers 1 3 + neighbor 192.168.34.4 timers connect 1 + neighbor 192.168.35.5 remote-as external + neighbor 192.168.35.5 timers 1 3 + neighbor 192.168.35.5 timers connect 1 + neighbor 192.168.35.5 shutdown + address-family ipv4 labeled-unicast + neighbor 192.168.31.1 activate + neighbor 192.168.32.2 activate + neighbor 192.168.35.5 activate + neighbor 192.168.34.4 activate + neighbor 192.168.34.4 route-map r4 out + neighbor 192.168.34.4 addpath-tx-all-paths + exit-address-family + ! +! +ip prefix-list r4 seq 5 permit 10.0.0.1/32 +! +route-map r4 permit 10 + match ip address prefix-list r4 +exit +! diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r3/zebra.conf b/tests/topotests/bgp_labeled_unicast_addpath/r3/zebra.conf new file mode 100644 index 0000000..838413a --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_addpath/r3/zebra.conf @@ -0,0 +1,13 @@ +! +interface r3-eth0 + ip address 192.168.31.3/24 +! +interface r3-eth1 + ip address 192.168.32.3/24 +! +interface r3-eth2 + ip address 192.168.34.3/24 +! +interface r3-eth3 + ip address 192.168.35.3/24 +! diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r4/bgpd.conf b/tests/topotests/bgp_labeled_unicast_addpath/r4/bgpd.conf new file mode 100644 index 0000000..e137156 --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_addpath/r4/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65004 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 192.168.34.3 remote-as external + neighbor 192.168.34.3 timers 1 3 + neighbor 192.168.34.3 timers connect 1 + address-family ipv4 labeled-unicast + neighbor 192.168.34.3 activate + exit-address-family +! diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r4/zebra.conf b/tests/topotests/bgp_labeled_unicast_addpath/r4/zebra.conf new file mode 100644 index 0000000..2ec426a --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_addpath/r4/zebra.conf @@ -0,0 +1,4 @@ +! +interface r4-eth0 + ip address 192.168.34.4/24 +! diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r5/bgpd.conf b/tests/topotests/bgp_labeled_unicast_addpath/r5/bgpd.conf new file mode 100644 index 0000000..5b38b5a --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_addpath/r5/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 65005 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 192.168.35.3 remote-as external + neighbor 192.168.35.3 timers 1 3 + neighbor 192.168.35.3 timers connect 1 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 192.168.35.3 activate + exit-address-family +! diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r5/zebra.conf b/tests/topotests/bgp_labeled_unicast_addpath/r5/zebra.conf new file mode 100644 index 0000000..6289e4f --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_addpath/r5/zebra.conf @@ -0,0 +1,7 @@ +! +interface lo + ip address 10.0.0.1/32 +! +interface r5-eth0 + ip address 192.168.35.5/24 +! diff --git a/tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py b/tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py new file mode 100644 index 0000000..f4bb487 --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Check if labeled-unicast works correctly with addpath capability. +Initially R3 MUST announce 10.0.0.1/32 multipath(2) from R1 + R2. +Later, we enable R5 and 10.0.0.1/32 multipath(3) MUST be announced, +R1 + R2 + R5. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 6): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r5"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_addpath_labeled_unicast(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r3 = tgen.gears["r3"] + r4 = tgen.gears["r4"] + + def _bgp_check_received_routes(pfxcount): + output = json.loads(r4.vtysh_cmd("show bgp ipv4 labeled-unicast summary json")) + expected = { + "peers": { + "192.168.34.3": { + "pfxRcd": pfxcount, + "state": "Established", + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_received_routes, 2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Failed to receive labeled-unicast with addpath (multipath=2)" + + step("Enable BGP session for R5") + r3.vtysh_cmd( + """ + configure terminal + router bgp 65003 + no neighbor 192.168.35.5 shutdown + """ + ) + + test_func = functools.partial(_bgp_check_received_routes, 3) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Failed to receive labeled-unicast with addpath (multipath=3)" + + step("Disable BGP session for R5") + r3.vtysh_cmd( + """ + configure terminal + router bgp 65003 + neighbor 192.168.35.5 shutdown + """ + ) + + test_func = functools.partial(_bgp_check_received_routes, 2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Failed to receive labeled-unicast with addpath (multipath=2)" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_labeled_unicast_default_originate/__init__.py b/tests/topotests/bgp_labeled_unicast_default_originate/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_labeled_unicast_default_originate/r1/bgpd.conf b/tests/topotests/bgp_labeled_unicast_default_originate/r1/bgpd.conf new file mode 100644 index 0000000..d0d1390 --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_default_originate/r1/bgpd.conf @@ -0,0 +1,35 @@ +! +router bgp 65001 + no bgp default ipv4-unicast + no bgp default ipv6-unicast + no bgp ebgp-requires-policy + neighbor 192.168.12.2 remote-as external + neighbor 192.168.12.2 timers 1 3 + neighbor 192.168.12.2 timers connect 1 + neighbor 2001:db8:12::2 remote-as external + neighbor 2001:db8:12::2 timers 1 3 + neighbor 2001:db8:12::2 timers connect 1 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 192.168.12.2 activate + neighbor 192.168.12.2 default-originate route-map r2 + exit-address-family + ! + address-family ipv6 labeled-unicast + neighbor 2001:db8:12::2 activate + neighbor 2001:db8:12::2 default-originate route-map r2 + exit-address-family + ! +! +route-map r2 permit 10 + set community 65001:65001 + set metric 666 +exit +! diff --git a/tests/topotests/bgp_labeled_unicast_default_originate/r1/zebra.conf b/tests/topotests/bgp_labeled_unicast_default_originate/r1/zebra.conf new file mode 100644 index 0000000..686b075 --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_default_originate/r1/zebra.conf @@ -0,0 +1,5 @@ +! +interface r1-eth0 + ip address 192.168.12.1/24 + ipv6 address 2001:db8:12::1/64 +! diff --git a/tests/topotests/bgp_labeled_unicast_default_originate/r2/bgpd.conf b/tests/topotests/bgp_labeled_unicast_default_originate/r2/bgpd.conf new file mode 100644 index 0000000..1498dff --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_default_originate/r2/bgpd.conf @@ -0,0 +1,19 @@ +! +router bgp 65002 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + no bgp default ipv6-unicast + neighbor 192.168.12.1 remote-as external + neighbor 192.168.12.1 timers 1 3 + neighbor 192.168.12.1 timers connect 1 + neighbor 2001:db8:12::1 remote-as external + neighbor 2001:db8:12::1 timers 1 3 + neighbor 2001:db8:12::1 timers connect 1 + address-family ipv4 labeled-unicast + neighbor 192.168.12.1 activate + exit-address-family + ! + address-family ipv6 labeled-unicast + neighbor 2001:db8:12::1 activate + exit-address-family +! diff --git a/tests/topotests/bgp_labeled_unicast_default_originate/r2/zebra.conf b/tests/topotests/bgp_labeled_unicast_default_originate/r2/zebra.conf new file mode 100644 index 0000000..cb5c55e --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_default_originate/r2/zebra.conf @@ -0,0 +1,5 @@ +! +interface r2-eth0 + ip address 192.168.12.2/24 + ipv6 address 2001:db8:12::2/64 +! diff --git a/tests/topotests/bgp_labeled_unicast_default_originate/test_bgp_labeled_unicast_default_originate.py b/tests/topotests/bgp_labeled_unicast_default_originate/test_bgp_labeled_unicast_default_originate.py new file mode 100644 index 0000000..34c23d9 --- /dev/null +++ b/tests/topotests/bgp_labeled_unicast_default_originate/test_bgp_labeled_unicast_default_originate.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Check if labeled-unicast works correctly with default-originate. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_labeled_unicast_default_originate(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_check_advertised_routes(): + output = json.loads( + r1.vtysh_cmd( + "show bgp ipv4 labeled-unicast neighbors 192.168.12.2 advertised-routes json" + ) + ) + expected = { + "bgpOriginatingDefaultNetwork": "0.0.0.0/0", + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_advertised_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to advertise default route for labeled-unicast" + + def _bgp_check_received_ipv4_routes(): + output = json.loads( + r2.vtysh_cmd("show bgp ipv4 labeled-unicast 0.0.0.0/0 json") + ) + expected = { + "paths": [ + { + "valid": True, + "metric": 666, + "community": { + "string": "65001:65001", + }, + "remoteLabel": 0, + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_received_ipv4_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to receive IPv4 default route for labeled-unicast" + + def _bgp_check_received_ipv6_routes(): + output = json.loads(r2.vtysh_cmd("show bgp ipv6 labeled-unicast ::/0 json")) + expected = { + "paths": [ + { + "valid": True, + "metric": 666, + "community": { + "string": "65001:65001", + }, + "remoteLabel": 2, + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_received_ipv6_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to receive IPv6 default route for labeled-unicast" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_large_comm_list_match/__init__.py b/tests/topotests/bgp_large_comm_list_match/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_large_comm_list_match/r1/bgpd.conf b/tests/topotests/bgp_large_comm_list_match/r1/bgpd.conf new file mode 100644 index 0000000..1a91f0f --- /dev/null +++ b/tests/topotests/bgp_large_comm_list_match/r1/bgpd.conf @@ -0,0 +1,28 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.0.2 remote-as external + neighbor 192.168.0.2 timers 1 3 + neighbor 192.168.0.2 timers connect 1 + address-family ipv4 + redistribute connected + neighbor 192.168.0.2 route-map r2 out + exit-address-family +! +ip prefix-list p1 seq 5 permit 172.16.255.1/32 +ip prefix-list p3 seq 5 permit 172.16.255.3/32 +ip prefix-list p4 seq 5 permit 172.16.255.4/32 +! +route-map r2 permit 10 + match ip address prefix-list p1 + set large-community 65001:1:1 65001:2:1 +route-map r2 permit 20 + match ip address prefix-list p3 + set large-community 65001:3:1 +route-map r2 permit 30 + match ip address prefix-list p4 + set large-community 65001:10:1 65001:12:1 65001:13:1 +exit +route-map r2 permit 40 +exit +! diff --git a/tests/topotests/bgp_large_comm_list_match/r1/zebra.conf b/tests/topotests/bgp_large_comm_list_match/r1/zebra.conf new file mode 100644 index 0000000..4219a7c --- /dev/null +++ b/tests/topotests/bgp_large_comm_list_match/r1/zebra.conf @@ -0,0 +1,12 @@ +! +interface lo + ip address 172.16.255.1/32 + ip address 172.16.255.2/32 + ip address 172.16.255.3/32 + ip address 172.16.255.4/32 +! +interface r1-eth0 + ip address 192.168.0.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_large_comm_list_match/r2/bgpd.conf b/tests/topotests/bgp_large_comm_list_match/r2/bgpd.conf new file mode 100644 index 0000000..779b705 --- /dev/null +++ b/tests/topotests/bgp_large_comm_list_match/r2/bgpd.conf @@ -0,0 +1,24 @@ +! +!debug bgp updates +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.0.1 remote-as external + neighbor 192.168.0.1 timers 1 3 + neighbor 192.168.0.1 timers connect 1 + neighbor 192.168.1.3 remote-as external + neighbor 192.168.1.3 timers 1 3 + neighbor 192.168.1.3 timers connect 1 + address-family ipv4 + neighbor 192.168.0.1 route-map r1 in + neighbor 192.168.0.1 soft-reconfiguration inbound + exit-address-family +! +bgp large-community-list 1 seq 5 permit 65001:1:1 65001:2:1 +bgp large-community-list 1 seq 10 permit 65001:3:1 +! +route-map r1 deny 10 + match large-community 1 +route-map r1 permit 20 +exit +! diff --git a/tests/topotests/bgp_large_comm_list_match/r2/zebra.conf b/tests/topotests/bgp_large_comm_list_match/r2/zebra.conf new file mode 100644 index 0000000..7fe82ba --- /dev/null +++ b/tests/topotests/bgp_large_comm_list_match/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface r2-eth0 + ip address 192.168.0.2/24 +! +interface r2-eth1 + ip address 192.168.1.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_large_comm_list_match/r3/bgpd.conf b/tests/topotests/bgp_large_comm_list_match/r3/bgpd.conf new file mode 100644 index 0000000..e7cb76a --- /dev/null +++ b/tests/topotests/bgp_large_comm_list_match/r3/bgpd.conf @@ -0,0 +1,21 @@ +! +!debug bgp updates +! +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + address-family ipv4 + neighbor 192.168.1.2 route-map r1 in + neighbor 192.168.1.2 soft-reconfiguration inbound + exit-address-family +! +bgp large-community-list 2 seq 10 permit 65001:12:1 +! +route-map r1 deny 10 + match large-community 2 any +exit +route-map r1 permit 20 +exit +! diff --git a/tests/topotests/bgp_large_comm_list_match/r3/zebra.conf b/tests/topotests/bgp_large_comm_list_match/r3/zebra.conf new file mode 100644 index 0000000..755dd18 --- /dev/null +++ b/tests/topotests/bgp_large_comm_list_match/r3/zebra.conf @@ -0,0 +1,6 @@ +! +interface r3-eth0 + ip address 192.168.1.3/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_large_comm_list_match/test_bgp_large_comm_list_match.py b/tests/topotests/bgp_large_comm_list_match/test_bgp_large_comm_list_match.py new file mode 100644 index 0000000..483c048 --- /dev/null +++ b/tests/topotests/bgp_large_comm_list_match/test_bgp_large_comm_list_match.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright 2023 by 6WIND S.A. +# + +""" +Check if BGP large-community-list works +when used as match rule in incoming route-maps. + +- case 1 should deny incoming updates with large-community-list 1 +bgp large-community-list 1 seq 5 permit 65001:1:1 65001:2:1 +bgp large-community-list 1 seq 10 permit 65001:3:1 +! +route-map r1 deny 10 + match large-community 1 + +route-map test deny 10 + match community 1 + +- case 2 should deny incoming updates with any large-community-list 1 +bgp large-community-list 2 seq 10 permit 65001:12:1 +! +route-map r1 deny 10 + match large-community 2 any +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_large_comm_list_match(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads( + router.vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.0.1 filtered-routes json" + ) + ) + expected = { + "receivedRoutes": { + "172.16.255.1/32": { + "path": "65001", + }, + "172.16.255.3/32": { + "path": "65001", + }, + } + } + return topotest.json_cmp(output, expected) + + step("BGP filtering check with large-community-list on R2") + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Failed to filter BGP UPDATES with large-community-list on R2" + + +def test_bgp_large_comm_list_match_any(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r3"] + + def _bgp_converge(): + output = json.loads( + router.vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.1.2 filtered-routes json" + ) + ) + expected = { + "receivedRoutes": { + "172.16.255.4/32": { + "path": "65002 65001", + }, + } + } + return topotest.json_cmp(output, expected) + + step("BGP filtering check with large-community-list on R3") + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to filter BGP UPDATES with large-community-list on R3" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_large_community/__init__.py b/tests/topotests/bgp_large_community/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_large_community/bgp_large_community_topo_1.json b/tests/topotests/bgp_large_community/bgp_large_community_topo_1.json new file mode 100644 index 0000000..902c01b --- /dev/null +++ b/tests/topotests/bgp_large_community/bgp_large_community_topo_1.json @@ -0,0 +1,262 @@ +{ + "ipv4base": "192.168.1.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "192.168.1.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + }, + "r3": { + "dest_link": { + "r1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + }, + "r3": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "1000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r4": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r4": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r5-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "4000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": {} + } + }, + "r5": { + "dest_link": { + "r4-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": {} + } + }, + "r5": { + "dest_link": { + "r4-link1": {} + } + } + } + } + } + } + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r4-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "6000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r5-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r5-link1": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_large_community/bgp_large_community_topo_2.json b/tests/topotests/bgp_large_community/bgp_large_community_topo_2.json new file mode 100644 index 0000000..36dee39 --- /dev/null +++ b/tests/topotests/bgp_large_community/bgp_large_community_topo_2.json @@ -0,0 +1,344 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "1000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "1000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r5": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r6": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "4000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": {} + } + }, + "r6": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": {} + } + }, + "r6": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r6": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "5000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + }, + "r6": { + "dest_link": { + "r5": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + }, + "r6": { + "dest_link": { + "r5": {} + } + } + } + } + } + } + } + }, + "r6": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r5": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "6000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": {} + } + }, + "r5": { + "dest_link": { + "r6": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": {} + } + }, + "r5": { + "dest_link": { + "r6": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py new file mode 100644 index 0000000..892b15a --- /dev/null +++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py @@ -0,0 +1,1216 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + + +""" +Following tests are covered to test large-community/community functionality: +1. Verify if large community attribute can be configured only in correct + canonical format. +2. Verify that the community attribute value, which we have advertised are + received in correct format and values, at the receiving end. +3. Verify BGP Large Community attribute"s transitive property attribute. +4. Verify that BGP Large Communities attribute are malformed, if the length of + the BGP Large Communities Attribute value, expressed in octets, + is not a non-zero multiple of 12. +5. Verify if overriding large community values works fine. +6. Verify that large community values" aggregation works fine. +7. Standard community also work fine in conjunction with large-community. +8. Matching prefixes based on attributes other than prefix list and make use + of set clause (IPV6). +9. Matching prefixes based on attributes other than prefix list and make use + of set clause (IPV4). +10. Verify community and large-community list operations in route-map with all + clause (exact, all, any, regex) works. +11. Verify that any value in BGP Large communities for boundary values. +12. Clear BGP neighbor-ship and check if large community and community + attributes are getting re-populated. + +""" + +import pytest +import time +from os import path as os_path +import sys + +# Required to instantiate the topology builder class. +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + create_route_maps, + create_bgp_community_lists, + create_prefix_lists, + verify_bgp_community, + step, + check_address_types, + required_linux_kernel_version, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp_and_verify +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd] + + +# Save the Current Working Directory to find configuration files. +CWD = os_path.dirname(os_path.realpath(__file__)) +sys.path.append(os_path.join(CWD, "../")) +sys.path.append(os_path.join(CWD, "../lib/")) + + +# Global variables +bgp_convergence = False +NETWORK = { + "ipv4": ["200.50.2.0", "200.50.2.1", "200.50.2.0"], + "ipv6": ["1::1", "1::2", "1::0"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NET_MASK = {"ipv4": "24", "ipv6": "120"} +IPV4_NET = ["200.50.2.0"] +IPV6_NET = ["1::0"] +CONFIG_ROUTER_R1 = False +CONFIG_ROUTER_R2 = False +CONFIG_ROUTER_ADDITIVE = False +ADDR_TYPES = [] +LARGE_COMM = { + "r1": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1", + "r2": "2:1:1 2:2:1 2:3:1 2:4:1 2:5:1", + "mal_1": "1:1 1:2 1:3 1:4 1:5", + "pf_list_1": "0:0:1 0:0:10 0:0:100", + "pf_list_2": "0:0:2 0:0:20 0:0:200", + "agg_1": "0:0:1 0:0:2 0:0:10 0:0:20 0:0:100 0:0:200 2:1:1 " + "2:2:1 2:3:1 2:4:1 2:5:1", + "agg_2": "0:0:2 0:0:20 0:0:200 2:1:1 " "2:2:1 2:3:1 2:4:1 2:5:1", +} +STANDARD_COMM = { + "r1": "1:1 1:2 1:3 1:4 1:5", + "r2": "2:1 2:2 2:3 2:4 2:5", + "mal_1": "1 2 3 4 5", + "pf_list_1": "0:1 0:10 0:100", + "pf_list_2": "0:2 0:20 0:200", + "agg_1": "0:1 0:2 0:10 0:20 0:100 0:200 2:1 2:2 2:3 2:4 2:5", + "agg_2": "0:2 0:20 0:200 2:1 2:2 2:3 2:4 2:5", +} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + global ADDR_TYPES + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_large_community_topo_1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global bgp_convergence + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + ##tgen.mininet_cli() + # Api call verify whether BGP is converged + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format( + bgp_convergence + ) + + ADDR_TYPES = check_address_types() + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def config_router_r1(tgen, topo, tc_name): + global CONFIG_ROUTER_R1 + + input_dict_1 = { + "r1": { + "route_maps": { + "LC1": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": {"num": LARGE_COMM["r1"]}, + "community": {"num": STANDARD_COMM["r1"]}, + }, + } + ] + } + } + } + + step("Configuring LC1 on r1") + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "%s/%s" + % (NETWORK["ipv4"][0], MASK["ipv4"]), + "no_of_network": 4, + } + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "route_maps": [ + {"name": "LC1", "direction": "out"} + ] + } + } + }, + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [ + {"name": "LC1", "direction": "out"} + ] + } + } + }, + }, + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "%s/%s" + % (NETWORK["ipv6"][0], MASK["ipv6"]), + "no_of_network": 4, + } + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "route_maps": [ + {"name": "LC1", "direction": "out"} + ] + } + } + }, + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [ + {"name": "LC1", "direction": "out"} + ] + } + } + }, + }, + } + }, + } + } + } + } + + step("Applying LC1 on r1 neighbors and advertising networks") + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + CONFIG_ROUTER_R1 = True + + +def config_router_r2(tgen, topo, tc_name): + global CONFIG_ROUTER_R2 + + input_dict = { + "r2": { + "route_maps": { + "LC2": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": {"num": LARGE_COMM["r2"]}, + "community": {"num": STANDARD_COMM["r2"]}, + }, + } + ] + } + } + } + + step("Configuring route-maps LC2 on r2") + result = create_route_maps(tgen, input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_1 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [ + {"name": "LC2", "direction": "out"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [ + {"name": "LC2", "direction": "out"} + ] + } + } + } + } + } + }, + } + } + } + } + + step("Applying LC2 on r2 neighbors in out direction") + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + CONFIG_ROUTER_R2 = True + + +def config_router_additive(tgen, topo, tc_name): + global CONFIG_ROUTER_ADDITIVE + + input_dict = { + "r2": { + "route_maps": { + "LC2": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": LARGE_COMM["r2"], + "action": "additive", + }, + "community": { + "num": STANDARD_COMM["r2"], + "action": "additive", + }, + }, + } + ] + } + } + } + + step("Configuring LC2 with community attributes as additive") + result = create_route_maps(tgen, input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + # tgen.mininet_cli() + CONFIG_ROUTER_ADDITIVE = True + + +def config_for_as_path(tgen, topo, tc_name): + config_router_r1(tgen, topo, tc_name) + + config_router_r2(tgen, topo, tc_name) + + # Create ipv6 prefix list + input_dict_1 = { + "r1": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "%s/%s" % (NETWORK["ipv4"][0], MASK["ipv4"]), + "action": "permit", + } + ], + "pf_list_2": [ + { + "seqid": "10", + "network": "%s/%s" % (NETWORK["ipv4"][1], MASK["ipv4"]), + "action": "permit", + } + ], + }, + "ipv6": { + "pf_list_3": [ + { + "seqid": "10", + "network": "%s/%s" % (NETWORK["ipv6"][0], MASK["ipv6"]), + "action": "permit", + } + ], + "pf_list_4": [ + { + "seqid": "10", + "network": "%s/%s" % (NETWORK["ipv6"][1], MASK["ipv6"]), + "action": "permit", + } + ], + }, + } + } + } + + step("Configuring prefix-lists on r1 to filter networks") + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_2 = { + "r1": { + "route_maps": { + "LC1": [ + { + "action": "permit", + "seq_id": 10, + "match": {"ipv4": {"prefix_lists": "pf_list_1"}}, + "set": { + "large_community": {"num": LARGE_COMM["pf_list_1"]}, + "community": {"num": STANDARD_COMM["pf_list_1"]}, + }, + }, + { + "action": "permit", + "seq_id": 20, + "match": {"ipv6": {"prefix_lists": "pf_list_3"}}, + "set": { + "large_community": {"num": LARGE_COMM["pf_list_1"]}, + "community": {"num": STANDARD_COMM["pf_list_1"]}, + }, + }, + { + "action": "permit", + "seq_id": 30, + "match": {"ipv4": {"prefix_lists": "pf_list_2"}}, + "set": { + "large_community": {"num": LARGE_COMM["pf_list_2"]}, + "community": {"num": STANDARD_COMM["pf_list_2"]}, + }, + }, + { + "action": "permit", + "seq_id": 40, + "match": {"ipv6": {"prefix_lists": "pf_list_4"}}, + "set": { + "large_community": {"num": LARGE_COMM["pf_list_2"]}, + "community": {"num": STANDARD_COMM["pf_list_2"]}, + }, + }, + ] + } + } + } + + step( + "Applying prefix-lists match in route-map LC1 on r1. Setting" + " community attritbute for filtered networks" + ) + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + config_router_additive(tgen, topo, tc_name) + + input_dict_3 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": LARGE_COMM["pf_list_1"], + "large": True, + }, + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": STANDARD_COMM["pf_list_1"], + }, + ] + } + } + + step("Configuring bgp community lists on r4") + result = create_bgp_community_lists(tgen, input_dict_3) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_4 = { + "r4": { + "route_maps": { + "LC4": [ + { + "action": "permit", + "seq_id": "10", + "match": { + "large_community_list": {"id": "ANY"}, + "community_list": {"id": "ANY"}, + }, + "set": {"path": {"as_num": "4000000", "as_action": "prepend"}}, + } + ] + } + } + } + + step("Applying community list on route-map on r4") + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_5 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r4-link1": { + "route_maps": [ + {"name": "LC4", "direction": "out"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r4-link1": { + "route_maps": [ + {"name": "LC4", "direction": "out"} + ] + } + } + } + } + } + }, + } + } + } + } + + step("Applying route-map LC4 out from r4 to r5 ") + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + +##################################################### +# +# Test cases +# +##################################################### +def test_large_community_set(request): + """ + Verify if large community attribute can be configured only in correct + canonical format. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # API call to modify router id + # input_dict dictionary to be provided to configure route_map + input_dict = { + "r1": { + "route_maps": { + "LC1": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": {"num": LARGE_COMM["r1"]}, + "community": {"num": STANDARD_COMM["r1"]}, + }, + } + ] + } + } + } + + step("Trying to set bgp communities") + result = create_route_maps(tgen, input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_advertise(request): + """ + Verify that the community attribute value, which we have advertised are + received in correct format and values, at the receiving end. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_router_r1(tgen, topo, tc_name) + + input_dict = { + "largeCommunity": LARGE_COMM["r1"], + "community": STANDARD_COMM["r1"], + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r2", [NETWORK[adt][0]], input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_community(tgen, adt, "r3", [NETWORK[adt][0]], input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_transitive(request): + """ + Verify BGP Large Community attribute"s transitive property attribute. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + config_router_r1(tgen, topo, tc_name) + + input_dict_1 = { + "largeCommunity": LARGE_COMM["r1"], + "community": STANDARD_COMM["r1"], + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][0]], input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_override(request): + """ + Verify if overriding large community values works fine. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_router_r1(tgen, topo, tc_name) + + config_router_r2(tgen, topo, tc_name) + + input_dict_3 = { + "largeCommunity": LARGE_COMM["r2"], + "community": STANDARD_COMM["r2"], + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][1]], input_dict_3) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_additive(request): + """ + Verify that large community values" aggregation works fine. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_router_r1(tgen, topo, tc_name) + + config_router_r2(tgen, topo, tc_name) + + config_router_additive(tgen, topo, tc_name) + + input_dict_1 = { + "largeCommunity": "%s %s" % (LARGE_COMM["r1"], LARGE_COMM["r2"]), + "community": "%s %s" % (STANDARD_COMM["r1"], STANDARD_COMM["r2"]), + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][0]], input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_match_as_path(request): + """ + Matching prefixes based on attributes other than prefix list and make use + of set clause. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_for_as_path(tgen, topo, tc_name) + + input_dict = { + "largeCommunity": "%s %s" % (LARGE_COMM["pf_list_1"], LARGE_COMM["r2"]), + "community": "%s %s" % (STANDARD_COMM["pf_list_1"], STANDARD_COMM["r2"]), + } + + input_dict_1 = { + "largeCommunity": "%s %s" % (LARGE_COMM["pf_list_2"], LARGE_COMM["r2"]), + "community": "%s %s" % (STANDARD_COMM["pf_list_2"], STANDARD_COMM["r2"]), + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r5", [NETWORK[adt][0]], input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_community( + tgen, adt, "r5", [NETWORK[adt][1]], input_dict_1, expected=False + ) + + assert result is not True, "Test case {} : Should fail \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_match_all(request): + """ + Verify community and large-community list operations in route-map with all + clause (exact, all, any, regex) works. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_router_r1(tgen, topo, tc_name) + + config_router_r2(tgen, topo, tc_name) + + config_router_additive(tgen, topo, tc_name) + + input_dict_1 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "1:1:1", + "large": True, + }, + { + "community_type": "standard", + "action": "permit", + "name": "ALL", + "value": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1 2:1:1 2:2:1", + "large": True, + }, + { + "community_type": "expanded", + "action": "permit", + "name": "EXP_ALL", + "value": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1 2:[1-5]:1", + "large": True, + }, + ] + } + } + + step("Create bgp community lists for ANY, EXACT and EXP_ALL match") + + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_2 = { + "r4": { + "route_maps": { + "LC4": [ + { + "action": "permit", + "seq_id": "10", + "match": {"large-community-list": {"id": "ANY"}}, + }, + { + "action": "permit", + "seq_id": "20", + "match": {"large-community-list": {"id": "EXACT"}}, + }, + { + "action": "permit", + "seq_id": "30", + "match": {"large-community-list": {"id": "EXP_ALL"}}, + }, + ] + } + } + } + + step("Applying bgp community lits on LC4 route-map") + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_3 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r4-link1": { + "route_maps": [ + {"name": "LC4", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r4-link1": { + "route_maps": [ + {"name": "LC4", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + } + } + + step("Apply route-mpa LC4 on r4 for r2 neighbor, direction 'in'") + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_4 = { + "largeCommunity": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1 2:1:1 2:2:1 2:3:1 " + "2:4:1 2:5:1" + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][0]], input_dict_4) + assert result is True, "Test case {} : Should fail \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +# @pytest.mark.skip(reason="as-set not working for ipv6") +def test_large_community_aggregate_network(request): + """ + Restart router and check if large community and community + attributes are getting re-populated. + """ + + tc_name = request.node.name + write_test_header(tc_name) + + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + config_for_as_path(tgen, topo, tc_name) + + input_dict = { + "community": STANDARD_COMM["agg_1"], + "largeCommunity": LARGE_COMM["agg_1"], + } + + input_dict_1 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "aggregate_address": [ + { + "network": "%s/%s" + % (NETWORK["ipv4"][2], NET_MASK["ipv4"]), + "as_set": True, + } + ] + } + }, + "ipv6": { + "unicast": { + "aggregate_address": [ + { + "network": "%s/%s" + % (NETWORK["ipv6"][2], NET_MASK["ipv6"]), + "as_set": True, + } + ] + } + }, + } + } + } + } + + step("Configuring aggregate address as-set on r2") + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + for adt in ADDR_TYPES: + result = verify_bgp_community( + tgen, adt, "r4", ["%s/%s" % (NETWORK[adt][2], NET_MASK[adt])], input_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "%s/%s" + % (NETWORK["ipv4"][0], MASK["ipv4"]), + "no_of_network": 1, + "delete": True, + } + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "%s/%s" + % (NETWORK["ipv6"][0], MASK["ipv6"]), + "no_of_network": 1, + "delete": True, + } + ] + } + }, + } + } + } + } + + step("Stop advertising one of the networks") + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_3 = { + "community": STANDARD_COMM["agg_2"], + "largeCommunity": LARGE_COMM["agg_2"], + } + + for adt in ADDR_TYPES: + step("Verifying bgp community values on r5 is also modified") + result = verify_bgp_community( + tgen, adt, "r4", ["%s/%s" % (NETWORK[adt][2], NET_MASK[adt])], input_dict_3 + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_boundary_values(request): + """ + Verify that any value in BGP Large communities for boundary values. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + input_dict = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "0:-1", + } + ] + } + } + + step("Checking boundary value for community 0:-1") + result = create_bgp_community_lists(tgen, input_dict) + assert result is not True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Checking community attribute 0:65536") + input_dict_2 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "0:65536", + } + ] + } + } + + step("Checking boundary value for community 0:65536") + result = create_bgp_community_lists(tgen, input_dict_2) + assert result is not True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Checking boundary value for community 0:4294967296") + input_dict_3 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "0:4294967296", + "large": True, + } + ] + } + } + + result = create_bgp_community_lists(tgen, input_dict_3) + assert result is not True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + step("Checking boundary value for community 0:-1:1") + + input_dict_4 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "0:-1:1", + "large": True, + } + ] + } + } + + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is not True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + +def test_large_community_invalid_chars(request): + """ + BGP canonical lcommunities must only be digits + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + input_dict = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "1:a:2", + "large": True, + } + ] + } + } + + step("Checking boundary value for community 1:a:2") + result = create_bgp_community_lists(tgen, input_dict) + assert result is not True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + +def test_large_community_after_clear_bgp(request): + """ + Clear BGP neighbor-ship and check if large community and community + attributes are getting re-populated. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_router_r1(tgen, topo, tc_name) + + input_dict = {"largeCommunity": LARGE_COMM["r1"], "community": STANDARD_COMM["r1"]} + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r2", [NETWORK[adt][0]], input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Clearing BGP on r1") + clear_bgp_and_verify(tgen, topo, "r1") + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r2", [NETWORK[adt][0]], input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py new file mode 100644 index 0000000..0044dbb --- /dev/null +++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py @@ -0,0 +1,2220 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +test_bgp_large_community_topo_1.py: Test BGP large community. + +Following tests are covered: +1. Verify the standard large-community-lists can permit or deny + large community attribute only in the correct canonical format. +2. Verify the expanded large-community-lists can permit or deny + large community attribute both in the correct canonical format + as well as REG_EX. +3. Verify that we can modify a large-community-list is in use, + to add/remove attribute value and it takes immediate effect. +4. Verify that large community attribute gets advertised when + route-map is applied to a neighbor and cleared when route-map + is removed. +5. Verify that duplicate BGP Large Community values are NOT be transmitted. +6. Verify if we want to remove all the large-community attributes from a + set of prefix we can set the value as NONE. +7. Redistribute connected and static routes in BGP process with a route-map + appending/removing L-comm attributes. +8. Verify if we want to remove specific large-community values from + a set of prefix we can make use of DELETE operation based on L-comm list. +9. Verify that if community values are NOT be advertised to a specific + neighbour, we negate send-community command. + (Send-community all is enabled by default for all neighbors) +10. Verify that large-community lists can not be configured without providing + specific L-community values(for match/delete operation in a route-map). +11. Verify that Match_EXACT clause should pass only if all of the L-comm + values configured (horizontally) in the community list is present in + the prefix. There must be no additional L-communities in the prefix. +12. Verify that Match_ALL clause should pass only if ALL of the L-comm values + configured (horizontally) in the community list is present in the prefix. + There could be additional L-communities in the prefix that are not present + in the L-comm list. +13. Verify that Match_ANY clause should pass only if at-least any one L-comm + value configured(vertically) in large-community list, is present in prefixes. +14. Verify large-community lists operation in a route-map with match RegEx + statements. +""" + +import os +import sys +import pytest +import time + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +# Import topoJson from lib, to create topology and initial configuration +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + create_route_maps, + create_bgp_community_lists, + verify_bgp_community, + step, + verify_create_community_list, + delete_route_maps, + verify_route_maps, + create_static_routes, + check_address_types, + required_linux_kernel_version, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp_and_verify +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd] + + +# Global variables +bgp_convergence = False + +NETWORKS = {"ipv4": ["200.50.2.0/32"], "ipv6": ["1::1/128"]} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_large_community_topo_2.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global bgp_convergence, ADDR_TYPES + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + # Ipv4 + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format( + bgp_convergence + ) + ADDR_TYPES = check_address_types() + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_create_bgp_standard_large_community_list(request): + """ + Create standard large-community-list and verify it can permit + or deny large community attribute only in the correct canonical + format. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + step("Create srtandard large community list") + input_dict = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "LC_1_STD", + "value": "2:1:1 2:1:2 1:2:3", + "large": True, + }, + { + "community_type": "standard", + "action": "permit", + "name": "LC_2_STD", + "value": "3:1:1 3:1:2", + "large": True, + }, + ] + } + } + result = create_bgp_community_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create srtandard large community list with in-correct values") + input_dict = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "LC_1_STD_ERR", + "value": "0:0:0", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ## TODO should fail + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_create_bgp_expanded_large_community_list(request): + """ + Create expanded large-community-list and verify it can permit + or deny large community attribute both in the correct canonical + format as well as REG_EX + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create expanded large community list") + input_dict = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "LC_1_EXP", + "value": "1:1:200 1:2:* 3:2:1", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_modify_large_community_lists_referenced_by_rmap(request): + """ + This test is to verify that we can modify a large-community-list + is in use, add/remove attribute value and it takes immediate effect. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create standard large community list") + input_dict_1 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "LC_DEL", + "value": "1:2:1 1:3:1 2:1:1 2:2:2 3:3:3", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_2 = { + "r1": { + "route_maps": { + "RM_R2_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "1:2:1 1:3:1 2:10:1 3:3:3 4:4:4 5:5:5", + "action": "additive", + } + }, + } + ] + } + }, + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": {"large_comm_list": {"id": "LC_DEL", "delete": True}}, + } + ] + } + }, + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}], + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "RM_R2_OUT", + "direction": "out", + } + ] + } + } + } + }, + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [{"network": "1::1/128"}], + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "RM_R2_OUT", + "direction": "out", + } + ] + } + } + } + }, + } + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify Community-list") + dut = "r4" + input_dict_4 = {"largeCommunity": "2:10:1 4:4:4 5:5:5"} + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_apply_and_remove(request): + """ + This test is to verify that large community attribute gets advertised when + route-map is applied to a neighbor and cleared when route-map is removed + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_1 = { + "r4": { + "route_maps": { + "RM_LC1": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "200:200:1 200:200:10 200:200:20000", + "action": "additive", + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_LC1", "direction": "out"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_LC1", "direction": "out"} + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r6" + input_dict_4 = {"largeCommunity": "200:200:1 200:200:10 200:200:20000"} + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Delete route map reference by community-list") + input_dict_3 = {"r4": {"route_maps": ["RM_LC1"]}} + result = delete_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify route map is deleted") + result = verify_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + for adt in ADDR_TYPES: + result = verify_bgp_community( + tgen, adt, dut, NETWORKS[adt], input_dict_4, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "largeCommunity is still present after deleting route-map \n Error: {}".format( + tc_name, result + ) + ) + + write_test_footer(tc_name) + + +def test_duplicate_large_community_list_attributes_not_transitive(request): + """ + This test is to verify that duplicate BGP Large Community values + are NOT be transmitted. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_1 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "0:0:1 0:0:10 0:0:100 2:0:1 2:0:2 2:0:3" + " 2:0:4 2:0:5", + "action": "additive", + } + }, + } + ], + "RM_R4_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "0:0:1 0:0:10 0:0:10000 2:0:1 2:0:2", + "action": "additive", + } + }, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + }, + "r6": { + "dest_link": { + "r4": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + }, + "r6": { + "dest_link": { + "r4": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r6" + input_dict_4 = { + "largeCommunity": "0:0:1 0:0:10 0:0:100 0:0:10000 2:0:1 2:0:2 2:0:3 2:0:4 2:0:5" + } + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_set_none(request): + """ + This test is to verify if we want to remove all the large-community + attributes from a set of prefix we can set the value as NONE. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_1 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "0:0:1 0:0:10 0:0:100 2:0:1 2:0:2 2:0:3" + " 2:0:4", + "action": "additive", + } + }, + } + ] + } + }, + "r6": { + "route_maps": { + "RM_R6_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": {"large_community": {"num": "none"}}, + } + ] + } + }, + } + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + }, + "r6": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": { + "route_maps": [ + {"name": "RM_R6_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": { + "route_maps": [ + {"name": "RM_R6_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify Community-list") + dut = "r6" + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Community-list is still present \n Error: {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_lcomm_lists_with_redistribute_static_connected_rmap(request): + """ + This test is to verify redistribute connected and static ipv4 routes + in BGP process with a route-map appending/removing L-comm attributes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("create static routes") + input_dict = { + "r1": { + "static_routes": [ + {"network": "200.50.2.0/32", "next_hop": "10.0.0.6"}, + {"network": "1::1/128", "next_hop": "fd00:0:0:1::2"}, + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("redistribute static routes") + input_dict_1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": "route-map RM_R2_OUT", + }, + { + "redist_type": "connected", + "attribute": "route-map RM_R2_OUT", + }, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": "route-map RM_R2_OUT", + }, + { + "redist_type": "connected", + "attribute": "route-map RM_R2_OUT", + }, + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_3 = { + "r1": { + "route_maps": { + "RM_R2_OUT": [ + { + "action": "permit", + "set": {"large_community": {"num": "55:55:55 555:555:555"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list for static and connected ipv4 route on" " r2") + + input_dict_5 = {"largeCommunity": "55:55:55 555:555:555"} + + if "ipv4" in ADDR_TYPES: + dut = "r2" + networks = ["200.50.2.0/32", "1.0.1.17/32"] + result = verify_bgp_community(tgen, "ipv4", dut, networks, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify large-community-list for static and connected ipv4 route" " on r4") + dut = "r4" + networks = ["200.50.2.0/32", "1.0.1.17/32"] + result = verify_bgp_community(tgen, "ipv4", dut, networks, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + if "ipv6" in ADDR_TYPES: + step("Verify large-community-list for static and connected ipv6 route" " on r2") + dut = "r2" + networks = ["1::1/128", "2001:db8:f::1:17/128"] + result = verify_bgp_community(tgen, "ipv6", dut, networks, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify large-community-list for static and connected ipv6 route" " on r4") + dut = "r4" + networks = ["1::1/128", "2001:db8:f::1:17/128"] + result = verify_bgp_community(tgen, "ipv6", dut, networks, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_set_delete(request): + """ + This test is to verify if we want to remove specific large-community + values from a set of prefix we can make use of DELETE operation based + on L-comm list + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("configure route_map") + input_dict_2 = { + "r6": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "Test", + "value": "1:2:1 1:1:10 1:3:100", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_3 = { + "r6": { + "route_maps": { + "RM_R6_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": {"large_comm_list": {"id": "Test", "delete": True}}, + } + ] + } + }, + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "1:2:1 1:1:10 1:3:100 2:1:1 2:2:2 2:3:3" + " 2:4:4 2:5:5", + "action": "additive", + } + }, + } + ] + } + }, + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + }, + "r6": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": { + "route_maps": [ + {"name": "RM_R6_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": { + "route_maps": [ + {"name": "RM_R6_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r6" + input_dict_5 = {"largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_no_send_community(request): + """ + This test is to verify if we want to remove specific large-community + values from a set of prefix we can make use of DELETE operation based + on L-comm list + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_2 = { + "r5": { + "route_maps": { + "RM_R6_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": {"num": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r5": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r5": { + "route_maps": [ + { + "name": "RM_R6_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r5": { + "route_maps": [ + { + "name": "RM_R6_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r6" + input_dict_4 = {"largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for no-send-community") + input_dict_5 = { + "r5": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": {"r5": {"no_send_community": "large"}} + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": {"r5": {"no_send_community": "large"}} + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify Community-list") + for adt in ADDR_TYPES: + result = verify_bgp_community( + tgen, adt, dut, NETWORKS[adt], input_dict_4, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_create_large_community_lists_with_no_attribute_values(request): + """ + This test is to verify that large-community lists can not be + configured without providing specific L-community values + (for match/delete operation in a route-map). + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create standard large commumity-list") + input_dict_1 = { + "r5": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "Test1", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_match_exact(request): + """ + This test is to verify that Match_EXACT clause should pass + only if all of the L-comm values configured (horizontally) + in the community list is present in the prefix. There must + be no additional L-communities in the prefix. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_2 = { + "r2": { + "route_maps": { + "RM_R4_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": {"num": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create standard large commumity-list") + input_dict_4 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "EXACT", + "value": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_5 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "match": { + "large-community-list": ["EXACT"], + "match_exact": True, + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_6 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r4" + input_dict_4 = {"largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_match_all(request): + """ + This test is to verify that Match_ALL clause should pass + only if ALL of the L-comm values configured (horizontally) + in the community list are present in the prefix. There + could be additional L-communities in the prefix that are + not present in the L-comm list. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_2 = { + "r2": { + "route_maps": { + "RM_R4_OUT": [ + { + "action": "permit", + "set": { + "large_community": { + "num": "1:1:1 1:2:3 2:1:1 2:2:2 2:3:3 2:4:4 2:5:5" + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create standard large commumity-list") + input_dict_4 = { + "r3": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ALL", + "value": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_5 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "match": {"large-community-list": {"id": "ALL"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_6 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r4" + input_dict_4 = {"largeCommunity": "1:1:1 1:2:3 2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_match_any(request): + """ + This test is to verify that Match_ANY clause should pass + only if at-least any one L-comm value configured(vertically) + in large-community list, is present in prefixes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_2 = { + "r2": { + "route_maps": { + "RM_R4_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": {"num": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create standard large commumity-list") + input_dict_4 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "2:1:1", + "large": True, + }, + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "2:2:1", + "large": True, + }, + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "2:3:1", + "large": True, + }, + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "2:4:1", + "large": True, + }, + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_5 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "match": {"large-community-list": {"id": "ANY"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_6 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r4" + input_dict_7 = {"largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_match_regex(request): + """ + This test is to verify large-community lists" operation in a route-map + with match RegEx statements. Match clause should pass only if the + complete string of L-comm values are matched + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_2 = { + "r2": { + "route_maps": { + "RM_R4_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "1:1:1 1:1:2 2:1:3 2:1:4 2:1:5", + }, + "community": {"num": "1:1 1:2 1:3 1:4 1:5"}, + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create standard large commumity-list") + input_dict_4 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ALL", + "value": "1:1:1 2:1:3 2:1:4 2:1:5", + "large": True, + }, + { + "community_type": "expanded", + "action": "permit", + "name": "EXP_ALL", + "value": "1:1:1 2:1:[3-5]", + "large": True, + }, + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_5 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "match": { + "large_community_list": { + "id": "ALL", + }, + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_6 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r4" + input_dict_7 = {"largeCommunity": "1:1:1 1:1:2 2:1:3 2:1:4 2:1:5"} + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Delete route map reference by community-list") + input_dict_3 = {"r4": {"route_maps": ["RM_R4_IN"]}} + result = delete_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_5 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "20", + "match": { + "large_community_list": { + "id": "EXP_ALL", + }, + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("clear ip bgp") + result = clear_bgp_and_verify(tgen, topo, "r4") + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r4" + input_dict_7 = {"largeCommunity": "1:1:1 1:1:2 2:1:3 2:1:4 2:1:5"} + for adt in ADDR_TYPES: + result = verify_bgp_community( + tgen, adt, dut, NETWORKS[adt], input_dict_7, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "largeCommunity is still present \n Error: {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_link_bw_ip/__init__.py b/tests/topotests/bgp_link_bw_ip/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json new file mode 100644 index 0000000..3e3c35e --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json @@ -0,0 +1,19 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65301:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json new file mode 100644 index 0000000..f07e89b --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json @@ -0,0 +1,32 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "extendedCommunity":{ + "string":"LB:65303:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65201:375000 (3.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json new file mode 100644 index 0000000..3501d12 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json @@ -0,0 +1,32 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "extendedCommunity":{ + "string":"LB:65303:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65301:250000 (2.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json new file mode 100644 index 0000000..b1ed004 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json @@ -0,0 +1,32 @@ +{ + "prefix":"198.10.1.11\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "extendedCommunity":{ + "string":"LB:65303:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65201:250000 (2.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json new file mode 100644 index 0000000..89469b8 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json @@ -0,0 +1,29 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "nexthops":[ + { + "ip":"11.1.1.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65201:375000 (3.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf new file mode 100644 index 0000000..b1ec70d --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf @@ -0,0 +1,12 @@ +hostname r1 +! +router bgp 65101 + bgp router-id 11.1.1.1 + no bgp ebgp-requires-policy + bgp bestpath as-path multipath-relax + neighbor 11.1.1.2 remote-as external + neighbor 11.1.1.2 timers 3 10 + neighbor 11.1.1.6 remote-as external + neighbor 11.1.1.6 timers 3 10 + neighbor 11.1.1.6 disable-link-bw-encoding-ieee +! diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json new file mode 100644 index 0000000..3c02e26 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json @@ -0,0 +1,20 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.6", + "weight":25 + }, + { + "fib":true, + "ip":"11.1.1.2", + "weight":75 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json new file mode 100644 index 0000000..3c2d42c --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json @@ -0,0 +1,20 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.6", + "weight":33 + }, + { + "fib":true, + "ip":"11.1.1.2", + "weight":66 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json new file mode 100644 index 0000000..3d80018 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json @@ -0,0 +1,20 @@ +{ + "198.10.1.11\/32":[ + { + "prefix":"198.10.1.11\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.6", + "weight":33 + }, + { + "fib":true, + "ip":"11.1.1.2", + "weight":66 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json new file mode 100644 index 0000000..6b757ef --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json @@ -0,0 +1,20 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.2", + "weight":1 + }, + { + "fib":true, + "ip":"11.1.1.6", + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json new file mode 100644 index 0000000..641ecab --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json @@ -0,0 +1,20 @@ +{ + "198.10.1.11\/32":[ + { + "prefix":"198.10.1.11\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.2", + "weight":1 + }, + { + "fib":true, + "ip":"11.1.1.6", + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json new file mode 100644 index 0000000..6ed3f8e --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json @@ -0,0 +1,15 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.2", + "weight":100 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json new file mode 100644 index 0000000..95531d9 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json @@ -0,0 +1,15 @@ +{ + "198.10.1.11\/32":[ + { + "prefix":"198.10.1.11\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.2", + "weight":100 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json new file mode 100644 index 0000000..beac501 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json @@ -0,0 +1,20 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.6", + "weight":1 + }, + { + "fib":true, + "ip":"11.1.1.2", + "weight":100 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json new file mode 100644 index 0000000..eb27ce2 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json @@ -0,0 +1,20 @@ +{ + "198.10.1.11\/32":[ + { + "prefix":"198.10.1.11\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.6", + "weight":1 + }, + { + "fib":true, + "ip":"11.1.1.2", + "weight":100 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/v4_route.json b/tests/topotests/bgp_link_bw_ip/r1/v4_route.json new file mode 100644 index 0000000..76c6396 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/v4_route.json @@ -0,0 +1,84 @@ +{ + "10.0.1.1\/32":[ + { + "prefix":"10.0.1.1\/32", + "protocol":"ospf", + "distance":110, + "metric":10, + "table":254, + "nexthops":[ + { + "flags":9, + "ip":"0.0.0.0", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "onLink":true + } + ] + }, + { + "prefix":"10.0.1.1\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "10.0.3.4\/32":[ + { + "prefix":"10.0.3.4\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "10.0.20.1\/32":[ + { + "prefix":"10.0.20.1\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":11, + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "active":true, + "onLink":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/zebra.conf b/tests/topotests/bgp_link_bw_ip/r1/zebra.conf new file mode 100644 index 0000000..0fc81f9 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface r1-eth0 + ip address 11.1.1.1/30 +! +interface r1-eth1 + ip address 11.1.1.5/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf new file mode 100644 index 0000000..022270f --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf @@ -0,0 +1,17 @@ +hostname r10 +! +ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32 +! +route-map redist permit 10 + match ip address prefix-list redist +! +router bgp 65354 + bgp router-id 11.1.6.2 + no bgp ebgp-requires-policy + neighbor 11.1.6.1 remote-as external + neighbor 11.1.6.1 timers 3 10 + ! + address-family ipv4 unicast + redistribute connected route-map redist + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r10/zebra.conf b/tests/topotests/bgp_link_bw_ip/r10/zebra.conf new file mode 100644 index 0000000..1a24fda --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r10/zebra.conf @@ -0,0 +1,6 @@ +interface r10-eth0 + ip address 11.1.6.2/30 +! +interface r10-eth1 + ip address 50.1.1.10/32 +! diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json new file mode 100644 index 0000000..3c38689 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json @@ -0,0 +1,19 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65301:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.2.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json new file mode 100644 index 0000000..1895cd8 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json @@ -0,0 +1,19 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65301:250000 (2.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.2.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json new file mode 100644 index 0000000..dfc4171 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json @@ -0,0 +1,32 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "extendedCommunity":{ + "string":"LB:65302:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.2.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65301:250000 (2.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.2.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf new file mode 100644 index 0000000..0c0e859 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf @@ -0,0 +1,13 @@ +hostname r2 +! +router bgp 65201 + bgp router-id 11.1.2.1 + bgp bestpath as-path multipath-relax + no bgp ebgp-requires-policy + neighbor 11.1.1.1 remote-as external + neighbor 11.1.1.1 timers 3 10 + neighbor 11.1.2.2 remote-as external + neighbor 11.1.2.2 timers 3 10 + neighbor 11.1.2.6 remote-as external + neighbor 11.1.2.6 timers 3 10 +! diff --git a/tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json b/tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json new file mode 100644 index 0000000..131100a --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json @@ -0,0 +1,19 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "protocol":"bgp", + "selected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.2.2", + "interfaceName":"r2-eth1", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json b/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json new file mode 100644 index 0000000..7e2fa6b --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json @@ -0,0 +1,20 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.2.6", + "weight":33 + }, + { + "fib":true, + "ip":"11.1.2.2", + "weight":66 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json b/tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json new file mode 100644 index 0000000..d0509bb --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json @@ -0,0 +1,15 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.2.2", + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/zebra.conf b/tests/topotests/bgp_link_bw_ip/r2/zebra.conf new file mode 100644 index 0000000..23573a1 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/zebra.conf @@ -0,0 +1,10 @@ +! +interface r2-eth0 + ip address 11.1.1.2/30 +! +interface r2-eth1 + ip address 11.1.2.1/30 +! +interface r2-eth2 + ip address 11.1.2.5/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r3/bgp-route-1.json b/tests/topotests/bgp_link_bw_ip/r3/bgp-route-1.json new file mode 100644 index 0000000..cddf127 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r3/bgp-route-1.json @@ -0,0 +1,29 @@ +{ + "prefix":"198.10.1.1/32", + "paths":[ + { + "aspath":{ + "string":"65303 65354", + "segments":[ + { + "type":"as-sequence", + "list":[ + 65303, + 65354 + ] + } + ], + "length":2 + }, + "valid":true, + "extendedCommunity":{ + "string":"LB:65303:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.3.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf new file mode 100644 index 0000000..cfd3949 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf @@ -0,0 +1,12 @@ +hostname r3 +! +router bgp 65202 + bgp router-id 11.1.3.1 + bgp bestpath as-path multipath-relax + no bgp ebgp-requires-policy + neighbor 11.1.1.5 remote-as external + neighbor 11.1.1.5 timers 3 10 + neighbor 11.1.3.2 remote-as external + neighbor 11.1.3.2 timers 3 10 + neighbor 11.1.3.2 disable-link-bw-encoding-ieee +! diff --git a/tests/topotests/bgp_link_bw_ip/r3/zebra.conf b/tests/topotests/bgp_link_bw_ip/r3/zebra.conf new file mode 100644 index 0000000..d667669 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r3/zebra.conf @@ -0,0 +1,7 @@ +! +interface r3-eth0 + ip address 11.1.1.6/30 +! +interface r3-eth1 + ip address 11.1.3.1/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json b/tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json new file mode 100644 index 0000000..87d1ae0 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json @@ -0,0 +1,23 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "nexthops":[ + { + "ip":"11.1.4.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "nexthops":[ + { + "ip":"11.1.4.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf new file mode 100644 index 0000000..88b260f --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf @@ -0,0 +1,32 @@ +! +log file bgpd.log +! +! debug bgp updates +! debug bgp zebra +! debug bgp bestpath 198.10.1.1/32 +! +hostname r4 +! +ip prefix-list anycast_ip seq 10 permit 198.10.1.0/24 le 32 +! +route-map anycast_ip permit 10 + match ip address prefix-list anycast_ip + set extcommunity bandwidth num-multipaths +! +route-map anycast_ip permit 20 +! +router bgp 65301 + bgp router-id 11.1.4.1 + bgp bestpath as-path multipath-relax + no bgp ebgp-requires-policy + neighbor 11.1.2.1 remote-as external + neighbor 11.1.2.1 timers 3 10 + neighbor 11.1.4.2 remote-as external + neighbor 11.1.4.2 timers 3 10 + neighbor 11.1.4.6 remote-as external + neighbor 11.1.4.6 timers 3 10 + ! + address-family ipv4 unicast + neighbor 11.1.2.1 route-map anycast_ip out + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json b/tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json new file mode 100644 index 0000000..a9ccf07 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json @@ -0,0 +1,21 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "protocol":"bgp", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.4.2", + "weight":1 + }, + { + "fib":true, + "ip":"11.1.4.6", + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r4/zebra.conf b/tests/topotests/bgp_link_bw_ip/r4/zebra.conf new file mode 100644 index 0000000..ef61f7e --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r4/zebra.conf @@ -0,0 +1,10 @@ +! +interface r4-eth0 + ip address 11.1.2.2/30 +! +interface r4-eth1 + ip address 11.1.4.1/30 +! +interface r4-eth2 + ip address 11.1.4.5/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf new file mode 100644 index 0000000..4014bfb --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf @@ -0,0 +1,23 @@ +hostname r5 +! +ip prefix-list anycast_ip seq 10 permit 198.10.1.0/24 le 32 +! +route-map anycast_ip permit 10 + match ip address prefix-list anycast_ip + set extcommunity bandwidth num-multipaths +! +route-map anycast_ip permit 20 +! +router bgp 65302 + bgp router-id 11.1.5.1 + bgp bestpath as-path multipath-relax + no bgp ebgp-requires-policy + neighbor 11.1.2.5 remote-as external + neighbor 11.1.2.5 timers 3 10 + neighbor 11.1.5.2 remote-as external + neighbor 11.1.5.2 timers 3 10 + ! + address-family ipv4 unicast + neighbor 11.1.2.5 route-map anycast_ip out + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r5/zebra.conf b/tests/topotests/bgp_link_bw_ip/r5/zebra.conf new file mode 100644 index 0000000..66c6596 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r5/zebra.conf @@ -0,0 +1,7 @@ +! +interface r5-eth0 + ip address 11.1.2.6/30 +! +interface r5-eth1 + ip address 11.1.5.1/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf new file mode 100644 index 0000000..89de8ee --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf @@ -0,0 +1,24 @@ +hostname r6 +! +ip prefix-list anycast_ip seq 10 permit 198.10.1.0/24 le 32 +! +route-map anycast_ip permit 10 + match ip address prefix-list anycast_ip + set extcommunity bandwidth num-multipaths +! +route-map anycast_ip permit 20 +! +router bgp 65303 + bgp router-id 11.1.6.1 + bgp bestpath as-path multipath-relax + no bgp ebgp-requires-policy + neighbor 11.1.3.1 remote-as external + neighbor 11.1.3.1 timers 3 10 + neighbor 11.1.3.1 disable-link-bw-encoding-ieee + neighbor 11.1.6.2 remote-as external + neighbor 11.1.6.2 timers 3 10 + ! + address-family ipv4 unicast + neighbor 11.1.3.1 route-map anycast_ip out + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r6/zebra.conf b/tests/topotests/bgp_link_bw_ip/r6/zebra.conf new file mode 100644 index 0000000..66ff563 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r6/zebra.conf @@ -0,0 +1,7 @@ +! +interface r6-eth0 + ip address 11.1.3.2/30 +! +interface r6-eth1 + ip address 11.1.6.1/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf new file mode 100644 index 0000000..39c0c81 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf @@ -0,0 +1,17 @@ +hostname r7 +! +ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32 +! +route-map redist permit 10 + match ip address prefix-list redist +! +router bgp 65351 + bgp router-id 11.1.4.2 + no bgp ebgp-requires-policy + neighbor 11.1.4.1 remote-as external + neighbor 11.1.4.1 timers 3 10 + ! + address-family ipv4 unicast + redistribute connected route-map redist + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r7/zebra.conf b/tests/topotests/bgp_link_bw_ip/r7/zebra.conf new file mode 100644 index 0000000..38e36ca --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r7/zebra.conf @@ -0,0 +1,6 @@ +interface r7-eth0 + ip address 11.1.4.2/30 +! +interface r7-eth1 + ip address 50.1.1.7/32 +! diff --git a/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf new file mode 100644 index 0000000..ea1d546 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf @@ -0,0 +1,17 @@ +hostname r8 +! +ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32 +! +route-map redist permit 10 + match ip address prefix-list redist +! +router bgp 65352 + bgp router-id 11.1.4.6 + no bgp ebgp-requires-policy + neighbor 11.1.4.5 remote-as external + neighbor 11.1.4.5 timers 3 10 + ! + address-family ipv4 unicast + redistribute connected route-map redist + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r8/zebra.conf b/tests/topotests/bgp_link_bw_ip/r8/zebra.conf new file mode 100644 index 0000000..1369e19 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r8/zebra.conf @@ -0,0 +1,6 @@ +interface r8-eth0 + ip address 11.1.4.6/30 +! +interface r8-eth1 + ip address 50.1.1.8/32 +! diff --git a/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf new file mode 100644 index 0000000..a6066ee --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf @@ -0,0 +1,17 @@ +hostname r9 +! +ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32 +! +route-map redist permit 10 + match ip address prefix-list redist +! +router bgp 65353 + bgp router-id 11.1.5.2 + no bgp ebgp-requires-policy + neighbor 11.1.5.1 remote-as external + neighbor 11.1.5.1 timers 3 10 + ! + address-family ipv4 unicast + redistribute connected route-map redist + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r9/zebra.conf b/tests/topotests/bgp_link_bw_ip/r9/zebra.conf new file mode 100644 index 0000000..c73caf3 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r9/zebra.conf @@ -0,0 +1,6 @@ +interface r9-eth0 + ip address 11.1.5.2/30 +! +interface r9-eth1 + ip address 50.1.1.9/32 +! diff --git a/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py b/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py new file mode 100644 index 0000000..af6976b --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py @@ -0,0 +1,575 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_linkbw_ip.py +# +# Copyright (c) 2020 by +# Cumulus Networks, Inc +# Vivek Venkatraman +# + +""" +test_bgp_linkbw_ip.py: Test weighted ECMP using BGP link-bandwidth +""" + +import os +import sys +from functools import partial +import pytest +import json + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.bgpd] + + +""" +This topology is for validating one of the primary use cases for +weighted ECMP (a.k.a. Unequal cost multipath) using BGP link-bandwidth: +https://tools.ietf.org/html/draft-mohanty-bess-ebgp-dmz + +The topology consists of two PODs. Pod-1 consists of a spine switch +and two leaf switches, with two servers attached to the first leaf and +one to the second leaf. Pod-2 consists of one spine and one leaf, with +one server connected to the leaf. The PODs are connected by a super-spine +switch. + +Note that the use of the term "switch" above is in keeping with common +data-center terminology. These devices are all regular routers; for +this scenario, the servers are also routers as they have to announce +anycast IP (VIP) addresses via BGP. +""" + + +def build_topo(tgen): + "Build function" + + # Create 10 routers - 1 super-spine, 2 spines, 3 leafs + # and 4 servers + routers = {} + for i in range(1, 11): + routers[i] = tgen.add_router("r{}".format(i)) + + # Create 13 "switches" - to interconnect the above routers + switches = {} + for i in range(1, 14): + switches[i] = tgen.add_switch("s{}".format(i)) + + # Interconnect R1 (super-spine) to R2 and R3 (the two spines) + switches[1].add_link(tgen.gears["r1"]) + switches[1].add_link(tgen.gears["r2"]) + switches[2].add_link(tgen.gears["r1"]) + switches[2].add_link(tgen.gears["r3"]) + + # Interconnect R2 (spine in pod-1) to R4 and R5 (the associated + # leaf switches) + switches[3].add_link(tgen.gears["r2"]) + switches[3].add_link(tgen.gears["r4"]) + switches[4].add_link(tgen.gears["r2"]) + switches[4].add_link(tgen.gears["r5"]) + + # Interconnect R3 (spine in pod-2) to R6 (associated leaf) + switches[5].add_link(tgen.gears["r3"]) + switches[5].add_link(tgen.gears["r6"]) + + # Interconnect leaf switches to servers + switches[6].add_link(tgen.gears["r4"]) + switches[6].add_link(tgen.gears["r7"]) + switches[7].add_link(tgen.gears["r4"]) + switches[7].add_link(tgen.gears["r8"]) + switches[8].add_link(tgen.gears["r5"]) + switches[8].add_link(tgen.gears["r9"]) + switches[9].add_link(tgen.gears["r6"]) + switches[9].add_link(tgen.gears["r10"]) + + # Create empty networks for the servers + switches[10].add_link(tgen.gears["r7"]) + switches[11].add_link(tgen.gears["r8"]) + switches[12].add_link(tgen.gears["r9"]) + switches[13].add_link(tgen.gears["r10"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + # tgen.mininet_cli() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_linkbw_adv(): + "Test #1: Test BGP link-bandwidth advertisement based on number of multipaths" + logger.info( + "\nTest #1: Test BGP link-bandwidth advertisement based on number of multipaths" + ) + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + # Configure anycast IP on server r7 + logger.info("Configure anycast IP on server r7") + + tgen.net["r7"].cmd("ip addr add 198.10.1.1/32 dev r7-eth1") + + # Check on spine router r2 for link-bw advertisement by leaf router r4 + logger.info("Check on spine router r2 for link-bw advertisement by leaf router r4") + + json_file = "{}/r2/bgp-route-1.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r2, "show bgp ipv4 uni 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on spine router r2" + assert result is None, assertmsg + + # Check on spine router r2 that default weight is used as there is no multipath + logger.info( + "Check on spine router r2 that default weight is used as there is no multipath" + ) + + json_file = "{}/r2/ip-route-1.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r2, "show ip route 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = "JSON output mismatch on spine router r2" + assert result is None, assertmsg + + # Check on super-spine router r1 that link-bw has been propagated by spine router r2 + logger.info( + "Check on super-spine router r1 that link-bw has been propagated by spine router r2" + ) + + json_file = "{}/r1/bgp-route-1.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show bgp ipv4 uni 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + +def test_bgp_cumul_linkbw(): + "Test #2: Test cumulative link-bandwidth propagation" + logger.info("\nTest #2: Test cumulative link-bandwidth propagation") + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r4 = tgen.gears["r4"] + + # Configure anycast IP on additional server r8 + logger.info("Configure anycast IP on server r8") + + tgen.net["r8"].cmd("ip addr add 198.10.1.1/32 dev r8-eth1") + + # Check multipath on leaf router r4 + logger.info("Check multipath on leaf router r4") + + json_file = "{}/r4/bgp-route-1.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r4, "show bgp ipv4 uni 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on leaf router r4" + assert result is None, assertmsg + + # Check regular ECMP is in effect on leaf router r4 + logger.info("Check regular ECMP is in effect on leaf router r4") + + json_file = "{}/r4/ip-route-1.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r4, "show ip route 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = "JSON output mismatch on leaf router r4" + assert result is None, assertmsg + + # Check on spine router r2 that leaf has propagated the cumulative link-bw based on num-multipaths + logger.info( + "Check on spine router r2 that leaf has propagated the cumulative link-bw based on num-multipaths" + ) + + json_file = "{}/r2/bgp-route-2.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r2, "show bgp ipv4 uni 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on spine router r2" + assert result is None, assertmsg + + +def test_weighted_ecmp(): + "Test #3: Test weighted ECMP - multipath with next hop weights" + logger.info("\nTest #3: Test weighted ECMP - multipath with next hop weights") + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + + # Configure anycast IP on additional server r9 + logger.info("Configure anycast IP on server r9") + + tgen.net["r9"].cmd("ip addr add 198.10.1.1/32 dev r9-eth1") + + # Check multipath on spine router r2 + logger.info("Check multipath on spine router r2") + json_file = "{}/r2/bgp-route-3.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r2, "show bgp ipv4 uni 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on spine router r2" + assert result is None, assertmsg + + # Check weighted ECMP is in effect on the spine router r2 + logger.info("Check weighted ECMP is in effect on the spine router r2") + + json_file = "{}/r2/ip-route-2.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r2, "show ip route 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = "JSON output mismatch on spine router r2" + assert result is None, assertmsg + + # Configure anycast IP on additional server r10 + logger.info("Configure anycast IP on server r10") + + tgen.net["r10"].cmd("ip addr add 198.10.1.1/32 dev r10-eth1") + + # Check if bandwidth is properly encoded with non IEEE floatig-point (uint32) format on r3 + logger.info( + "Check if bandwidth is properly encoded with non IEEE floatig-point (uint32) format on r3" + ) + json_file = "{}/r3/bgp-route-1.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r3, "show bgp ipv4 uni 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on r3" + assert result is None, assertmsg + + # Check multipath on super-spine router r1 + logger.info("Check multipath on super-spine router r1") + json_file = "{}/r1/bgp-route-2.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show bgp ipv4 uni 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + # Check weighted ECMP is in effect on the super-spine router r1 + logger.info("Check weighted ECMP is in effect on the super-spine router r1") + json_file = "{}/r1/ip-route-1.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + +def test_weighted_ecmp_link_flap(): + "Test #4: Test weighted ECMP rebalancing upon change (link flap)" + logger.info("\nTest #4: Test weighted ECMP rebalancing upon change (link flap)") + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + # Bring down link on server r9 + logger.info("Bring down link on server r9") + + tgen.net["r9"].cmd("ip link set dev r9-eth1 down") + + # Check spine router r2 has only one path + logger.info("Check spine router r2 has only one path") + + json_file = "{}/r2/ip-route-3.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r2, "show ip route 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on spine router r2" + assert result is None, assertmsg + + # Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1 + logger.info( + "Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1" + ) + + json_file = "{}/r1/bgp-route-3.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show bgp ipv4 uni 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + json_file = "{}/r1/ip-route-2.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + # Bring up link on server r9 + logger.info("Bring up link on server r9") + + tgen.net["r9"].cmd("ip link set dev r9-eth1 up") + + # Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1 + logger.info( + "Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1" + ) + + json_file = "{}/r1/bgp-route-2.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show bgp ipv4 uni 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + json_file = "{}/r1/ip-route-1.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + +def test_weighted_ecmp_second_anycast_ip(): + "Test #5: Test weighted ECMP for a second anycast IP" + logger.info("\nTest #5: Test weighted ECMP for a second anycast IP") + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + # Configure anycast IP on additional server r7, r9 and r10 + logger.info("Configure anycast IP on server r7, r9 and r10") + + tgen.net["r7"].cmd("ip addr add 198.10.1.11/32 dev r7-eth1") + tgen.net["r9"].cmd("ip addr add 198.10.1.11/32 dev r9-eth1") + tgen.net["r10"].cmd("ip addr add 198.10.1.11/32 dev r10-eth1") + + # Check link-bandwidth and weighted ECMP on super-spine router r1 + logger.info("Check link-bandwidth and weighted ECMP on super-spine router r1") + + json_file = "{}/r1/bgp-route-4.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show bgp ipv4 uni 198.10.1.11/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + json_file = "{}/r1/ip-route-3.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 198.10.1.11/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + +def test_paths_with_and_without_linkbw(): + "Test #6: Test paths with and without link-bandwidth - receiver should resort to regular ECMP" + logger.info( + "\nTest #6: Test paths with and without link-bandwidth - receiver should resort to regular ECMP" + ) + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + r1 = tgen.gears["r1"] + + # Configure leaf router r6 to not advertise any link-bandwidth + logger.info("Configure leaf router r6 to not advertise any link-bandwidth") + + tgen.net["r6"].cmd( + 'vtysh -c "conf t" -c "router bgp 65303" -c "address-family ipv4 unicast" -c "no neighbor 11.1.3.1 route-map anycast_ip out"' + ) + + # Check link-bandwidth change on super-spine router r1 + logger.info("Check link-bandwidth change on super-spine router r1") + + json_file = "{}/r1/bgp-route-5.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show bgp ipv4 uni 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + # Check super-spine router r1 resorts to regular ECMP + logger.info("Check super-spine router r1 resorts to regular ECMP") + + json_file = "{}/r1/ip-route-4.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + json_file = "{}/r1/ip-route-5.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 198.10.1.11/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + +def test_linkbw_handling_options(): + "Test #7: Test different options for processing link-bandwidth on the receiver" + logger.info( + "\nTest #7: Test different options for processing link-bandwidth on the receiver" + ) + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + r1 = tgen.gears["r1"] + + # Configure super-spine r1 to skip multipaths without link-bandwidth + logger.info("Configure super-spine r1 to skip multipaths without link-bandwidth") + + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65101" -c "bgp bestpath bandwidth skip-missing"' + ) + + # Check super-spine router r1 resorts to only one path as other path is skipped + logger.info( + "Check super-spine router r1 resorts to only one path as other path is skipped" + ) + + json_file = "{}/r1/ip-route-6.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + json_file = "{}/r1/ip-route-7.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 198.10.1.11/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + # Configure super-spine r1 to use default-weight for multipaths without link-bandwidth + logger.info( + "Configure super-spine r1 to use default-weight for multipaths without link-bandwidth" + ) + + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65101" -c "bgp bestpath bandwidth default-weight-for-missing"' + ) + + # Check super-spine router r1 uses ECMP with weight 1 for path without link-bandwidth + logger.info( + "Check super-spine router r1 uses ECMP with weight 1 for path without link-bandwidth" + ) + + json_file = "{}/r1/ip-route-8.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 198.10.1.1/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + json_file = "{}/r1/ip-route-9.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 198.10.1.11/32 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = "JSON output mismatch on super-spine router r1" + assert result is None, assertmsg + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_listen_on_multiple_addresses/bgp_listen_on_multiple_addresses.json b/tests/topotests/bgp_listen_on_multiple_addresses/bgp_listen_on_multiple_addresses.json new file mode 100644 index 0000000..95de8cc --- /dev/null +++ b/tests/topotests/bgp_listen_on_multiple_addresses/bgp_listen_on_multiple_addresses.json @@ -0,0 +1,154 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "1000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "2000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "2000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "3000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py b/tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py new file mode 100755 index 0000000..988b579 --- /dev/null +++ b/tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_listen_on_multiple_addresses.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 by Boeing Defence Australia +# Adriano Marto Reis +# + +""" +test_bgp_listen_on_multiple_addresses.py: Test BGP daemon listening for +connections on multiple addresses. + + +------+ +------+ +------+ +------+ + | | | | | | | | + | r1 |--------| r2 |--------| r3 |--------| r4 | + | | | | | | | | + +------+ +------+ +------+ +------+ + + | | | | + | AS 1000 | AS 2000 | AS 3000 | + | | | | + +------------+--------------------------------+-------------+ +""" + +import os +import sys +import pytest + + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json +from lib.topojson import linux_intf_config_from_json +from lib.common_config import start_topology +from lib.topotest import router_json_cmp, run_and_expect +from functools import partial + +pytestmark = [pytest.mark.bgpd] + + +LISTEN_ADDRESSES = { + "r1": ["10.0.0.1"], + "r2": ["10.0.0.2", "10.0.1.1"], + "r3": ["10.0.1.2", "10.0.2.1"], + "r4": ["10.0.2.2"], +} + + +def setup_module(mod): + "Sets up the test environment." + json_file = "{}/bgp_listen_on_multiple_addresses.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Adds extra parameters to bgpd so they listen for connections on specific + # multiple addresses. + for router_name in tgen.routers().keys(): + tgen.net[router_name].daemons_options["bgpd"] = "-l " + " -l ".join( + LISTEN_ADDRESSES[router_name] + ) + + start_topology(tgen) + + linux_intf_config_from_json(tgen, topo) + + build_config_from_json(tgen, topo) + + +def teardown_module(_mod): + "Tears-down the test environment." + tgen = get_topogen() + tgen.stop_topology() + + +def test_peering(): + "Checks if the routers peer-up." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + _bgp_converge_initial("r1", "10.0.0.2") + _bgp_converge_initial("r2", "10.0.0.1") + _bgp_converge_initial("r2", "10.0.1.2") + _bgp_converge_initial("r3", "10.0.1.1") + _bgp_converge_initial("r3", "10.0.2.2") + _bgp_converge_initial("r4", "10.0.2.1") + + +def test_listening_address(): + """ + Checks if bgpd is only listening on the specified IP addresses. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for router in tgen.routers().values(): + # bgpd must not be listening on the default address. + output = router.run("netstat -nlt4 | grep 0.0.0.0:179") + assert output == "", "{}: bpgd is listening on 0.0.0.0:179".format(router.name) + + # bgpd must be listening on the specified addresses. + for address in LISTEN_ADDRESSES[router.name]: + output = router.run("netstat -nlt4 | grep {}:179".format(address)) + assert output != "", "{}: bpgd is not listening on {}:179".format( + router.name, address + ) + + +def _bgp_converge_initial(router_name, peer_address, timeout=180): + """ + Waits for the BGP connection between a given router and a given peer + (specified by its IP address) to be established. If the connection is + not established within a given timeout, then an exception is raised. + """ + tgen = get_topogen() + router = tgen.routers()[router_name] + expected = {"ipv4Unicast": {"peers": {peer_address: {"state": "Established"}}}} + + test_func = partial(router_json_cmp, router, "show ip bgp summary json", expected) + _, result = run_and_expect(test_func, None, count=timeout, wait=1) + assert result is None, "{}: Failed to establish connection with {}".format( + router_name, peer_address + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_llgr/__init__.py b/tests/topotests/bgp_llgr/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_llgr/r0/bgpd.conf b/tests/topotests/bgp_llgr/r0/bgpd.conf new file mode 100644 index 0000000..a20e64f --- /dev/null +++ b/tests/topotests/bgp_llgr/r0/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65000 + no bgp ebgp-requires-policy + bgp graceful-restart + bgp graceful-restart restart-time 0 + bgp long-lived-graceful-restart stale-time 20 + neighbor 192.168.0.2 remote-as external + neighbor 192.168.0.2 timers 3 10 + neighbor 192.168.0.2 timers connect 1 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_llgr/r0/zebra.conf b/tests/topotests/bgp_llgr/r0/zebra.conf new file mode 100644 index 0000000..4d11b67 --- /dev/null +++ b/tests/topotests/bgp_llgr/r0/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.1.1/32 +! +int r0-eth0 + ip address 192.168.0.1/24 +! diff --git a/tests/topotests/bgp_llgr/r1/bgpd.conf b/tests/topotests/bgp_llgr/r1/bgpd.conf new file mode 100644 index 0000000..bf33eeb --- /dev/null +++ b/tests/topotests/bgp_llgr/r1/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65001 + no bgp ebgp-requires-policy + bgp graceful-restart + bgp graceful-restart restart-time 0 + bgp long-lived-graceful-restart stale-time 20 + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 3 10 + neighbor 192.168.1.2 timers connect 1 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_llgr/r1/zebra.conf b/tests/topotests/bgp_llgr/r1/zebra.conf new file mode 100644 index 0000000..1782edc --- /dev/null +++ b/tests/topotests/bgp_llgr/r1/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.1.1/32 +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_llgr/r2/bgpd.conf b/tests/topotests/bgp_llgr/r2/bgpd.conf new file mode 100644 index 0000000..6515ef8 --- /dev/null +++ b/tests/topotests/bgp_llgr/r2/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65002 + no bgp ebgp-requires-policy + bgp graceful-restart + bgp long-lived-graceful-restart stale-time 20 + neighbor 192.168.0.1 remote-as external + neighbor 192.168.0.1 timers 3 10 + neighbor 192.168.0.1 timers connect 1 + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 3 10 + neighbor 192.168.1.1 timers connect 1 + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 3 10 + neighbor 192.168.2.1 timers connect 1 + neighbor PG peer-group + neighbor PG remote-as external + bgp listen range 192.168.3.0/24 peer-group PG + address-family ipv4 unicast + neighbor 192.168.1.1 weight 100 diff --git a/tests/topotests/bgp_llgr/r2/zebra.conf b/tests/topotests/bgp_llgr/r2/zebra.conf new file mode 100644 index 0000000..e5e88d4 --- /dev/null +++ b/tests/topotests/bgp_llgr/r2/zebra.conf @@ -0,0 +1,13 @@ +! +int r2-eth0 + ip address 192.168.0.2/24 +! +int r2-eth1 + ip address 192.168.1.2/24 +! +int r2-eth2 + ip address 192.168.2.2/24 +! +int r2-eth3 + ip address 192.168.3.2/24 +! diff --git a/tests/topotests/bgp_llgr/r3/bgpd.conf b/tests/topotests/bgp_llgr/r3/bgpd.conf new file mode 100644 index 0000000..dfe20c1 --- /dev/null +++ b/tests/topotests/bgp_llgr/r3/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65003 + bgp router-id 192.168.2.1 + no bgp ebgp-requires-policy + bgp graceful-restart + bgp long-lived-graceful-restart stale-time 20 + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 3 10 + neighbor 192.168.2.2 timers connect 1 diff --git a/tests/topotests/bgp_llgr/r3/zebra.conf b/tests/topotests/bgp_llgr/r3/zebra.conf new file mode 100644 index 0000000..8a7941b --- /dev/null +++ b/tests/topotests/bgp_llgr/r3/zebra.conf @@ -0,0 +1,4 @@ +! +int r3-eth0 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/bgp_llgr/r4/bgpd.conf b/tests/topotests/bgp_llgr/r4/bgpd.conf new file mode 100644 index 0000000..49ce387 --- /dev/null +++ b/tests/topotests/bgp_llgr/r4/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 65004 + bgp router-id 192.168.3.1 + no bgp ebgp-requires-policy + bgp graceful-restart + bgp graceful-restart restart-time 0 + bgp long-lived-graceful-restart stale-time 30 + neighbor 192.168.3.2 remote-as external + neighbor 192.168.3.2 timers 3 10 + neighbor 192.168.3.2 timers connect 1 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_llgr/r4/zebra.conf b/tests/topotests/bgp_llgr/r4/zebra.conf new file mode 100644 index 0000000..7ec20be --- /dev/null +++ b/tests/topotests/bgp_llgr/r4/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.1.2/32 +! +int r4-eth0 + ip address 192.168.3.1/24 +! diff --git a/tests/topotests/bgp_llgr/test_bgp_llgr.py b/tests/topotests/bgp_llgr/test_bgp_llgr.py new file mode 100644 index 0000000..d604871 --- /dev/null +++ b/tests/topotests/bgp_llgr/test_bgp_llgr.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2021 by +# Donatas Abraitis +# + +""" +Test if BGP Long-lived Graceful Restart capability works: + Check if we can see 172.16.1.1/32 after initial converge in R3. + Check if we can see 172.16.1.1/32 as best selected due to higher weigth in R2. + Kill bgpd in R1. + Check if we can see 172.16.1.1/32 as stale in R2. + Check if we can see 172.16.1.1/32 depreferenced due to LLGR_STALE in R2. + Check if we can see 172.16.1.1/32 after R1 was killed in R3. +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = [pytest.mark.bgpd] + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +from lib.common_config import ( + kill_router_daemons, + start_router_daemons, + step, +) + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(0, 6): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s0") + switch.add_link(tgen.gears["r0"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + # Dynamic neighbor + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_llgr(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp json")) + expected = { + "routes": { + "172.16.1.1/32": [{"nexthops": [{"ip": "192.168.2.2", "used": True}]}], + "172.16.1.2/32": [{"nexthops": [{"ip": "192.168.2.2", "used": True}]}], + } + } + return topotest.json_cmp(output, expected) + + step("Check if we can see 172.16.1.1/32 after initial converge in R3") + test_func = functools.partial(_bgp_converge, r3) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see 172.16.1.1/32 in r3" + + def _bgp_weight_prefered_route(router): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.1.1/32 json")) + expected = { + "paths": [ + { + "bestpath": {"selectionReason": "Weight"}, + "nexthops": [ + { + "ip": "192.168.1.1", + } + ], + } + ] + } + return topotest.json_cmp(output, expected) + + step( + "Check if we can see 172.16.1.1/32 as best selected due to higher weigth in R2" + ) + test_func = functools.partial(_bgp_weight_prefered_route, r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Prefix 172.16.1.1/32 is not selected as bests path due to weight" + + step("Kill bgpd in R1") + kill_router_daemons(tgen, "r1", ["bgpd"]) + + def _bgp_stale_route(router, prefix): + output = json.loads(router.vtysh_cmd("show ip bgp {} json".format(prefix))) + expected = {"paths": [{"community": {"string": "llgr-stale"}, "stale": True}]} + return topotest.json_cmp(output, expected) + + step("Check if we can see 172.16.1.1/32 as stale in R2") + test_func = functools.partial(_bgp_stale_route, r2, "172.16.1.1/32") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Prefix 172.16.1.1/32 is not stale" + + def _bgp_llgr_depreference_route(router): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.1.1/32 json")) + expected = { + "paths": [ + { + "bestpath": {"selectionReason": "First path received"}, + "nexthops": [ + { + "ip": "192.168.0.1", + } + ], + } + ] + } + return topotest.json_cmp(output, expected) + + step("Check if we can see 172.16.1.1/32 depreferenced due to LLGR_STALE in R2") + test_func = functools.partial(_bgp_llgr_depreference_route, r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Prefix 172.16.1.1/32 is not depreferenced due to LLGR_STALE" + + step("Check if we can see 172.16.1.1/32 after R1 was killed in R3") + test_func = functools.partial(_bgp_converge, r3) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see 172.16.1.1/32 in r3" + + step("Kill bgpd in R4 (dynamic peer)") + kill_router_daemons(tgen, "r4", ["bgpd"]) + + step("Check if we can see 172.16.1.2/32 after R4 (dynamic peer) was killed") + test_func = functools.partial(_bgp_stale_route, r2, "172.16.1.2/32") + _, result = topotest.run_and_expect(test_func, None, count=120, wait=0.5) + assert result is None, "Cannot see 172.16.1.2/32 in r2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_as/__init__.py b/tests/topotests/bgp_local_as/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_local_as/r1/bgpd.conf b/tests/topotests/bgp_local_as/r1/bgpd.conf new file mode 100644 index 0000000..fa147d8 --- /dev/null +++ b/tests/topotests/bgp_local_as/r1/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as 65002 + neighbor 192.168.1.2 local-as 65002 + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor PG peer-group + neighbor PG remote-as 65003 + neighbor PG local-as 65003 + neighbor 192.168.2.2 peer-group PG + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_local_as/r1/zebra.conf b/tests/topotests/bgp_local_as/r1/zebra.conf new file mode 100644 index 0000000..5b32fae --- /dev/null +++ b/tests/topotests/bgp_local_as/r1/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 172.16.255.1/32 +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +interface r1-eth1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/bgp_local_as/r2/bgpd.conf b/tests/topotests/bgp_local_as/r2/bgpd.conf new file mode 100644 index 0000000..9c25bdf --- /dev/null +++ b/tests/topotests/bgp_local_as/r2/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as internal + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 +! diff --git a/tests/topotests/bgp_local_as/r2/zebra.conf b/tests/topotests/bgp_local_as/r2/zebra.conf new file mode 100644 index 0000000..0c95656 --- /dev/null +++ b/tests/topotests/bgp_local_as/r2/zebra.conf @@ -0,0 +1,4 @@ +! +interface r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_local_as/r3/bgpd.conf b/tests/topotests/bgp_local_as/r3/bgpd.conf new file mode 100644 index 0000000..54ccd90 --- /dev/null +++ b/tests/topotests/bgp_local_as/r3/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as internal + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 +! diff --git a/tests/topotests/bgp_local_as/r3/zebra.conf b/tests/topotests/bgp_local_as/r3/zebra.conf new file mode 100644 index 0000000..d28dedd --- /dev/null +++ b/tests/topotests/bgp_local_as/r3/zebra.conf @@ -0,0 +1,4 @@ +! +interface r3-eth0 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/bgp_local_as/test_bgp_local_as.py b/tests/topotests/bgp_local_as/test_bgp_local_as.py new file mode 100644 index 0000000..9e5f146 --- /dev/null +++ b/tests/topotests/bgp_local_as/test_bgp_local_as.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" + +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_local_as_same_remote_as(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_check_local_as_same_remote_as(): + output = json.loads( + tgen.gears["r2"].vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json") + ) + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "Local"}, + "nexthops": [{"ip": "192.168.1.1", "hostname": "r1"}], + } + ] + } + return topotest.json_cmp(output, expected) + + step("Check if iBGP works when local-as == remote-as") + test_func = functools.partial(_bgp_check_local_as_same_remote_as) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefixes on R2" + + +def test_bgp_peer_group_local_as_same_remote_as(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_check_local_as_same_remote_as(): + output = json.loads( + tgen.gears["r3"].vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json") + ) + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "Local"}, + "nexthops": [{"ip": "192.168.2.1", "hostname": "r1"}], + } + ] + } + return topotest.json_cmp(output, expected) + + step("Initial BGP converge") + test_func = functools.partial(_bgp_check_local_as_same_remote_as) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefixes on R3" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/__init__.py b/tests/topotests/bgp_local_as_dotplus_private_remove/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r1/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r1/bgpd.conf new file mode 100644 index 0000000..1846df2 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r1/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 0.65000 as-notation dot+ + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 0.1000 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 local-as 0.500 + address-family ipv4 unicast + neighbor 192.168.255.2 remove-private-AS + redistribute connected diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r1/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r1/zebra.conf new file mode 100644 index 0000000..0a283c0 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r2/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r2/bgpd.conf new file mode 100644 index 0000000..c9adfa4 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r2/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 0.1000 as-notation dot+ + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 0.500 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r2/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r2/zebra.conf new file mode 100644 index 0000000..606c17b --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r3/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r3/bgpd.conf new file mode 100644 index 0000000..9a83127 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r3/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 3000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 1000 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 local-as 500 + address-family ipv4 unicast + neighbor 192.168.255.2 remove-private-AS + redistribute connected diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r3/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r3/zebra.conf new file mode 100644 index 0000000..39499a1 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r3/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r3-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r4/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r4/bgpd.conf new file mode 100644 index 0000000..c9adfa4 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r4/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 0.1000 as-notation dot+ + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 0.500 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r4/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r4/zebra.conf new file mode 100644 index 0000000..b859115 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r4/zebra.conf @@ -0,0 +1,6 @@ +! +interface r4-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py b/tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py new file mode 100644 index 0000000..930fd79 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python + +# +# bgp_local_as_private_remove.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +bgp_local_as_private_remove.py: +Test if primary AS number is not removed in cases when `local-as` +used together with `remove-private-AS`. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_remove_private_as(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + r4 = tgen.gears["r4"] + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge initially" + + def _bgp_as_path(router, asn_path, asn_length): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.254/32 json")) + expected = { + "paths": [ + { + "aspath": { + "string": asn_path, + "length": asn_length, + } + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_as_path, r2, "0.500", 1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Private ASNs not stripped" + + test_func = functools.partial(_bgp_as_path, r4, "0.500 0.3000", 2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Private ASNs not stripped" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_as_private_remove/__init__.py b/tests/topotests/bgp_local_as_private_remove/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf new file mode 100644 index 0000000..16a9eea --- /dev/null +++ b/tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 1000 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 local-as 500 + address-family ipv4 unicast + neighbor 192.168.255.2 remove-private-AS + redistribute connected diff --git a/tests/topotests/bgp_local_as_private_remove/r1/zebra.conf b/tests/topotests/bgp_local_as_private_remove/r1/zebra.conf new file mode 100644 index 0000000..0a283c0 --- /dev/null +++ b/tests/topotests/bgp_local_as_private_remove/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf new file mode 100644 index 0000000..802c327 --- /dev/null +++ b/tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 1000 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 500 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_local_as_private_remove/r2/zebra.conf b/tests/topotests/bgp_local_as_private_remove/r2/zebra.conf new file mode 100644 index 0000000..606c17b --- /dev/null +++ b/tests/topotests/bgp_local_as_private_remove/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf new file mode 100644 index 0000000..9a83127 --- /dev/null +++ b/tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 3000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 1000 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 local-as 500 + address-family ipv4 unicast + neighbor 192.168.255.2 remove-private-AS + redistribute connected diff --git a/tests/topotests/bgp_local_as_private_remove/r3/zebra.conf b/tests/topotests/bgp_local_as_private_remove/r3/zebra.conf new file mode 100644 index 0000000..39499a1 --- /dev/null +++ b/tests/topotests/bgp_local_as_private_remove/r3/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r3-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf new file mode 100644 index 0000000..802c327 --- /dev/null +++ b/tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 1000 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 500 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_local_as_private_remove/r4/zebra.conf b/tests/topotests/bgp_local_as_private_remove/r4/zebra.conf new file mode 100644 index 0000000..b859115 --- /dev/null +++ b/tests/topotests/bgp_local_as_private_remove/r4/zebra.conf @@ -0,0 +1,6 @@ +! +interface r4-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py b/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py new file mode 100644 index 0000000..9d22a79 --- /dev/null +++ b/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# bgp_local_as_private_remove.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +bgp_local_as_private_remove.py: +Test if primary AS number is not removed in cases when `local-as` +used together with `remove-private-AS`. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_remove_private_as(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + r4 = tgen.gears["r4"] + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge initially" + + def _bgp_as_path(router, asn_path, asn_length): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.254/32 json")) + expected = { + "paths": [ + { + "aspath": { + "string": asn_path, + "length": asn_length, + } + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_as_path, r2, "500", 1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Private ASNs not stripped" + + test_func = functools.partial(_bgp_as_path, r4, "500 3000", 2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Private ASNs not stripped" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_asn/bgp_local_asn_agg.json b/tests/topotests/bgp_local_asn/bgp_local_asn_agg.json new file mode 100644 index 0000000..a842c9e --- /dev/null +++ b/tests/topotests/bgp_local_asn/bgp_local_asn_agg.json @@ -0,0 +1,147 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r1": {}}} + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r1": {}}} + } + } + } + } + }, + "static_routes":[ + { + "network":"10.1.1.0/32", + "next_hop":"Null0" + }, + { + "network":"10:1::1:0/128", + "next_hop":"Null0" + }] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r2": {}}} + } + } + } + } + }, + "static_routes":[ + { + "network":"10.1.2.0/32", + "next_hop":"Null0" + }, + { + "network":"10:1::2:0/128", + "next_hop":"Null0" + }] + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {}}}, + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {}}}, + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_local_asn/bgp_local_asn_ecmp.json b/tests/topotests/bgp_local_asn/bgp_local_asn_ecmp.json new file mode 100644 index 0000000..7184679 --- /dev/null +++ b/tests/topotests/bgp_local_asn/bgp_local_asn_ecmp.json @@ -0,0 +1,317 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + } + + ], + "static_routes":[ + { + "network":"10.0.0.1/32", + "next_hop":"Null0" + }, + { + "network":"10::1/128", + "next_hop":"Null0" + } + ] + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + } + ] + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link8": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "r4": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {}, + "r3-link8": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "r4": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {}, + "r3-link8": {} + } + } + } + } + } + } + } + ] + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link8": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {}, + "r4-link5": {}, + "r4-link6": {}, + "r4-link7": {}, + "r4-link8": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {}, + "r4-link5": {}, + "r4-link6": {}, + "r4-link7": {}, + "r4-link8": {} + } + } + } + } + } + } + } + ] + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_local_asn/bgp_local_asn_topo1.json b/tests/topotests/bgp_local_asn/bgp_local_asn_topo1.json new file mode 100644 index 0000000..da665be --- /dev/null +++ b/tests/topotests/bgp_local_asn/bgp_local_asn_topo1.json @@ -0,0 +1,132 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": {"dest_link": {"r1": {}}} + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": {"dest_link": {"r1": {}}} + } + } + } + } + }, + "static_routes":[ + { + "network":"10.1.1.0/32", + "next_hop":"Null0" + }, + { + "network":"10:1::1:0/128", + "next_hop":"Null0" + }] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + } + } + } + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_local_asn/bgp_local_asn_topo2.json b/tests/topotests/bgp_local_asn/bgp_local_asn_topo2.json new file mode 100644 index 0000000..51f9bb2 --- /dev/null +++ b/tests/topotests/bgp_local_asn/bgp_local_asn_topo2.json @@ -0,0 +1,117 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "12000100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {}}} + } + } + } + } + } + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "12000200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + } + } + } + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "12000300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "12000400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_local_asn/bgp_local_asn_vrf_topo1.json b/tests/topotests/bgp_local_asn/bgp_local_asn_vrf_topo1.json new file mode 100644 index 0000000..d415b63 --- /dev/null +++ b/tests/topotests/bgp_local_asn/bgp_local_asn_vrf_topo1.json @@ -0,0 +1,152 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128}, + "routers": { + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp":[ + { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + + "r3": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + + "r3": {"dest_link": {"r2": {}}} + } + } + } + } + } + ], + "static_routes":[ + { + "network":"10.0.0.1/32", + "next_hop":"Null0" + }, + { + "network":"10::1/128", + "next_hop":"Null0" + }] + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto","vrf": "BLUE"} + }, + "vrfs":[ + { + "name": "BLUE", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": {"r3": {}}} + } + } + } + } + }, + { + "local_as": "300", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": {"r3": {}}} + } + } + } + } + } + ] + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto","vrf": "BLUE"} + }, + "vrfs":[ + { + "name": "BLUE", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "400", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": {"r4": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": {"r4": {}}} + } + } + } + } + } + ] + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_local_asn/bgp_local_asn_vrf_topo2.json b/tests/topotests/bgp_local_asn/bgp_local_asn_vrf_topo2.json new file mode 100644 index 0000000..311ccce --- /dev/null +++ b/tests/topotests/bgp_local_asn/bgp_local_asn_vrf_topo2.json @@ -0,0 +1,128 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128}, + "routers": { + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto","vrf": "RED"} + }, + "vrfs":[{"name": "RED","id": "1"}], + "bgp":[ + { + "local_as": "200", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r2": {}}} + } + } + } + } + } + ] + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto","vrf": "RED"}, + "r4": {"ipv4": "auto", "ipv6": "auto","vrf": "BLUE"} + }, + "vrfs":[{"name": "RED","id": "1"}, + {"name": "BLUE","id": "2"}], + "bgp": + [ + { + "local_as": "300", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": {"r3": {}}} + } + } + } + } + }, + { + "local_as": "300", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": {"r3": {}}} + } + } + } + } + } + ] + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto","vrf": "BLUE"} + }, + "vrfs":[{"name": "BLUE","id": "1"}], + "bgp": + [ + { + "local_as": "400", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": {"r4": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": {"r4": {}}} + } + } + } + } + } + ] + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_local_asn/test_bgp_local_asn_agg.py b/tests/topotests/bgp_local_asn/test_bgp_local_asn_agg.py new file mode 100644 index 0000000..26e8fe9 --- /dev/null +++ b/tests/topotests/bgp_local_asn/test_bgp_local_asn_agg.py @@ -0,0 +1,407 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: +1. Verify the BGP Local AS functionality by aggregating routes in between eBGP Peers. +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + check_address_types, + check_router_status +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + verify_bgp_rib, + create_router_bgp, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK_1_1 = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"} +NETWORK_1_2 = {"ipv4": "10.1.2.0/32", "ipv6": "10:1::2:0/128"} +AGGREGATE_NW = {"ipv4": "10.1.0.0/16", "ipv6": "10:1::/96"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_local_asn_agg.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +#################################################################################################################### +# +# Testcases +# +#################################################################################################################### + + +def test_verify_bgp_local_as_agg_in_EBGP_p0(request): + """ + Verify the BGP Local AS functionality by aggregating routes in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Done in base config: Advertise prefix 10.1.1.0/24 from Router-1(AS-100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/120 from Router-1(AS-100)." + ) + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK_1_1[addr_type]}]} + } + + input_static_verify_r2 = { + "r2": {"static_routes": [{"network": NETWORK_1_2[addr_type]}]} + } + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", input_static_verify_r2) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure aggregate-address to summarise all the advertised routes.") + for addr_type in ADDR_TYPES: + route_aggregate = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "aggregate_address": [ + { + "network": AGGREGATE_NW[addr_type], + "summary": True, + "as_set": True, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, route_aggregate) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that we see a summarised route on advertising router R3 " + "and receiving router R4 for both AFIs" + ) + + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + input_static_r1 = { + "r1": {"static_routes": [{"network": [NETWORK_1_1[addr_type]]}]} + } + + input_static_r2 = { + "r2": {"static_routes": [{"network": [NETWORK_1_2[addr_type]]}]} + } + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_agg_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1", "r2"], [input_static_r1, input_static_r2]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 110 {100,110,200} by following " + "commands at R3 router." + ) + dut = "r3" + aspath = "{100,110,200}" + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + result = verify_bgp_rib( + tgen, addr_type, dut, input_static_agg_r1, aspath=aspath + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "{100,200}" + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + result = verify_bgp_rib( + tgen, addr_type, dut, input_static_agg_r1, aspath=aspath + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "110 {100,200}" + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + result = verify_bgp_rib( + tgen, addr_type, dut, input_static_agg_r1, aspath=aspath + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_asn/test_bgp_local_asn_ecmp.py b/tests/topotests/bgp_local_asn/test_bgp_local_asn_ecmp.py new file mode 100644 index 0000000..8a11570 --- /dev/null +++ b/tests/topotests/bgp_local_asn/test_bgp_local_asn_ecmp.py @@ -0,0 +1,511 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# +########################################################################################################################################## +# +# Testcases +# +########################################################################################################################################### +########################################################################################################################################### +# +# 1.10.1.7. Verify the BGP Local AS functionality with ECMP on 8 links by adding no-prepend and replace-as command in between eBGP Peers. +# +################################################################################################################################################# + +import os +import sys +import time +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + create_static_routes, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + check_address_types, + check_router_status, + create_static_routes, + verify_fib_routes, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + verify_bgp_rib, + create_router_bgp, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +BGP_CONVERGENCE = False +NETWORK = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_local_asn_ecmp.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +########################################################################################################################################## +# +# Testcases +# +########################################################################################################################################### + + +def test_verify_bgp_local_as_in_ecmp_EBGP_p0(request): + """ + Verify the BGP Local AS functionality with ECMP on 8 links by + adding no-prepend and replace-as command in between eBGP Peers. + """ + + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + dut = "r1" + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "local_asn": {"local_as": "110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + dest_link = {} + for link_no in range(1, 9): + link = "r3-link" + str(link_no) + dest_link[link] = {"local_asn": {"local_as": "110"}} + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r4": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "local_asn": {"remote_as": "110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + dest_link = {} + for link_no in range(1, 9): + link = "r4-link" + str(link_no) + dest_link[link] = {"local_asn": {"remote_as": "110"}} + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "400", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r3": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 110 200 100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + dest_link = {} + for link_no in range(1, 9): + link = "r3-link" + str(link_no) + dest_link[link] = {"local_asn": {"local_as": "110"}} + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r4": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R2") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + dest_link = {} + for link_no in range(1, 9): + link = "r3-link" + str(link_no) + dest_link[link] = { + "local_asn": {"local_as": "110", "no_prepend": True, "replace_as": True} + } + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r4": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_asn/test_bgp_local_asn_topo1.py b/tests/topotests/bgp_local_asn/test_bgp_local_asn_topo1.py new file mode 100644 index 0000000..25c9bee --- /dev/null +++ b/tests/topotests/bgp_local_asn/test_bgp_local_asn_topo1.py @@ -0,0 +1,3642 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +########################################################################################################## +# +# Functionality Testcases +# +########################################################################################################## +""" +1. Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between eBGP Peers. +2. Verify the BGP Local AS functionality by configuring 4 Byte AS at R3 and 2 Byte AS at R2 & R4 in between eBGP Peers. +3. Verify that BGP Local AS functionality by performing graceful restart in between eBGP Peers. +4. Verify the BGP Local AS functionality by adding another AS & by same AS with AS-Prepend command in between eBGP Peers. +4. Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between iBGP Peers. +5. Verify the BGP Local AS functionality with allowas-in in between iBGP Peers. +6. Verify that BGP Local AS functionality by performing shut/ noshut on the interfaces in between BGP neighbors. +7. Verify that BGP Local AS functionality by restarting BGP,Zebra and FRR services and + further restarting clear BGP * and shutdown BGP neighbor. +8. Verify the BGP Local AS functionality with different AS configurations. +9. Verify the BGP Local AS functionality with R3& R4 with different AS configurations. +""" + +import os +import sys +import time +import pytest +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + create_static_routes, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + get_frr_ipv6_linklocal, + check_address_types, + check_router_status, + create_static_routes, + verify_fib_routes, + create_route_maps, + kill_router_daemons, + start_router_daemons, + shutdown_bringup_interface, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + clear_bgp_and_verify, + verify_bgp_rib, + modify_as_number, + create_router_bgp, + verify_bgp_advertised_routes_from_neighbor, + verify_graceful_restart, + verify_r_bit, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} +NEXT_HOP_IP_GR = {"ipv4": "10.0.0.5", "ipv6": "fd00:0:0:1::2/64"} +NEXT_HOP_IP_1 = {"ipv4": "10.0.0.101", "ipv6": "fd00::1"} +NEXT_HOP_IP_2 = {"ipv4": "10.0.0.102", "ipv6": "fd00::2"} + +BGP_CONVERGENCE = False +PREFERRED_NEXT_HOP = "link_local" +KEEPALIVETIMER = 1 +HOLDDOWNTIMER = 3 + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_local_asn_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +########################################################################################################## +# +# Local APIs +# +########################################################################################################## + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + This function groups the repetitive function calls into one function. + """ + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + return True + + +def next_hop_per_address_family( + tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP +): + """ + This function returns link_local or global next_hop per address-family + """ + intferface = topo["routers"][peer]["links"]["{}".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in preferred_next_hop: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +########################################################################################################## +# +# Testcases +# +########################################################################################################## + + +def test_verify_bgp_local_as_in_EBGP_p0(request): + """ + Verify the BGP Local AS functionality by adding no-prepend and + replace-as command in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 110 200 100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r3" + aspath = "200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_4B_AS_mid_2B_AS_p0(request): + """ + Verify the BGP Local AS functionality by configuring 4 Byte AS + at R3 and 2 Byte AS at R2 & R4 in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "12000110" + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: { + "local_asn": { + "remote_as": "12000110" + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-12000110 is got added in the AS list 12000110 200 100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "12000110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "12000110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r3" + aspath = "200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "12000110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "12000110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_GR_EBGP_p0(request): + """ + Verify that BGP Local AS functionality by performing graceful restart in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure basic BGP Peerings between R1,R2,R3 and R4") + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP_GR[addr_type], + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "local_asn": {"remote_as": "110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "400", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "local_asn": {"remote_as": "110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP_GR[addr_type], + } + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 110 200 100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + """ + GR Steps : Helper BGP router R2, mark and unmark IPV4 routes + as stale as the restarting router R3 come up within the restart time + """ + # Create route-map to prefer global next-hop + input_dict = { + "r2": { + "route_maps": { + "rmap_global": [ + {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}} + ] + } + }, + "r3": { + "route_maps": { + "rmap_global": [ + {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}} + ] + } + }, + } + result = create_route_maps(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_neigh_rm = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_neigh_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure graceful-restart + input_dict = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "graceful-restart-helper": True, + "local_asn": {"remote_as": "110"}, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "graceful-restart-helper": True, + "local_asn": {"remote_as": "110"}, + } + } + } + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {"graceful-restart": True}}} + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r3", peer="r2") + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r3", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r2" + peer = "r3" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + input_topo = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, "bgp") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info("[Phase 2] : R3 goes for reload ") + + kill_router_daemons(tgen, "r3", ["bgpd"]) + + logger.info( + "[Phase 3] : R3 is still down, restart time 120 sec." + " So time verify the routes are present in BGP RIB" + " and ZEBRA" + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info("[Phase 5] : R3 is about to come up now ") + start_router_daemons(tgen, "r3", ["bgpd"]) + + logger.info("[Phase 5] : R3 is UP Now ! ") + + for addr_type in ADDR_TYPES: + result = verify_bgp_convergence(tgen, topo) + assert ( + result is True + ), "BGP Convergence after BGPd restart" " :Failed \n Error:{}".format(result) + + # Verifying GR stats + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r3", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r2", peer="r3") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Configure local-as with no-prepend at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R2") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_aspath_p0(request): + """ + Verify the BGP Local AS functionality by adding another AS & by same AS with AS-Prepend command in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure basic BGP Peerings between R1,R2,R3 and R4") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 110 200 100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r3" + aspath = "200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure a route-map on R3 to prepend AS 2 times.") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r3": { + "route_maps": { + "ASP_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "path": {"as_num": "1000 1000", "as_action": "prepend"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure route map in out direction on R4") + # Configure neighbor for route map + input_dict_7 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "ASP_{}".format( + addr_type + ), + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verify that AS-300 is got replaced with 200 in the AS list 110 1000 1000 200 100 by following" + "commands at R3 router." + ) + dut = "r4" + aspath = "110 1000 1000 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_iBGP_p0(request): + """ + Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between iBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Modify AS Number for R3") + input_dict_modify_as_number = {"r3": {"bgp": {"local_as": 200}}} + result = modify_as_number(tgen, topo, input_dict_modify_as_number) + + step("Base config is done as part of JSON") + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "400", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "local_asn": {"remote_as": "110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "next_hop_self": True, + "local_asn": { + "remote_as": "200", + }, + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 110 200 100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r4" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "110 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_allow_as_in_iBGP_p0(request): + """ + Verify the BGP Local AS functionality with allowas-in in between iBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Modidy AS Number for R4") + input_dict_modify_as_number = {"r4": {"bgp": {"local_as": 100}}} + result = modify_as_number(tgen, topo, input_dict_modify_as_number) + + step("Base config is done as part of JSON") + dut = "r1" + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure allow-as at R4") + for addr_type in ADDR_TYPES: + allow_as_config_r4 = { + "r4": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "allowas-in": { + "number_occurences": 1 + } + } + } + } + } + } + } + } + } + ] + } + } + + step( + "Configuring allow-as for {} address-family on router R4 ".format(addr_type) + ) + result = create_router_bgp(tgen, topo, allow_as_config_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + # now modify the as in r4 and reconfig bgp in r3 with new remote as. + topo1 = deepcopy(topo) + topo1["routers"]["r4"]["bgp"]["local_as"] = "100" + + delete_bgp = {"r3": {"bgp": {"delete": True}}} + result = create_router_bgp(tgen, topo1, delete_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + build_config_from_json(tgen, topo1, save_bkup=False) + + step("Configure local-as at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "local_asn": {"remote_as": "110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "100", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "local_asn": {"remote_as": "110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 110 200 100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R2") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_rep_as_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_port_reset_p0(request): + """ + Verify that BGP Local AS functionality by performing shut/ noshut on the interfaces in between BGP neighbors. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100)." + ) + step("Verify that Static routes are redistributed in BGP process") + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step("Api call to modfiy BGP timers at R3") + for addr_type in ADDR_TYPES: + input_dict_r3_timers = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_timers) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 110 200 100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify advertised routes at R3 towards R4") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for count in range(1, 1): + step("Iteration {}".format(count)) + step("Shut down connecting interface between R3<<>>R4 on R3.") + + intf1 = topo["routers"]["r3"]["links"]["r4"]["interface"] + + interfaces = [intf1] + for intf in interfaces: + shutdown_bringup_interface(tgen, "r3", intf, False) + + step( + "On R3, all BGP peering in respective vrf instances go down" + " when the interface is shut" + ) + + result = verify_bgp_convergence(tgen, topo, expected=False) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: BGP will not be converged \n " + "Error {}".format(tc_name, result) + ) + + step("BGP neighborship is verified after restart of r3") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verify that AS-110 is got added in the AS list 110 200 100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_negative2_p0(request): + """ + Verify the BGP Local AS functionality with different AS configurations. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 110 200 100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that AS-110 is not prepended in the AS list 110 200 100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + step("Verify that AS-300 is replaced with AS-110 at R3 router.") + dut = "r4" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # configure negative scenarios + step("Configure local-as at R3 towards R4.") + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "300"}} + } + } + } + } + } + }, + } + } + } + if "bgp" in topo["routers"]["r3"].keys(): + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: Cannot have local-as same as BGP AS number \n " + "Error {}".format(tc_name, result) + ) + + step("Configure another local-as at R3 towards R4.") + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "110", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + } + } + if "bgp" in topo["routers"]["r3"].keys(): + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: Cannot have local-as same as BGP AS number \n " + "Error {}".format(tc_name, result) + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_negative3_p0(request): + """ + Verify the BGP Local AS functionality with R3& R4 with different AS configurations. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure basic BGP Peerings between R1,R2,R3 and R4") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 110 200 100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Perform Negative scenarios + step("Configure another local-as at R3 towards R4.") + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "300"}} + } + } + } + } + } + }, + } + } + } + if "bgp" in topo["routers"]["r3"].keys(): + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: Cannot have local-as same as BGP AS number \n " + "Error {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_restart_daemons_p0(request): + """ + Verify that BGP Local AS functionality by restarting BGP,Zebra and FRR services and + further restarting clear BGP * and shutdown BGP neighbor. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100)." + ) + step("Verify that Static routes are redistributed in BGP process") + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 110 200 100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Kill BGPd daemon on R3.") + kill_router_daemons(tgen, "r3", ["bgpd"]) + + step("Bring up BGPd daemon on R3.") + start_router_daemons(tgen, "r3", ["bgpd"]) + + step( + "Verify that AS-110 is got added in the AS list 110 200 100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify advertised routes at R3 towards R4") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verify that AS-110 is not prepended in the AS list 200 100 by following " + "commands at R3 router." + ) + dut = "r3" + aspath = "200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Kill BGPd daemon on R3.") + kill_router_daemons(tgen, "r3", ["bgpd"]) + + step("Bring up BGPd daemon on R3.") + start_router_daemons(tgen, "r3", ["bgpd"]) + + step( + "Verify that AS-110 is not prepended in the AS list 200 100 by following " + "commands at R3 router." + ) + dut = "r3" + aspath = "200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verified that AS-300 is got replaced with original AS-110 at R4 by following commands" + ) + dut = "r4" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verified that AS-300 is got replaced with original AS-110 at R4 by following commands" + ) + dut = "r4" + aspath = "110 200 100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_asn/test_bgp_local_asn_topo2.py b/tests/topotests/bgp_local_asn/test_bgp_local_asn_topo2.py new file mode 100644 index 0000000..0c10c64 --- /dev/null +++ b/tests/topotests/bgp_local_asn/test_bgp_local_asn_topo2.py @@ -0,0 +1,686 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +########################################################################################################## +# +# Testcases +# +########################################################################################################## +########################################################################################################## +# +# 1.10.1.2. Verify the BGP Local AS functionality by configuring 4 Byte AS in between eBGP Peers. +# +# 1.10.1.4. Verify the BGP Local AS functionality by configuring Old AS(local as) in 2 bytes and New AS in 4 bytes in between eBGP Peers. +# +############################################################################################################### + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + create_static_routes, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + check_address_types, + check_router_status, + create_static_routes, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + verify_bgp_rib, + create_router_bgp, + verify_bgp_advertised_routes_from_neighbor, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_local_asn_topo2.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +########################################################################################################## +# +# Testcases +# +########################################################################################################## + + +def test_verify_bgp_local_as_in_4_Byte_AS_EBGP_p0(request): + """ + Verify the BGP Local AS functionality by configuring 4 Byte AS in between eBGP Peers. + """ + + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "12000300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "12000110" + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip( + ["r2", "r4"], ["12000200", "12000400"], ["r3", "r3"] + ): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: { + "local_asn": { + "remote_as": "12000110" + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step( + "Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-12000100)." + ) + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-12000100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r2", "r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-12000110 is got added in the AS list 12000110 12000200 12000100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "12000110 12000200 12000100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "12000300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "12000110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r3" + aspath = "12000200 12000100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "12000300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "12000110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "12000110 12000200 12000100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_old_AS2_new_AS4_EBGP_p0(request): + """ + Verify the BGP Local AS functionality by configuring Old AS(local as) in + 2 bytes and New AS in 4 bytes in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "12000300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip( + ["r2", "r4"], ["12000200", "12000400"], ["r3", "r3"] + ): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step( + "Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-12000100)." + ) + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-12000100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 110 12000200 12000100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "110 12000200 12000100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "12000300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r3" + aspath = "12000200 12000100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "12000300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "110 12000200 12000100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_asn/test_bgp_local_asn_vrf_topo1.py b/tests/topotests/bgp_local_asn/test_bgp_local_asn_vrf_topo1.py new file mode 100644 index 0000000..ea6ab59 --- /dev/null +++ b/tests/topotests/bgp_local_asn/test_bgp_local_asn_vrf_topo1.py @@ -0,0 +1,1095 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +1. Verify the BGP Local AS functionality by adding new AS when dynamically import routes + from default vrf to non-default vrf with route map by adding AS by as-prepend command. +2. Verify the BGP Local AS functionality by adding new AS when dynamically import routes + from non-default vrf to default vrf and further advertised to eBGP peers. +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + create_static_routes, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + check_address_types, + check_router_status, + create_static_routes, + verify_fib_routes, + create_route_maps, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + verify_bgp_rib, + create_router_bgp, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_local_asn_vrf_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +################################################################################################ +# +# Testcases +# +############################################################################################### + + +def test_verify_local_asn_ipv4_import_from_default_to_non_default_VRF_p0(request): + """ + Verify the BGP Local AS functionality by adding new AS when dynamically import routes + from default vrf to non-default vrf with route map by adding AS by as-prepend command. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + + # configure static routes + step("Advertise prefix 10.0.0.1/32 from Router-1(AS-100).") + step("Advertise an ipv6 prefix 10::1/128 from Router-1(AS-100).") + dut = "r2" + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r2": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R2") + input_dict_static_route_redist = { + "r2": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure import vrf BLUE on R3 under IPv4 Address Family") + input_import_vrf_ipv4 = { + "r3": { + "bgp": [ + { + "local_as": 300, + "vrf": "BLUE", + "address_family": { + "ipv4": {"unicast": {"import": {"vrf": "default"}}}, + "ipv6": {"unicast": {"import": {"vrf": "default"}}}, + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_import_vrf_ipv4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify IPv4 and IPv6 static routes received on R2") + for addr_type in ADDR_TYPES: + input_dict_static_route = { + "r2": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + result = verify_rib(tgen, addr_type, "r2", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r2", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r2", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "vrf": "BLUE", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "local_asn": {"remote_as": "110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "400", + "vrf": "BLUE", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "local_asn": {"remote_as": "110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 VRF BLUE & R4 VRF BLUE") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + } + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 110 200 100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "110 200" + for addr_type in ADDR_TYPES: + input_static_r2 = { + "r2": {"static_routes": [{"network": NETWORK[addr_type], "vrf": "BLUE"}]} + } + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure a route-map on R3 to prepend AS 2 times.") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r3": { + "route_maps": { + "ASP_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "path": {"as_num": "1000 1000", "as_action": "prepend"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure route map in out direction on R4") + # Configure neighbor for route map + input_dict_7 = { + "r3": { + "bgp": { + "local_as": "300", + "vrf": "BLUE", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "ASP_{}".format( + addr_type + ), + "direction": "out", + } + ] + } + } + } + } + } + } + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "vrf": "BLUE", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "200" + for addr_type in ADDR_TYPES: + input_static_r2 = { + "r2": {"static_routes": [{"network": NETWORK[addr_type], "vrf": "BLUE"}]} + } + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R2") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "vrf": "BLUE", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "110 1000 1000 200" + for addr_type in ADDR_TYPES: + input_static_r2 = { + "r2": {"static_routes": [{"network": NETWORK[addr_type], "vrf": "BLUE"}]} + } + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_local_asn_ipv4_import_from_non_default_to_default_VRF_p0(request): + """ + Verify the BGP Local AS functionality by adding new AS when dynamically import routes + from non-default vrf to default vrf and further advertised to eBGP peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Resetting the config from JSON") + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + # configure static routes + step("Advertise prefix 10.0.0.1/32 from Router-1(AS-100).") + step("Advertise an ipv6 prefix 10::1/128 from Router-1(AS-100).") + dut = "r4" + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r4": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R4") + input_dict_static_route_redist = { + "r4": { + "bgp": { + "local_as": 400, + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure import from BLUE vrf to default vrf on R3 under IPv4 Address Family" + ) + input_import_vrf_ipv4 = { + "r3": { + "bgp": [ + { + "local_as": 300, + "vrf": "default", + "address_family": { + "ipv4": {"unicast": {"import": {"vrf": "BLUE"}}}, + "ipv6": {"unicast": {"import": {"vrf": "BLUE"}}}, + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_import_vrf_ipv4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify IPv4 and IPv6 static routes received on R3 VRF BLUE & R4 VRF BLUE") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r4": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + } + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "vrf": "BLUE", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "local_asn": {"remote_as": "110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "400", + "vrf": "BLUE", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "local_asn": {"remote_as": "110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R2") + for addr_type in ADDR_TYPES: + input_dict_static_route_from_r4 = { + "r4": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + result = verify_rib(tgen, addr_type, "r2", input_dict_static_route_from_r4) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r2", input_dict_static_route_from_r4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes( + tgen, addr_type, "r2", input_dict_static_route_from_r4 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 110 400 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "110 400" + for addr_type in ADDR_TYPES: + input_static_r2 = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "vrf": "BLUE", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + dut = "r3" + aspath = "400" + for addr_type in ADDR_TYPES: + input_static_r2 = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R2") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "300", + "vrf": "BLUE", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r2" + aspath = "110 400" + for addr_type in ADDR_TYPES: + input_static_r2 = { + "r4": { + "static_routes": [ + { + "network": NETWORK[addr_type], + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_asn/test_bgp_local_asn_vrf_topo2.py b/tests/topotests/bgp_local_asn/test_bgp_local_asn_vrf_topo2.py new file mode 100644 index 0000000..b1204bf --- /dev/null +++ b/tests/topotests/bgp_local_asn/test_bgp_local_asn_vrf_topo2.py @@ -0,0 +1,813 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +1. Verify the BGP Local AS functionality by adding new AS when leaking routes + from non-default VRF to non-default with route map by prefix lists. +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + create_static_routes, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + check_address_types, + check_router_status, + create_static_routes, + create_prefix_lists, + verify_fib_routes, + create_route_maps, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + verify_bgp_rib, + create_router_bgp, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_local_asn_vrf_topo2.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +################################################################################################ +# +# Testcases +# +############################################################################################### + + +def test_verify_local_asn_ipv4_import_from_non_default_to_non_default_VRF_p0(request): + """ + Verify the BGP Local AS functionality by adding new AS when leaking routes + from non-default VRF to non-default with route map by prefix lists. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + + # configure static routes + step("Advertise prefix 10.1.1.0/32 from Router-1(AS-100).") + step("Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100).") + dut = "r2" + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R2") + input_dict_static_route_redist = { + "r2": { + "bgp": { + "local_as": 200, + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure import vrf BLUE from vrf RED on R3 under IPv4 Address Family") + input_import_vrf_ipv4 = { + "r3": { + "bgp": [ + { + "local_as": 300, + "vrf": "BLUE", + "address_family": { + "ipv4": {"unicast": {"import": {"vrf": "RED"}}}, + "ipv6": {"unicast": {"import": {"vrf": "RED"}}}, + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_import_vrf_ipv4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify IPv4 and IPv6 static routes received on R3 VRF BLUE & R4 VRF BLUE") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + } + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r2 = { + "r3": { + "vrfs": [{"name": "RED", "id": "1"}], + "bgp": [ + { + "local_as": "300", + "vrf": "RED", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + ], + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "vrfs": [{"name": "BLUE", "id": "1"}], + "bgp": [ + { + "local_as": "300", + "vrf": "BLUE", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "110"}} + } + } + } + } + } + }, + } + ], + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "vrfs": [{"name": "RED", "id": "1"}], + "bgp": [ + { + "local_as": "200", + "vrf": "RED", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "local_asn": {"remote_as": "110"} + } + } + } + } + } + } + }, + } + ], + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "vrfs": [{"name": "BLUE", "id": "1"}], + "bgp": [ + { + "local_as": "400", + "vrf": "BLUE", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "local_asn": {"remote_as": "110"} + } + } + } + } + } + } + }, + } + ], + } + } + result = create_router_bgp(tgen, topo, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 VRF BLUE & R4 VRF BLUE") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + } + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 110 200 100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "110 200" + for addr_type in ADDR_TYPES: + input_static_r2 = { + "r2": {"static_routes": [{"network": NETWORK[addr_type], "vrf": "BLUE"}]} + } + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # configure the prefix-list and route-maps. + for adt in ADDR_TYPES: + # Create Static routes + input_dict_rm1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[adt], + "no_of_ip": 1, + "next_hop": NEXT_HOP_IP[adt], + "vrf": "RED", + } + ] + } + } + + result = create_static_routes(tgen, input_dict_rm1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_rm_rd = { + "r2": { + "bgp": { + "local_as": 200, + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_rm_rd) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_rm_pl = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + { + "seqid": 10, + "action": "permit", + "network": NETWORK["ipv4"], + } + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + { + "seqid": 100, + "action": "permit", + "network": NETWORK["ipv6"], + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_rm_pl) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_rm_r3 = { + "r3": { + "route_maps": { + "rmap_match_tag_1_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + } + ], + "rmap_match_tag_2_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_2_{}".format(addr_type) + } + }, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_rm_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_conf_neighbor_rm = { + "r3": { + "bgp": [ + { + "local_as": "300", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv4", + "direction": "out", + }, + { + "name": "rmap_match_tag_1_ipv4", + "direction": "out", + }, + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv6", + "direction": "in", + }, + { + "name": "rmap_match_tag_1_ipv6", + "direction": "out", + }, + ] + } + } + } + } + } + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_conf_neighbor_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for adt in ADDR_TYPES: + # Verifying RIB routes + dut = "r3" + input_dict_r2_rib = { + "r2": { + "static_routes": [ + { + "network": [NETWORK[adt]], + "no_of_ip": 1, + "next_hop": NEXT_HOP_IP[adt], + "vrf": "RED", + } + ] + } + } + result = verify_rib(tgen, adt, dut, input_dict_r2_rib) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying RIB routes + dut = "r4" + input_dict_r2_rib = { + "r2": { + "static_routes": [ + { + "network": [NETWORK[adt]], + "no_of_ip": 1, + "next_hop": NEXT_HOP_IP[adt], + "vrf": "BLUE", + } + ] + } + } + result = verify_rib(tgen, adt, dut, input_dict_r2_rib) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r2 = { + "r3": { + "vrfs": [{"name": "RED", "id": "1"}], + "bgp": [ + { + "local_as": "300", + "vrf": "RED", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ], + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "vrfs": [{"name": "BLUE", "id": "1"}], + "bgp": [ + { + "local_as": "300", + "vrf": "BLUE", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ], + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "200" + for addr_type in ADDR_TYPES: + input_static_r2 = { + "r2": {"static_routes": [{"network": NETWORK[addr_type], "vrf": "BLUE"}]} + } + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R2") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r2 = { + "r3": { + "vrfs": [{"name": "RED", "id": "1"}], + "bgp": [ + { + "local_as": "300", + "vrf": "RED", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ], + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "vrfs": [{"name": "BLUE", "id": "1"}], + "bgp": [ + { + "local_as": "300", + "vrf": "BLUE", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ], + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "110 200" + for addr_type in ADDR_TYPES: + input_static_r2 = { + "r2": {"static_routes": [{"network": NETWORK[addr_type], "vrf": "BLUE"}]} + } + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_agg.json b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_agg.json new file mode 100644 index 0000000..b481932 --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_agg.json @@ -0,0 +1,147 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r1": {}}} + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r1": {}}} + } + } + } + } + }, + "static_routes":[ + { + "network":"10.1.1.0/32", + "next_hop":"Null0" + }, + { + "network":"10:1::1:0/128", + "next_hop":"Null0" + }] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.200", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r2": {}}} + } + } + } + } + }, + "static_routes":[ + { + "network":"10.1.2.0/32", + "next_hop":"Null0" + }, + { + "network":"10:1::2:0/128", + "next_hop":"Null0" + }] + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {}}}, + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {}}}, + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json new file mode 100644 index 0000000..afacab4 --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json @@ -0,0 +1,317 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "1.100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + } + + ], + "static_routes":[ + { + "network":"10.0.0.1/32", + "next_hop":"Null0" + }, + { + "network":"10::1/128", + "next_hop":"Null0" + } + ] + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "1.200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + } + ] + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link8": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "1.300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "r4": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {}, + "r3-link8": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "r4": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {}, + "r3-link8": {} + } + } + } + } + } + } + } + ] + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link8": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "1.400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {}, + "r4-link5": {}, + "r4-link6": {}, + "r4-link7": {}, + "r4-link8": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {}, + "r4-link5": {}, + "r4-link6": {}, + "r4-link7": {}, + "r4-link8": {} + } + } + } + } + } + } + } + ] + } + } +} diff --git a/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_topo1.json b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_topo1.json new file mode 100644 index 0000000..02aacf7 --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_topo1.json @@ -0,0 +1,132 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": {"dest_link": {"r1": {}}} + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": {"dest_link": {"r1": {}}} + } + } + } + } + }, + "static_routes":[ + { + "network":"10.1.1.0/32", + "next_hop":"Null0" + }, + { + "network":"10:1::1:0/128", + "next_hop":"Null0" + }] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + } + } + } + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_agg.py b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_agg.py new file mode 100644 index 0000000..cb8fa1e --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_agg.py @@ -0,0 +1,420 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: +1. Verify the BGP Local AS functionality by aggregating routes in between eBGP Peers. +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + check_address_types, + check_router_status +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + verify_bgp_rib, + create_router_bgp, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK_1_1 = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"} +NETWORK_1_2 = {"ipv4": "10.1.2.0/32", "ipv6": "10:1::2:0/128"} +AGGREGATE_NW = {"ipv4": "10.1.0.0/16", "ipv6": "10:1::/96"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_local_asn_dot_agg.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +#################################################################################################################### +# +# Testcases +# +#################################################################################################################### + + +def test_verify_bgp_local_as_agg_in_EBGP_p0(request): + """ + Verify the BGP Local AS functionality by aggregating routes in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Done in base config: Advertise prefix 10.1.1.0/24 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/120 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK_1_1[addr_type]}]} + } + + input_static_verify_r2 = { + "r2": {"static_routes": [{"network": NETWORK_1_2[addr_type]}]} + } + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", input_static_verify_r2) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure aggregate-address to summarise all the advertised routes.") + for addr_type in ADDR_TYPES: + route_aggregate = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "aggregate_address": [ + { + "network": AGGREGATE_NW[addr_type], + "summary": True, + "as_set": True, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, route_aggregate) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that we see a summarised route on advertising router R3 " + "and receiving router R4 for both AFIs" + ) + + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + input_static_r1 = { + "r1": {"static_routes": [{"network": [NETWORK_1_1[addr_type]]}]} + } + + input_static_r2 = { + "r2": {"static_routes": [{"network": [NETWORK_1_2[addr_type]]}]} + } + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_agg_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1", "r2"], [input_static_r1, input_static_r2]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 1.110 {1.100,1.110,1.200} by following " + "commands at R3 router." + ) + dut = "r3" + aspath = "{1.100,1.110,1.200}" + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + result = verify_bgp_rib( + tgen, addr_type, dut, input_static_agg_r1, aspath=aspath + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "{1.100,1.200}" + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + result = verify_bgp_rib( + tgen, addr_type, dut, input_static_agg_r1, aspath=aspath + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 {1.100,1.200}" + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + result = verify_bgp_rib( + tgen, addr_type, dut, input_static_agg_r1, aspath=aspath + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_ecmp.py b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_ecmp.py new file mode 100644 index 0000000..6937a61 --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_ecmp.py @@ -0,0 +1,524 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# +# +########################################################################################################################################## +# +# Testcases +# +########################################################################################################################################### +########################################################################################################################################### +# +# 1.10.1.7. Verify the BGP Local AS functionality with ECMP on 8 links by adding no-prepend and replace-as command in between eBGP Peers. +# +################################################################################################################################################# + +import os +import sys +import time +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + create_static_routes, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + check_address_types, + check_router_status, + create_static_routes, + verify_fib_routes, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + verify_bgp_rib, + create_router_bgp, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +BGP_CONVERGENCE = False +NETWORK = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_local_asn_dot_ecmp.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +########################################################################################################################################## +# +# Testcases +# +########################################################################################################################################### + + +def test_verify_bgp_local_as_in_ecmp_EBGP_p0(request): + """ + Verify the BGP Local AS functionality with ECMP on 8 links by + adding no-prepend and replace-as command in between eBGP Peers. + """ + + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + dut = "r1" + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "local_asn": {"local_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + dest_link = {} + for link_no in range(1, 9): + link = "r3-link" + str(link_no) + dest_link[link] = {"local_asn": {"local_as": "1.110"}} + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r4": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + dest_link = {} + for link_no in range(1, 9): + link = "r4-link" + str(link_no) + dest_link[link] = {"local_asn": {"remote_as": "1.110"}} + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "1.400", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r3": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 1.110 1.200 1.100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + dest_link = {} + for link_no in range(1, 9): + link = "r3-link" + str(link_no) + dest_link[link] = {"local_asn": {"local_as": "1.110"}} + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r4": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R2") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + dest_link = {} + for link_no in range(1, 9): + link = "r3-link" + str(link_no) + dest_link[link] = { + "local_asn": {"local_as": "1.110", "no_prepend": True, "replace_as": True} + } + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r4": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_topo1.py b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_topo1.py new file mode 100644 index 0000000..e9234f5 --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_topo1.py @@ -0,0 +1,3655 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +########################################################################################################## +# +# Functionality Testcases +# +########################################################################################################## +""" +1. Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between eBGP Peers. +2. Verify the BGP Local AS functionality by configuring 4 Byte AS at R3 and 2 Byte AS at R2 & R4 in between eBGP Peers. +3. Verify that BGP Local AS functionality by performing graceful restart in between eBGP Peers. +4. Verify the BGP Local AS functionality by adding another AS & by same AS with AS-Prepend command in between eBGP Peers. +4. Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between iBGP Peers. +5. Verify the BGP Local AS functionality with allowas-in in between iBGP Peers. +6. Verify that BGP Local AS functionality by performing shut/ noshut on the interfaces in between BGP neighbors. +7. Verify that BGP Local AS functionality by restarting BGP,Zebra and FRR services and + further restarting clear BGP * and shutdown BGP neighbor. +8. Verify the BGP Local AS functionality with different AS configurations. +9. Verify the BGP Local AS functionality with R3& R4 with different AS configurations. +""" + +import os +import sys +import time +import pytest +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + create_static_routes, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + get_frr_ipv6_linklocal, + check_address_types, + check_router_status, + create_static_routes, + verify_fib_routes, + create_route_maps, + kill_router_daemons, + start_router_daemons, + shutdown_bringup_interface, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + clear_bgp_and_verify, + verify_bgp_rib, + modify_as_number, + create_router_bgp, + verify_bgp_advertised_routes_from_neighbor, + verify_graceful_restart, + verify_r_bit, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} +NEXT_HOP_IP_GR = {"ipv4": "10.0.0.5", "ipv6": "fd00:0:0:1::2/64"} +NEXT_HOP_IP_1 = {"ipv4": "10.0.0.101", "ipv6": "fd00::1"} +NEXT_HOP_IP_2 = {"ipv4": "10.0.0.102", "ipv6": "fd00::2"} + +BGP_CONVERGENCE = False +PREFERRED_NEXT_HOP = "link_local" +KEEPALIVETIMER = 1 +HOLDDOWNTIMER = 3 + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_local_asn_dot_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +########################################################################################################## +# +# Local APIs +# +########################################################################################################## + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + This function groups the repetitive function calls into one function. + """ + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + return True + + +def next_hop_per_address_family( + tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP +): + """ + This function returns link_local or global next_hop per address-family + """ + intferface = topo["routers"][peer]["links"]["{}".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in preferred_next_hop: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +########################################################################################################## +# +# Testcases +# +########################################################################################################## + + +def test_verify_bgp_local_as_in_EBGP_p0(request): + """ + Verify the BGP Local AS functionality by adding no-prepend and + replace-as command in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_4B_AS_mid_4B_AS_p0(request): + """ + Verify the BGP Local AS functionality by configuring 4 Byte AS + at R3 and 4 Byte AS at R2 & R4 in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "183.2926" + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: { + "local_asn": { + "remote_as": "183.2926" + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-183.2926 is got added in the AS list 183.2926 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "183.2926 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "183.2926", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "183.2926", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "183.2926 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_GR_EBGP_p0(request): + """ + Verify that BGP Local AS functionality by performing graceful restart in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure basic BGP Peerings between R1,R2,R3 and R4") + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP_GR[addr_type], + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "1.400", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP_GR[addr_type], + } + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + """ + GR Steps : Helper BGP router R2, mark and unmark IPV4 routes + as stale as the restarting router R3 come up within the restart time + """ + # Create route-map to prefer global next-hop + input_dict = { + "r2": { + "route_maps": { + "rmap_global": [ + {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}} + ] + } + }, + "r3": { + "route_maps": { + "rmap_global": [ + {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}} + ] + } + }, + } + result = create_route_maps(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_neigh_rm = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_neigh_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure graceful-restart + input_dict = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "graceful-restart-helper": True, + "local_asn": {"remote_as": "1.110"}, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "graceful-restart-helper": True, + "local_asn": {"remote_as": "1.110"}, + } + } + } + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {"graceful-restart": True}}} + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r3", peer="r2") + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r3", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r2" + peer = "r3" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + input_topo = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, "bgp") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info("[Phase 2] : R3 goes for reload ") + + kill_router_daemons(tgen, "r3", ["bgpd"]) + + logger.info( + "[Phase 3] : R3 is still down, restart time 120 sec." + " So time verify the routes are present in BGP RIB" + " and ZEBRA" + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info("[Phase 5] : R3 is about to come up now ") + start_router_daemons(tgen, "r3", ["bgpd"]) + + logger.info("[Phase 5] : R3 is UP Now ! ") + + for addr_type in ADDR_TYPES: + result = verify_bgp_convergence(tgen, topo) + assert ( + result is True + ), "BGP Convergence after BGPd restart" " :Failed \n Error:{}".format(result) + + # Verifying GR stats + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r3", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r2", peer="r3") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Configure local-as with no-prepend at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R2") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_aspath_p0(request): + """ + Verify the BGP Local AS functionality by adding another AS & by same AS with AS-Prepend command in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure basic BGP Peerings between R1,R2,R3 and R4") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure a route-map on R3 to prepend AS 2 times.") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r3": { + "route_maps": { + "ASP_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "path": {"as_num": "1.1000 1.1000", "as_action": "prepend"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure route map in out direction on R4") + # Configure neighbor for route map + input_dict_7 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "ASP_{}".format( + addr_type + ), + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verify that AS-1.300 is got replaced with 1.200 in the AS list 1.110 1.1000 1.1000 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r4" + aspath = "1.110 1.1000 1.1000 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_iBGP_p0(request): + """ + Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between iBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Modify AS Number for R3") + input_dict_modify_as_number = {"r3": {"bgp": {"local_as": "1.200"}}} + result = modify_as_number(tgen, topo, input_dict_modify_as_number) + + step("Base config is done as part of JSON") + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "1.400", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "next_hop_self": True, + "local_asn": { + "remote_as": "1.200", + }, + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_allow_as_in_iBGP_p0(request): + """ + Verify the BGP Local AS functionality with allowas-in in between iBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Modidy AS Number for R4") + input_dict_modify_as_number = {"r4": {"bgp": {"local_as": "1.100"}}} + result = modify_as_number(tgen, topo, input_dict_modify_as_number) + + step("Base config is done as part of JSON") + dut = "r1" + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure allow-as at R4") + for addr_type in ADDR_TYPES: + allow_as_config_r4 = { + "r4": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "allowas-in": { + "number_occurences": 1 + } + } + } + } + } + } + } + } + } + ] + } + } + + step( + "Configuring allow-as for {} address-family on router R4 ".format(addr_type) + ) + result = create_router_bgp(tgen, topo, allow_as_config_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + # now modify the as in r4 and reconfig bgp in r3 with new remote as. + topo1 = deepcopy(topo) + topo1["routers"]["r4"]["bgp"]["local_as"] = "1.100" + + delete_bgp = {"r3": {"bgp": {"delete": True}}} + result = create_router_bgp(tgen, topo1, delete_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + build_config_from_json(tgen, topo1, save_bkup=False) + + step("Configure local-as at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "1.100", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R2") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_rep_as_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_port_reset_p0(request): + """ + Verify that BGP Local AS functionality by performing shut/ noshut on the interfaces in between BGP neighbors. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step("Api call to modfiy BGP timers at R3") + for addr_type in ADDR_TYPES: + input_dict_r3_timers = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_timers) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify advertised routes at R3 towards R4") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for count in range(1, 1): + step("Iteration {}".format(count)) + step("Shut down connecting interface between R3<<>>R4 on R3.") + + intf1 = topo["routers"]["r3"]["links"]["r4"]["interface"] + + interfaces = [intf1] + for intf in interfaces: + shutdown_bringup_interface(tgen, "r3", intf, False) + + step( + "On R3, all BGP peering in respective vrf instances go down" + " when the interface is shut" + ) + + result = verify_bgp_convergence(tgen, topo, expected=False) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: BGP will not be converged \n " + "Error {}".format(tc_name, result) + ) + + step("BGP neighborship is verified after restart of r3") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_negative2_p0(request): + """ + Verify the BGP Local AS functionality with different AS configurations. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that AS-1.110 is not prepended in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + step("Verify that AS-1.300 is replaced with AS-1.110 at R3 router.") + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # configure negative scenarios + step("Configure local-as at R3 towards R4.") + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.300"}} + } + } + } + } + } + }, + } + } + } + if "bgp" in topo["routers"]["r3"].keys(): + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: Cannot have local-as same as BGP AS number \n " + "Error {}".format(tc_name, result) + ) + + step("Configure another local-as at R3 towards R4.") + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.110", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + if "bgp" in topo["routers"]["r3"].keys(): + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: Cannot have local-as same as BGP AS number \n " + "Error {}".format(tc_name, result) + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_negative3_p0(request): + """ + Verify the BGP Local AS functionality with R3& R4 with different AS configurations. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure basic BGP Peerings between R1,R2,R3 and R4") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Perform Negative scenarios + step("Configure another local-as at R3 towards R4.") + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.300"}} + } + } + } + } + } + }, + } + } + } + if "bgp" in topo["routers"]["r3"].keys(): + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: Cannot have local-as same as BGP AS number \n " + "Error {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_restart_daemons_p0(request): + """ + Verify that BGP Local AS functionality by restarting BGP,Zebra and FRR services and + further restarting clear BGP * and shutdown BGP neighbor. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Kill BGPd daemon on R3.") + kill_router_daemons(tgen, "r3", ["bgpd"]) + + step("Bring up BGPd daemon on R3.") + start_router_daemons(tgen, "r3", ["bgpd"]) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify advertised routes at R3 towards R4") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verify that AS-1.110 is not prepended in the AS list 1.200 1.100 by following " + "commands at R3 router." + ) + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Kill BGPd daemon on R3.") + kill_router_daemons(tgen, "r3", ["bgpd"]) + + step("Bring up BGPd daemon on R3.") + start_router_daemons(tgen, "r3", ["bgpd"]) + + step( + "Verify that AS-1.110 is not prepended in the AS list 1.200 1.100 by following " + "commands at R3 router." + ) + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verified that AS-1.300 is got replaced with original AS-1.110 at R4 by following commands" + ) + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verified that AS-1.300 is got replaced with original AS-1.110 at R4 by following commands" + ) + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf b/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf new file mode 100644 index 0000000..a31439c --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 65500 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + bgp labeled-unicast explicit-null + neighbor 192.0.2.2 remote-as 65501 +! + address-family ipv4 unicast + no neighbor 192.0.2.2 activate + network 192.168.2.1/32 + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 192.0.2.2 activate + exit-address-family +! diff --git a/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf b/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf new file mode 100644 index 0000000..b845748 --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf @@ -0,0 +1,6 @@ +interface r1-eth0 + ip address 192.0.2.1/24 +! +interface r1-eth1 + ip address 192.168.2.1/32 +! \ No newline at end of file diff --git a/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf b/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf new file mode 100644 index 0000000..41c2b9b --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 65501 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + bgp labeled-unicast explicit-null + neighbor 192.0.2.1 remote-as 65500 +! + address-family ipv4 unicast + no neighbor 192.0.2.1 activate + network 192.168.2.2/32 + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 192.0.2.1 activate + exit-address-family +! diff --git a/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf b/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf new file mode 100644 index 0000000..9a63961 --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf @@ -0,0 +1,6 @@ +interface r2-eth0 + ip address 192.0.2.2/24 +! +interface r2-eth1 + ip address 192.168.2.2/32 +! diff --git a/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py b/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py new file mode 100644 index 0000000..0656e1e --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# test_bgp_explicitnull.py +# +# Part of NetDEF Topology Tests +# +# Copyright 2023 by 6WIND S.A. +# + +""" +test_bgp_lu_explicitnull.py: Test BGP LU label allocation +""" + +import os +import sys +import json +import functools +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + + +pytestmark = [pytest.mark.bgpd] + + +# Basic scenario for BGP-LU. Nodes are directly connected. +# The 192.168.2.2/32 prefix is advertised from r2 to r1 +# The explicit-null label should be used +# The 192.168.2.1/32 prefix is advertised from r1 to r2 +# The explicit-null label should be used +# Traffic from 192.168.2.1 to 192.168.2.2 should use explicit-null label +# +# AS65500 BGP-LU AS65501 +# +-----+ +-----+ +# | |.1 .2| | +# | 1 +----------------+ 2 + 192.168.0.2/32 +# | | 192.0.2.0/24 | | +# +-----+ +-----+ + + +def build_topo(tgen): + "Build function" + + # Create routers + tgen.add_router("r1") + tgen.add_router("r2") + + # r1-r2 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # r1 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + + # r2 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + + # Skip if no mpls support + if not tgen.hasmpls: + logger.info("MPLS is not available, skipping test") + pytest.skip("MPLS is not available, skipping") + return + + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # Enable mpls input for routers, so we can ping + sval = "net.mpls.conf.{}.input" + topotest.sysctl_assure(router_list["r2"], sval.format("r2-eth0"), 1) + topotest.sysctl_assure(router_list["r1"], sval.format("r1-eth0"), 1) + + # For all registred routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def check_show_ip_label_prefix_found(router, ipversion, prefix, label): + output = json.loads( + router.vtysh_cmd("show {} route {} json".format(ipversion, prefix)) + ) + expected = { + prefix: [ + {"prefix": prefix, "nexthops": [{"fib": True, "labels": [int(label)]}]} + ] + } + return topotest.json_cmp(output, expected) + + +def test_converge_bgplu(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # tgen.mininet_cli(); + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + # Check r1 gets prefix 192.168.2.2/32 + test_func = functools.partial( + check_show_ip_label_prefix_found, + tgen.gears["r1"], + "ip", + "192.168.2.2/32", + "0", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, prefix 192.168.2.2/32 from r2 not present" + + # Check r2 gets prefix 192.168.2.1/32 + test_func = functools.partial( + check_show_ip_label_prefix_found, + tgen.gears["r2"], + "ip", + "192.168.2.1/32", + "0", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, prefix 192.168.2.1/32 from r1 not present" + + +def test_traffic_connectivity(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _check_ping(name, dest_addr, src_addr): + tgen = get_topogen() + output = tgen.gears[name].run( + "ping {} -c 1 -w 1 -I {}".format(dest_addr, src_addr) + ) + logger.info(output) + if " 0% packet loss" not in output: + return True + + logger.info("r1, check ping 192.168.2.2 from 192.168.2.1 is OK") + tgen = get_topogen() + func = functools.partial(_check_ping, "r1", "192.168.2.2", "192.168.2.1") + # tgen.mininet_cli() + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "r1, ping to 192.168.2.2 from 192.168.2.1 fails" + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_lu_topo1/R1/bgpd.conf b/tests/topotests/bgp_lu_topo1/R1/bgpd.conf new file mode 100644 index 0000000..a164152 --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R1/bgpd.conf @@ -0,0 +1,21 @@ +! +! debug bgp labelpool +! debug bgp zebra +! +router bgp 1 + bgp router-id 10.0.0.1 + timers bgp 3 9 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.0.2 remote-as 2 + neighbor 10.0.0.2 solo + neighbor 10.0.0.2 timers connect 10 +! + address-family ipv4 unicast + no neighbor 10.0.0.2 activate + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 10.0.0.2 activate + exit-address-family +! diff --git a/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json b/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json new file mode 100644 index 0000000..c66571f --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json @@ -0,0 +1,6 @@ +{ + "ledger":506, + "inUse":506, + "requests":0, + "labelChunks":3 +} diff --git a/tests/topotests/bgp_lu_topo1/R1/zebra.conf b/tests/topotests/bgp_lu_topo1/R1/zebra.conf new file mode 100644 index 0000000..f8a9ce4 --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R1/zebra.conf @@ -0,0 +1,6 @@ +! debug zebra events +! debug zebra dplane +! debug zebra mpls +! +interface R1-eth0 + ip address 10.0.0.1/24 diff --git a/tests/topotests/bgp_lu_topo1/R2/bgpd.conf b/tests/topotests/bgp_lu_topo1/R2/bgpd.conf new file mode 100644 index 0000000..d35bbb8 --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R2/bgpd.conf @@ -0,0 +1,23 @@ +! debug bgp labelpool +! debug bgp zebra +! +router bgp 2 + bgp router-id 10.0.0.2 + timers bgp 3 9 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.1.3 remote-as 2 + neighbor 10.0.1.3 update-source 10.0.1.2 + neighbor 10.0.1.3 timers connect 10 + neighbor 10.0.0.1 remote-as 1 + neighbor 10.0.0.1 timers connect 10 +! + address-family ipv4 unicast + neighbor 10.0.1.3 activate + no neighbor 10.0.0.1 activate + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 10.0.0.1 activate + exit-address-family +! diff --git a/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json new file mode 100644 index 0000000..d35e4ef --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json @@ -0,0 +1,6 @@ +{ + "ledger":0, + "inUse":0, + "requests":0, + "labelChunks":1 +} diff --git a/tests/topotests/bgp_lu_topo1/R2/zebra.conf b/tests/topotests/bgp_lu_topo1/R2/zebra.conf new file mode 100644 index 0000000..083da3e --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R2/zebra.conf @@ -0,0 +1,11 @@ +! +! debug zebra events +! debug zebra dplane +! debug zebra mpls +! +interface R2-eth0 + ip address 10.0.0.2/24 +! +interface R2-eth1 + ip address 10.0.1.2/24 +! \ No newline at end of file diff --git a/tests/topotests/bgp_lu_topo1/R3/bgpd.conf b/tests/topotests/bgp_lu_topo1/R3/bgpd.conf new file mode 100644 index 0000000..31d26ea --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R3/bgpd.conf @@ -0,0 +1,523 @@ +log file /tmp/bgpd.log +! +! debug bgp updates +! +router bgp 2 + bgp router-id 10.0.1.3 + timers bgp 3 9 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.1.2 remote-as 2 + neighbor 10.0.1.2 timers connect 10 + ! + address-family ipv4 unicast + neighbor 10.0.1.2 activate + network 11.0.0.1/32 + network 11.0.0.2/32 + network 11.0.0.3/32 + network 11.0.0.4/32 + network 11.0.0.5/32 + network 11.0.0.6/32 + network 11.0.0.7/32 + network 11.0.0.8/32 + network 11.0.0.9/32 + network 11.0.0.10/32 + network 11.0.0.11/32 + network 11.0.0.12/32 + network 11.0.0.13/32 + network 11.0.0.14/32 + network 11.0.0.15/32 + network 11.0.0.16/32 + network 11.0.0.17/32 + network 11.0.0.18/32 + network 11.0.0.19/32 + network 11.0.0.20/32 + network 11.0.0.21/32 + network 11.0.0.22/32 + network 11.0.0.23/32 + network 11.0.0.24/32 + network 11.0.0.25/32 + network 11.0.0.26/32 + network 11.0.0.27/32 + network 11.0.0.28/32 + network 11.0.0.29/32 + network 11.0.0.30/32 + network 11.0.0.31/32 + network 11.0.0.32/32 + network 11.0.0.33/32 + network 11.0.0.34/32 + network 11.0.0.35/32 + network 11.0.0.36/32 + network 11.0.0.37/32 + network 11.0.0.38/32 + network 11.0.0.39/32 + network 11.0.0.40/32 + network 11.0.0.41/32 + network 11.0.0.42/32 + network 11.0.0.43/32 + network 11.0.0.44/32 + network 11.0.0.45/32 + network 11.0.0.46/32 + network 11.0.0.47/32 + network 11.0.0.48/32 + network 11.0.0.49/32 + network 11.0.0.50/32 + network 11.0.0.51/32 + network 11.0.0.52/32 + network 11.0.0.53/32 + network 11.0.0.54/32 + network 11.0.0.55/32 + network 11.0.0.56/32 + network 11.0.0.57/32 + network 11.0.0.58/32 + network 11.0.0.59/32 + network 11.0.0.60/32 + network 11.0.0.61/32 + network 11.0.0.62/32 + network 11.0.0.63/32 + network 11.0.0.64/32 + network 11.0.0.65/32 + network 11.0.0.66/32 + network 11.0.0.67/32 + network 11.0.0.68/32 + network 11.0.0.69/32 + network 11.0.0.70/32 + network 11.0.0.71/32 + network 11.0.0.72/32 + network 11.0.0.73/32 + network 11.0.0.74/32 + network 11.0.0.75/32 + network 11.0.0.76/32 + network 11.0.0.77/32 + network 11.0.0.78/32 + network 11.0.0.79/32 + network 11.0.0.80/32 + network 11.0.0.81/32 + network 11.0.0.82/32 + network 11.0.0.83/32 + network 11.0.0.84/32 + network 11.0.0.85/32 + network 11.0.0.86/32 + network 11.0.0.87/32 + network 11.0.0.88/32 + network 11.0.0.89/32 + network 11.0.0.90/32 + network 11.0.0.91/32 + network 11.0.0.92/32 + network 11.0.0.93/32 + network 11.0.0.94/32 + network 11.0.0.95/32 + network 11.0.0.96/32 + network 11.0.0.97/32 + network 11.0.0.98/32 + network 11.0.0.99/32 + network 11.0.0.100/32 + network 11.0.0.101/32 + network 11.0.0.102/32 + network 11.0.0.103/32 + network 11.0.0.104/32 + network 11.0.0.105/32 + network 11.0.0.106/32 + network 11.0.0.107/32 + network 11.0.0.108/32 + network 11.0.0.109/32 + network 11.0.0.110/32 + network 11.0.0.111/32 + network 11.0.0.112/32 + network 11.0.0.113/32 + network 11.0.0.114/32 + network 11.0.0.115/32 + network 11.0.0.116/32 + network 11.0.0.117/32 + network 11.0.0.118/32 + network 11.0.0.119/32 + network 11.0.0.120/32 + network 11.0.0.121/32 + network 11.0.0.122/32 + network 11.0.0.123/32 + network 11.0.0.124/32 + network 11.0.0.125/32 + network 11.0.0.126/32 + network 11.0.0.127/32 + network 11.0.0.128/32 + network 11.0.0.129/32 + network 11.0.0.130/32 + network 11.0.0.131/32 + network 11.0.0.132/32 + network 11.0.0.133/32 + network 11.0.0.134/32 + network 11.0.0.135/32 + network 11.0.0.136/32 + network 11.0.0.137/32 + network 11.0.0.138/32 + network 11.0.0.139/32 + network 11.0.0.140/32 + network 11.0.0.141/32 + network 11.0.0.142/32 + network 11.0.0.143/32 + network 11.0.0.144/32 + network 11.0.0.145/32 + network 11.0.0.146/32 + network 11.0.0.147/32 + network 11.0.0.148/32 + network 11.0.0.149/32 + network 11.0.0.150/32 + network 11.0.0.151/32 + network 11.0.0.152/32 + network 11.0.0.153/32 + network 11.0.0.154/32 + network 11.0.0.155/32 + network 11.0.0.156/32 + network 11.0.0.157/32 + network 11.0.0.158/32 + network 11.0.0.159/32 + network 11.0.0.160/32 + network 11.0.0.161/32 + network 11.0.0.162/32 + network 11.0.0.163/32 + network 11.0.0.164/32 + network 11.0.0.165/32 + network 11.0.0.166/32 + network 11.0.0.167/32 + network 11.0.0.168/32 + network 11.0.0.169/32 + network 11.0.0.170/32 + network 11.0.0.171/32 + network 11.0.0.172/32 + network 11.0.0.173/32 + network 11.0.0.174/32 + network 11.0.0.175/32 + network 11.0.0.176/32 + network 11.0.0.177/32 + network 11.0.0.178/32 + network 11.0.0.179/32 + network 11.0.0.180/32 + network 11.0.0.181/32 + network 11.0.0.182/32 + network 11.0.0.183/32 + network 11.0.0.184/32 + network 11.0.0.185/32 + network 11.0.0.186/32 + network 11.0.0.187/32 + network 11.0.0.188/32 + network 11.0.0.189/32 + network 11.0.0.190/32 + network 11.0.0.191/32 + network 11.0.0.192/32 + network 11.0.0.193/32 + network 11.0.0.194/32 + network 11.0.0.195/32 + network 11.0.0.196/32 + network 11.0.0.197/32 + network 11.0.0.198/32 + network 11.0.0.199/32 + network 11.0.0.200/32 + network 11.0.0.201/32 + network 11.0.0.202/32 + network 11.0.0.203/32 + network 11.0.0.204/32 + network 11.0.0.205/32 + network 11.0.0.206/32 + network 11.0.0.207/32 + network 11.0.0.208/32 + network 11.0.0.209/32 + network 11.0.0.210/32 + network 11.0.0.211/32 + network 11.0.0.212/32 + network 11.0.0.213/32 + network 11.0.0.214/32 + network 11.0.0.215/32 + network 11.0.0.216/32 + network 11.0.0.217/32 + network 11.0.0.218/32 + network 11.0.0.219/32 + network 11.0.0.220/32 + network 11.0.0.221/32 + network 11.0.0.222/32 + network 11.0.0.223/32 + network 11.0.0.224/32 + network 11.0.0.225/32 + network 11.0.0.226/32 + network 11.0.0.227/32 + network 11.0.0.228/32 + network 11.0.0.229/32 + network 11.0.0.230/32 + network 11.0.0.231/32 + network 11.0.0.232/32 + network 11.0.0.233/32 + network 11.0.0.234/32 + network 11.0.0.235/32 + network 11.0.0.236/32 + network 11.0.0.237/32 + network 11.0.0.238/32 + network 11.0.0.239/32 + network 11.0.0.240/32 + network 11.0.0.241/32 + network 11.0.0.242/32 + network 11.0.0.243/32 + network 11.0.0.244/32 + network 11.0.0.245/32 + network 11.0.0.246/32 + network 11.0.0.247/32 + network 11.0.0.248/32 + network 11.0.0.249/32 + network 11.0.0.250/32 + network 11.0.0.251/32 + network 11.0.0.252/32 + network 11.0.0.253/32 + network 11.0.1.1/32 + network 11.0.1.2/32 + network 11.0.1.3/32 + network 11.0.1.4/32 + network 11.0.1.5/32 + network 11.0.1.6/32 + network 11.0.1.7/32 + network 11.0.1.8/32 + network 11.0.1.9/32 + network 11.0.1.10/32 + network 11.0.1.11/32 + network 11.0.1.12/32 + network 11.0.1.13/32 + network 11.0.1.14/32 + network 11.0.1.15/32 + network 11.0.1.16/32 + network 11.0.1.17/32 + network 11.0.1.18/32 + network 11.0.1.19/32 + network 11.0.1.20/32 + network 11.0.1.21/32 + network 11.0.1.22/32 + network 11.0.1.23/32 + network 11.0.1.24/32 + network 11.0.1.25/32 + network 11.0.1.26/32 + network 11.0.1.27/32 + network 11.0.1.28/32 + network 11.0.1.29/32 + network 11.0.1.30/32 + network 11.0.1.31/32 + network 11.0.1.32/32 + network 11.0.1.33/32 + network 11.0.1.34/32 + network 11.0.1.35/32 + network 11.0.1.36/32 + network 11.0.1.37/32 + network 11.0.1.38/32 + network 11.0.1.39/32 + network 11.0.1.40/32 + network 11.0.1.41/32 + network 11.0.1.42/32 + network 11.0.1.43/32 + network 11.0.1.44/32 + network 11.0.1.45/32 + network 11.0.1.46/32 + network 11.0.1.47/32 + network 11.0.1.48/32 + network 11.0.1.49/32 + network 11.0.1.50/32 + network 11.0.1.51/32 + network 11.0.1.52/32 + network 11.0.1.53/32 + network 11.0.1.54/32 + network 11.0.1.55/32 + network 11.0.1.56/32 + network 11.0.1.57/32 + network 11.0.1.58/32 + network 11.0.1.59/32 + network 11.0.1.60/32 + network 11.0.1.61/32 + network 11.0.1.62/32 + network 11.0.1.63/32 + network 11.0.1.64/32 + network 11.0.1.65/32 + network 11.0.1.66/32 + network 11.0.1.67/32 + network 11.0.1.68/32 + network 11.0.1.69/32 + network 11.0.1.70/32 + network 11.0.1.71/32 + network 11.0.1.72/32 + network 11.0.1.73/32 + network 11.0.1.74/32 + network 11.0.1.75/32 + network 11.0.1.76/32 + network 11.0.1.77/32 + network 11.0.1.78/32 + network 11.0.1.79/32 + network 11.0.1.80/32 + network 11.0.1.81/32 + network 11.0.1.82/32 + network 11.0.1.83/32 + network 11.0.1.84/32 + network 11.0.1.85/32 + network 11.0.1.86/32 + network 11.0.1.87/32 + network 11.0.1.88/32 + network 11.0.1.89/32 + network 11.0.1.90/32 + network 11.0.1.91/32 + network 11.0.1.92/32 + network 11.0.1.93/32 + network 11.0.1.94/32 + network 11.0.1.95/32 + network 11.0.1.96/32 + network 11.0.1.97/32 + network 11.0.1.98/32 + network 11.0.1.99/32 + network 11.0.1.100/32 + network 11.0.1.101/32 + network 11.0.1.102/32 + network 11.0.1.103/32 + network 11.0.1.104/32 + network 11.0.1.105/32 + network 11.0.1.106/32 + network 11.0.1.107/32 + network 11.0.1.108/32 + network 11.0.1.109/32 + network 11.0.1.110/32 + network 11.0.1.111/32 + network 11.0.1.112/32 + network 11.0.1.113/32 + network 11.0.1.114/32 + network 11.0.1.115/32 + network 11.0.1.116/32 + network 11.0.1.117/32 + network 11.0.1.118/32 + network 11.0.1.119/32 + network 11.0.1.120/32 + network 11.0.1.121/32 + network 11.0.1.122/32 + network 11.0.1.123/32 + network 11.0.1.124/32 + network 11.0.1.125/32 + network 11.0.1.126/32 + network 11.0.1.127/32 + network 11.0.1.128/32 + network 11.0.1.129/32 + network 11.0.1.130/32 + network 11.0.1.131/32 + network 11.0.1.132/32 + network 11.0.1.133/32 + network 11.0.1.134/32 + network 11.0.1.135/32 + network 11.0.1.136/32 + network 11.0.1.137/32 + network 11.0.1.138/32 + network 11.0.1.139/32 + network 11.0.1.140/32 + network 11.0.1.141/32 + network 11.0.1.142/32 + network 11.0.1.143/32 + network 11.0.1.144/32 + network 11.0.1.145/32 + network 11.0.1.146/32 + network 11.0.1.147/32 + network 11.0.1.148/32 + network 11.0.1.149/32 + network 11.0.1.150/32 + network 11.0.1.151/32 + network 11.0.1.152/32 + network 11.0.1.153/32 + network 11.0.1.154/32 + network 11.0.1.155/32 + network 11.0.1.156/32 + network 11.0.1.157/32 + network 11.0.1.158/32 + network 11.0.1.159/32 + network 11.0.1.160/32 + network 11.0.1.161/32 + network 11.0.1.162/32 + network 11.0.1.163/32 + network 11.0.1.164/32 + network 11.0.1.165/32 + network 11.0.1.166/32 + network 11.0.1.167/32 + network 11.0.1.168/32 + network 11.0.1.169/32 + network 11.0.1.170/32 + network 11.0.1.171/32 + network 11.0.1.172/32 + network 11.0.1.173/32 + network 11.0.1.174/32 + network 11.0.1.175/32 + network 11.0.1.176/32 + network 11.0.1.177/32 + network 11.0.1.178/32 + network 11.0.1.179/32 + network 11.0.1.180/32 + network 11.0.1.181/32 + network 11.0.1.182/32 + network 11.0.1.183/32 + network 11.0.1.184/32 + network 11.0.1.185/32 + network 11.0.1.186/32 + network 11.0.1.187/32 + network 11.0.1.188/32 + network 11.0.1.189/32 + network 11.0.1.190/32 + network 11.0.1.191/32 + network 11.0.1.192/32 + network 11.0.1.193/32 + network 11.0.1.194/32 + network 11.0.1.195/32 + network 11.0.1.196/32 + network 11.0.1.197/32 + network 11.0.1.198/32 + network 11.0.1.199/32 + network 11.0.1.200/32 + network 11.0.1.201/32 + network 11.0.1.202/32 + network 11.0.1.203/32 + network 11.0.1.204/32 + network 11.0.1.205/32 + network 11.0.1.206/32 + network 11.0.1.207/32 + network 11.0.1.208/32 + network 11.0.1.209/32 + network 11.0.1.210/32 + network 11.0.1.211/32 + network 11.0.1.212/32 + network 11.0.1.213/32 + network 11.0.1.214/32 + network 11.0.1.215/32 + network 11.0.1.216/32 + network 11.0.1.217/32 + network 11.0.1.218/32 + network 11.0.1.219/32 + network 11.0.1.220/32 + network 11.0.1.221/32 + network 11.0.1.222/32 + network 11.0.1.223/32 + network 11.0.1.224/32 + network 11.0.1.225/32 + network 11.0.1.226/32 + network 11.0.1.227/32 + network 11.0.1.228/32 + network 11.0.1.229/32 + network 11.0.1.230/32 + network 11.0.1.231/32 + network 11.0.1.232/32 + network 11.0.1.233/32 + network 11.0.1.234/32 + network 11.0.1.235/32 + network 11.0.1.236/32 + network 11.0.1.237/32 + network 11.0.1.238/32 + network 11.0.1.239/32 + network 11.0.1.240/32 + network 11.0.1.241/32 + network 11.0.1.242/32 + network 11.0.1.243/32 + network 11.0.1.244/32 + network 11.0.1.245/32 + network 11.0.1.246/32 + network 11.0.1.247/32 + network 11.0.1.248/32 + network 11.0.1.249/32 + network 11.0.1.250/32 + network 11.0.1.251/32 + network 11.0.1.252/32 + network 11.0.1.253/32 + exit-address-family + ! +! diff --git a/tests/topotests/bgp_lu_topo1/R3/zebra.conf b/tests/topotests/bgp_lu_topo1/R3/zebra.conf new file mode 100644 index 0000000..ea4a148 --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R3/zebra.conf @@ -0,0 +1,9 @@ +log file /tmp/zebra.log +! +! debug zebra events +! debug zebra packet detail +! debug zebra mpls +! +interface R3-eth0 + ip address 10.0.1.3/24 +! diff --git a/tests/topotests/bgp_lu_topo1/test_bgp_lu.py b/tests/topotests/bgp_lu_topo1/test_bgp_lu.py new file mode 100644 index 0000000..7d59762 --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/test_bgp_lu.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_lu.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# + +""" +test_bgp_lu.py: Test BGP LU label allocation +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +# Basic scenario for BGP-LU. Nodes are directly connected. +# Node 3 is advertising many routes to 2, which advertises them +# as BGP-LU to 1; this way we get routes with actual labels, as +# opposed to implicit-null routes in the 2-node case. +# +# AS1 BGP-LU AS2 iBGP AS2 +# +-----+ +-----+ +-----+ +# | |.1 .2| |.2 .3| | +# | 1 +----------------+ 2 +-----------------+ 3 | +# | | 10.0.0.0/24 | | 10.0.1.0/24 | | +# +-----+ +-----+ +-----+ + + +def build_topo(tgen): + "Build function" + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # + # Create routers + tgen.add_router("R1") + tgen.add_router("R2") + tgen.add_router("R3") + + # R1-R2 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R2"]) + + # R2-R3 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["R2"]) + switch.add_link(tgen.gears["R3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def check_labelpool(router): + json_file = "{}/{}/labelpool.summ.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bgp labelpool summary json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assertmsg = '"{}" JSON output mismatches - Did not converge'.format(router.name) + assert result is None, assertmsg + + +def test_converge_bgplu(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # tgen.mininet_cli(); + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + + check_labelpool(r1) + check_labelpool(r2) + + +def test_clear_bgplu(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # tgen.mininet_cli(); + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + + r1.vtysh_cmd("clear bgp 10.0.0.2") + check_labelpool(r1) + check_labelpool(r2) + + r2.vtysh_cmd("clear bgp 10.0.1.3") + check_labelpool(r1) + check_labelpool(r2) + + r1.vtysh_cmd("clear bgp 10.0.0.2") + r2.vtysh_cmd("clear bgp 10.0.1.3") + check_labelpool(r1) + check_labelpool(r2) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_lu_topo2/R1/bgpd.conf b/tests/topotests/bgp_lu_topo2/R1/bgpd.conf new file mode 100644 index 0000000..9fe4026 --- /dev/null +++ b/tests/topotests/bgp_lu_topo2/R1/bgpd.conf @@ -0,0 +1,29 @@ +! +no log unique-id +! +! debug bgp labelpool +! debug bgp zebra +! +router bgp 1 + bgp router-id 10.0.0.1 + timers bgp 3 9 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.0.2 remote-as 2 +! neighbor 10.0.0.2 solo + neighbor 10.0.0.2 timers connect 10 + neighbor 10.0.4.4 remote-as 4 +! neighbor 10.0.4.4 solo + neighbor 10.0.4.4 timers connect 10 +! + address-family ipv4 unicast + no neighbor 10.0.0.2 activate + no neighbor 10.0.4.4 activate + redistribute connected + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 10.0.0.2 activate + neighbor 10.0.4.4 activate + exit-address-family +! diff --git a/tests/topotests/bgp_lu_topo2/R1/labelpool.summ.json b/tests/topotests/bgp_lu_topo2/R1/labelpool.summ.json new file mode 100644 index 0000000..faeaa3e --- /dev/null +++ b/tests/topotests/bgp_lu_topo2/R1/labelpool.summ.json @@ -0,0 +1,6 @@ +{ + "ledger":51, + "inUse":51, + "requests":0, + "labelChunks":1 +} diff --git a/tests/topotests/bgp_lu_topo2/R1/zebra.conf b/tests/topotests/bgp_lu_topo2/R1/zebra.conf new file mode 100644 index 0000000..64c34a3 --- /dev/null +++ b/tests/topotests/bgp_lu_topo2/R1/zebra.conf @@ -0,0 +1,13 @@ +! +no log unique-id +! +! debug zebra events +! debug zebra rib det +! debug zebra dplane +! debug zebra mpls +! +interface R1-eth0 + ip address 10.0.0.1/24 +! +interface R1-eth1 + ip address 10.0.4.1/24 diff --git a/tests/topotests/bgp_lu_topo2/R2/bgpd.conf b/tests/topotests/bgp_lu_topo2/R2/bgpd.conf new file mode 100644 index 0000000..917060c --- /dev/null +++ b/tests/topotests/bgp_lu_topo2/R2/bgpd.conf @@ -0,0 +1,27 @@ +! +no log unique-id +! +! debug bgp labelpool +! debug bgp zebra +! +router bgp 2 + bgp router-id 10.0.0.2 + no bgp ebgp-requires-policy + no bgp network import-check + timers bgp 3 9 + neighbor 10.0.0.1 remote-as 1 + neighbor 10.0.0.1 timers connect 10 + neighbor 10.0.1.3 remote-as 2 + neighbor 10.0.1.3 update-source 10.0.1.2 + neighbor 10.0.1.3 timers connect 10 +! + address-family ipv4 unicast + network 10.0.0.0/24 + neighbor 10.0.1.3 activate + no neighbor 10.0.0.1 activate + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 10.0.0.1 activate + exit-address-family +! diff --git a/tests/topotests/bgp_lu_topo2/R2/labelpool.summ.json b/tests/topotests/bgp_lu_topo2/R2/labelpool.summ.json new file mode 100644 index 0000000..5f9d8e6 --- /dev/null +++ b/tests/topotests/bgp_lu_topo2/R2/labelpool.summ.json @@ -0,0 +1,6 @@ +{ + "ledger":1, + "inUse":1, + "requests":0, + "labelChunks":1 +} diff --git a/tests/topotests/bgp_lu_topo2/R2/zebra.conf b/tests/topotests/bgp_lu_topo2/R2/zebra.conf new file mode 100644 index 0000000..f465914 --- /dev/null +++ b/tests/topotests/bgp_lu_topo2/R2/zebra.conf @@ -0,0 +1,14 @@ +! +no log unique-id +! +! debug zebra events +! debug zebra dplane +! debug zebra mpls +! debug zebra rib det +! +interface R2-eth0 + ip address 10.0.0.2/24 +! +interface R2-eth1 + ip address 10.0.1.2/24 +! \ No newline at end of file diff --git a/tests/topotests/bgp_lu_topo2/R3/bgpd.conf b/tests/topotests/bgp_lu_topo2/R3/bgpd.conf new file mode 100644 index 0000000..6443445 --- /dev/null +++ b/tests/topotests/bgp_lu_topo2/R3/bgpd.conf @@ -0,0 +1,70 @@ +log file /tmp/bgpd.log +no log unique-id +! +! +! debug bgp updates +! +router bgp 2 + bgp router-id 10.0.1.3 + no bgp ebgp-requires-policy + no bgp network import-check + timers bgp 3 9 + neighbor 10.0.1.2 remote-as 2 + neighbor 10.0.1.2 timers connect 10 + ! + address-family ipv4 unicast + neighbor 10.0.1.2 activate + network 10.0.1.0/24 + network 11.0.0.1/32 + network 11.0.0.2/32 + network 11.0.0.3/32 + network 11.0.0.4/32 + network 11.0.0.5/32 + network 11.0.0.6/32 + network 11.0.0.7/32 + network 11.0.0.8/32 + network 11.0.0.9/32 + network 11.0.0.10/32 + network 11.0.0.11/32 + network 11.0.0.12/32 + network 11.0.0.13/32 + network 11.0.0.14/32 + network 11.0.0.15/32 + network 11.0.0.16/32 + network 11.0.0.17/32 + network 11.0.0.18/32 + network 11.0.0.19/32 + network 11.0.0.20/32 + network 11.0.0.21/32 + network 11.0.0.22/32 + network 11.0.0.23/32 + network 11.0.0.24/32 + network 11.0.0.25/32 + network 11.0.0.26/32 + network 11.0.0.27/32 + network 11.0.0.28/32 + network 11.0.0.29/32 + network 11.0.0.30/32 + network 11.0.0.31/32 + network 11.0.0.32/32 + network 11.0.0.33/32 + network 11.0.0.34/32 + network 11.0.0.35/32 + network 11.0.0.36/32 + network 11.0.0.37/32 + network 11.0.0.38/32 + network 11.0.0.39/32 + network 11.0.0.40/32 + network 11.0.0.41/32 + network 11.0.0.42/32 + network 11.0.0.43/32 + network 11.0.0.44/32 + network 11.0.0.45/32 + network 11.0.0.46/32 + network 11.0.0.47/32 + network 11.0.0.48/32 + network 11.0.0.49/32 + network 11.0.0.50/32 + exit-address-family + ! +! diff --git a/tests/topotests/bgp_lu_topo2/R3/staticd.conf b/tests/topotests/bgp_lu_topo2/R3/staticd.conf new file mode 100644 index 0000000..867fc5a --- /dev/null +++ b/tests/topotests/bgp_lu_topo2/R3/staticd.conf @@ -0,0 +1,5 @@ +log file /tmp/staticd.log +no log unique-id +! +! +ip route 10.0.4.0/24 10.0.1.2 diff --git a/tests/topotests/bgp_lu_topo2/R3/zebra.conf b/tests/topotests/bgp_lu_topo2/R3/zebra.conf new file mode 100644 index 0000000..dd24deb --- /dev/null +++ b/tests/topotests/bgp_lu_topo2/R3/zebra.conf @@ -0,0 +1,11 @@ +log file /tmp/zebra.log +no log unique-id +! +! +! debug zebra events +! debug zebra packet detail +! debug zebra mpls +! +interface R3-eth0 + ip address 10.0.1.3/24 +! diff --git a/tests/topotests/bgp_lu_topo2/R4/bgpd.conf b/tests/topotests/bgp_lu_topo2/R4/bgpd.conf new file mode 100644 index 0000000..45c81fb --- /dev/null +++ b/tests/topotests/bgp_lu_topo2/R4/bgpd.conf @@ -0,0 +1,23 @@ +! +no log unique-id +! +! debug bgp labelpool +! debug bgp zebra +! +router bgp 4 + bgp router-id 10.0.4.4 + timers bgp 3 9 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.4.1 remote-as 1 + neighbor 10.0.4.1 solo + neighbor 10.0.4.1 timers connect 10 +! + address-family ipv4 unicast + no neighbor 10.0.4.1 activate + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 10.0.4.1 activate + exit-address-family +! diff --git a/tests/topotests/bgp_lu_topo2/R4/zebra.conf b/tests/topotests/bgp_lu_topo2/R4/zebra.conf new file mode 100644 index 0000000..53ffe51 --- /dev/null +++ b/tests/topotests/bgp_lu_topo2/R4/zebra.conf @@ -0,0 +1,9 @@ +no log unique-id +! +! debug zebra events +! debug zebra dplane +! debug zebra mpls +! debug zebra rib det +! +interface R4-eth0 + ip address 10.0.4.4/24 diff --git a/tests/topotests/bgp_lu_topo2/test_bgp_lu2.py b/tests/topotests/bgp_lu_topo2/test_bgp_lu2.py new file mode 100644 index 0000000..13e6582 --- /dev/null +++ b/tests/topotests/bgp_lu_topo2/test_bgp_lu2.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# test_bgp_lu2.py +# +# Part of FRR/NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# Copyright (c) 2021 by Nvidia, Inc. +# + +""" +test_bgp_lu2.py: Test BGP LU label allocation +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.bgpd] + +# +# Basic scenario for BGP-LU. Nodes are directly connected. +# Node 3 is advertising routes to 2, which advertises them +# as BGP-LU to 1; this way we get routes with actual labels, as +# opposed to implicit-null routes in the 2-node case. +# +# R2 is an LER, with MPLS towards R1, and IP towards R3. R1 is an LSR, with +# MPLS on both sides. +# +# +# AS4 BGP-LU AS1 BGP-LU AS2 iBGP AS2 +# +-----+ +-----+ +-----+ +-----+ +# | |.4 .1| |.1 .2| |.2 .3| | +# | 4 +-------------+ 1 +----------------+ 2 +-----------------+ 3 | +# | | 10.0.4.0/24 | | 10.0.0.0/24 | | 10.0.1.0/24 | | +# +-----+ +-----+ +-----+ +-----+ +# +# + + +def build_topo(tgen): + "Build function" + + # This function's only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # + # Create routers + tgen.add_router("R1") + tgen.add_router("R2") + tgen.add_router("R3") + tgen.add_router("R4") + + # R1-R2 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R2"]) + + # R2-R3 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["R2"]) + switch.add_link(tgen.gears["R3"]) + + # R1-R4 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R4"]) + + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + + # Skip if no mpls support + if not tgen.hasmpls: + logger.info("MPLS is not available, skipping test") + pytest.skip("MPLS is not available, skipping") + return + + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # Enable mpls input for routers, so we can ping + sval = "net.mpls.conf.{}.input" + topotest.sysctl_assure(router_list["R2"], sval.format("R2-eth0"), 1) + topotest.sysctl_assure(router_list["R1"], sval.format("R1-eth0"), 1) + topotest.sysctl_assure(router_list["R1"], sval.format("R1-eth1"), 1) + topotest.sysctl_assure(router_list["R4"], sval.format("R4-eth0"), 1) + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Have static config for R3 too + if router == router_list["R3"]: + router.load_config( + TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def check_labelpool(router): + json_file = "{}/{}/labelpool.summ.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bgp labelpool summary json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assertmsg = '"{}" JSON output mismatches - Did not converge'.format(router.name) + assert result is None, assertmsg + + +def test_converge_bgplu(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # TODO -- enable for debugging + # tgen.mininet_cli() + + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + + check_labelpool(r1) + check_labelpool(r2) + + +def test_ping(): + "Simple ping tests" + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # + logger.info("Ping from R2 to R3") + router = tgen.gears["R2"] + output = router.run("ping -c 4 -w 4 {}".format("10.0.1.3")) + assert " 0% packet loss" in output, "Ping R2->R3 FAILED" + logger.info("Ping from R2 to R3 ... success") + + # + logger.info("Ping from R4 to R2") + router = tgen.gears["R4"] + output = router.run("ping -c 4 -w 4 {}".format("10.0.0.2")) + assert " 0% packet loss" in output, "Ping R4->R2 FAILED" + logger.info("Ping from R4 to R2 ... success") + + # + logger.info("Ping from R4 to R3") + router = tgen.gears["R4"] + output = router.run("ping -c 4 -w 4 {}".format("10.0.1.3")) + assert " 0% packet loss" in output, "Ping R4->R3 FAILED" + logger.info("Ping from R4 to R3 ... success") + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_max_med_on_startup/__init__.py b/tests/topotests/bgp_max_med_on_startup/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_max_med_on_startup/r1/bgpd.conf b/tests/topotests/bgp_max_med_on_startup/r1/bgpd.conf new file mode 100644 index 0000000..41bf963 --- /dev/null +++ b/tests/topotests/bgp_max_med_on_startup/r1/bgpd.conf @@ -0,0 +1,11 @@ +! +router bgp 65001 + bgp max-med on-startup 5 777 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_max_med_on_startup/r1/zebra.conf b/tests/topotests/bgp_max_med_on_startup/r1/zebra.conf new file mode 100644 index 0000000..7c2ed09 --- /dev/null +++ b/tests/topotests/bgp_max_med_on_startup/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_max_med_on_startup/r2/bgpd.conf b/tests/topotests/bgp_max_med_on_startup/r2/bgpd.conf new file mode 100644 index 0000000..187713d --- /dev/null +++ b/tests/topotests/bgp_max_med_on_startup/r2/bgpd.conf @@ -0,0 +1,7 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65001 + neighbor 192.168.255.1 timers 3 10 + ! +! diff --git a/tests/topotests/bgp_max_med_on_startup/r2/zebra.conf b/tests/topotests/bgp_max_med_on_startup/r2/zebra.conf new file mode 100644 index 0000000..fd45c48 --- /dev/null +++ b/tests/topotests/bgp_max_med_on_startup/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_max_med_on_startup/test_bgp_max_med_on_startup.py b/tests/topotests/bgp_max_med_on_startup/test_bgp_max_med_on_startup.py new file mode 100644 index 0000000..a9810ba --- /dev/null +++ b/tests/topotests/bgp_max_med_on_startup/test_bgp_max_med_on_startup.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_max_med_on_startup.py +# +# Copyright (c) 2022 Rubicon Communications, LLC. +# + +""" +Test whether `bgp max-med on-startup (5-86400) [(0-4294967295)]` is working +correctly. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_max_med_on_startup(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = {"192.168.255.1": {"bgpState": "Established"}} + return topotest.json_cmp(output, expected) + + def _bgp_has_routes(router, metric): + output = json.loads( + router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 routes json") + ) + expected = {"routes": {"172.16.255.254/32": [{"metric": metric}]}} + return topotest.json_cmp(output, expected) + + # Check session is established + test_func = functools.partial(_bgp_converge, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed bgp convergence on r2" + + # Check metric has value of max-med + test_func = functools.partial(_bgp_has_routes, router2, 777) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "r2 does not receive routes with metric 777" + + # Check that when the max-med timer expires, metric is updated + test_func = functools.partial(_bgp_has_routes, router2, 0) + success, result = topotest.run_and_expect(test_func, None, count=16, wait=0.5) + assert result is None, "r2 does not receive routes with metric 0" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/__init__.py b/tests/topotests/bgp_maximum_prefix_invalid_update/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf new file mode 100644 index 0000000..271a5bb --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + neighbor 192.168.255.2 prefix-list r2 out + exit-address-family + ! +! +ip prefix-list r2 seq 5 permit 172.16.255.253/32 +ip prefix-list r2 seq 10 permit 172.16.255.254/32 +! diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf new file mode 100644 index 0000000..68c5021 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 172.16.255.254/32 + ip address 172.16.255.253/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf new file mode 100644 index 0000000..cb30808 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 + neighbor 192.168.255.1 maximum-prefix 1 + exit-address-family + ! +! diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf new file mode 100644 index 0000000..606c17b --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py new file mode 100644 index 0000000..c6bdbc3 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# bgp_local_as_private_remove.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +bgp_maximum_prefix_invalid_update.py: +Test if unnecesarry UPDATE message like below: + +[Error] Error parsing NLRI +%NOTIFICATION: sent to neighbor X.X.X.X 3/10 (UPDATE Message Error/Invalid Network Field) 0 bytes + +is not sent if maximum-prefix count is overflow. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_maximum_prefix_invalid(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + + def _bgp_parsing_nlri(): + output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "lastNotificationReason": "Cease/Maximum Number of Prefixes Reached", + "lastResetDueTo": "BGP Notification send", + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_parsing_nlri) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Didn't send NOTIFICATION when hitting maximum-prefix" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_maximum_prefix_out/__init__.py b/tests/topotests/bgp_maximum_prefix_out/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf b/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf new file mode 100644 index 0000000..adb594b --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf @@ -0,0 +1,11 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65002 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 unicast + redistribute connected + neighbor 192.168.255.1 maximum-prefix-out 2 + exit-address-family + ! +! diff --git a/tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf b/tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf new file mode 100644 index 0000000..2416225 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf @@ -0,0 +1,13 @@ +! +interface lo + ip address 172.16.255.250/32 + ip address 172.16.255.251/32 + ip address 172.16.255.252/32 + ip address 172.16.255.253/32 + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf b/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf new file mode 100644 index 0000000..229de2e --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf @@ -0,0 +1,8 @@ +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + exit-address-family + ! +! diff --git a/tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf b/tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf new file mode 100644 index 0000000..08dd374 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py b/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py new file mode 100644 index 0000000..0b346f6 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_maximum_prefix_out.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Donatas Abraitis +# + +""" +Test if `neighbor maximum-prefix-out ` is working +correctly. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_maximum_prefix_out(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + # format (router to configure, command, expected received prefixes on r2) + tests = [ + # test of the initial config + (None, 2), + # modifying the max-prefix-out value + ( + "router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 4", + 4, + ), + # removing the max-prefix-out value + ( + "router bgp\n address-family ipv4\n no neighbor 192.168.255.1 maximum-prefix-out", + 6, + ), + # setting a max-prefix-out value + ( + "router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 3", + 3, + ), + # setting a max-prefix-out value - higher than the total number of prefix + ( + "router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 8", + 6, + ), + # adding a new prefix + ("router bgp\n int lo\n ip address 172.16.255.249/32", 7), + # setting a max-prefix-out value - lower than the total number of prefix + ( + "router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 1", + 1, + ), + # adding a new prefix + ("router bgp\n int lo\n ip address 172.16.255.248/32", 1), + # removing the max-prefix-out value + ( + "router bgp\n address-family ipv4\n no neighbor 192.168.255.1 maximum-prefix-out 1", + 8, + ), + # test setting the existing neighbor into a peer-group with a max-prefix-out value + ( + """ + router bgp + neighbor test peer-group + neighbor test remote-as 65002 + neighbor test timers 3 10 + address-family ipv4 + neighbor test maximum-prefix-out 3 + ! + neighbor 192.168.255.1 peer-group test + """, + 3, + ), + # max-prefix-out value of the neighbor must take the precedence + ( + "router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 4", + 4, + ), + ( + "router bgp\n address-family ipv4\n no neighbor 192.168.255.1 maximum-prefix-out", + 3, + ), + ( + """ + router bgp + no neighbor 192.168.255.1 peer-group test + neighbor 192.168.255.1 remote-as 65002 + neighbor 192.168.255.1 timers 3 10 + """, + 8, + ), + ( + "router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 5", + 5, + ), + # test setting the existing neighbor with a max-pref-out value into a peer-group with a max-pref-out value + ("router bgp\n neighbor 192.168.255.1 peer-group test", 5), + ( + "router bgp\n address-family ipv4\n no neighbor 192.168.255.1 maximum-prefix-out 5", + 3, + ), + ] + + def _bgp_converge(router, nb_prefixes): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": { + "ipv4Unicast": {"acceptedPrefixCounter": nb_prefixes} + }, + } + } + return topotest.json_cmp(output, expected) + + for test in tests: + cfg, exp_prfxs = test + if cfg: + cmd = ( + """ + configure terminal + %s + """ + % cfg + ) + router1.vtysh_cmd(cmd) + + test_func = functools.partial(_bgp_converge, router2, exp_prfxs) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, 'Failed bgp convergence in "{}"'.format(router2) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_minimum_holdtime/__init__.py b/tests/topotests/bgp_minimum_holdtime/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_minimum_holdtime/r1/bgpd.conf b/tests/topotests/bgp_minimum_holdtime/r1/bgpd.conf new file mode 100644 index 0000000..847a2d4 --- /dev/null +++ b/tests/topotests/bgp_minimum_holdtime/r1/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65000 + bgp minimum-holdtime 20 + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 timers connect 10 +! diff --git a/tests/topotests/bgp_minimum_holdtime/r1/zebra.conf b/tests/topotests/bgp_minimum_holdtime/r1/zebra.conf new file mode 100644 index 0000000..e2c399e --- /dev/null +++ b/tests/topotests/bgp_minimum_holdtime/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_minimum_holdtime/r2/bgpd.conf b/tests/topotests/bgp_minimum_holdtime/r2/bgpd.conf new file mode 100644 index 0000000..6d1080c --- /dev/null +++ b/tests/topotests/bgp_minimum_holdtime/r2/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_minimum_holdtime/r2/zebra.conf b/tests/topotests/bgp_minimum_holdtime/r2/zebra.conf new file mode 100644 index 0000000..606c17b --- /dev/null +++ b/tests/topotests/bgp_minimum_holdtime/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_minimum_holdtime/test_bgp_minimum_holdtime.py b/tests/topotests/bgp_minimum_holdtime/test_bgp_minimum_holdtime.py new file mode 100755 index 0000000..9f4d968 --- /dev/null +++ b/tests/topotests/bgp_minimum_holdtime/test_bgp_minimum_holdtime.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2021 by +# Takemasa Imada +# + +""" +Test if minimum-holdtime works. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_minimum_holdtime(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_neighbor_check_if_notification_sent(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show ip bgp neighbor 192.168.255.2 json") + ) + expected = { + "192.168.255.2": { + "connectionsEstablished": 0, + "lastNotificationReason": "OPEN Message Error/Unacceptable Hold Time", + "lastResetDueTo": "BGP Notification send", + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_neighbor_check_if_notification_sent) + success, result = topotest.run_and_expect(test_func, None, count=40, wait=0.5) + assert result is None, "Failed to send notification message\n" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_multi_vrf_topo1/bgp_multi_vrf_topo1.json b/tests/topotests/bgp_multi_vrf_topo1/bgp_multi_vrf_topo1.json new file mode 100644 index 0000000..327744d --- /dev/null +++ b/tests/topotests/bgp_multi_vrf_topo1/bgp_multi_vrf_topo1.json @@ -0,0 +1,884 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "red1": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": {} + } + } + } + } + } + } + } + ] + }, + "blue1": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "BLUE_A", + "id": "1" + }, + { + "name": "BLUE_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": {} + } + } + } + } + } + } + } + ] + }, + "r1": { + "links": { + "red1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "red1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "blue1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "blue1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link1": + { "next_hop_self": true } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link1": + { "next_hop_self": true } + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link2": + { "next_hop_self": true } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link2": + { "next_hop_self": true } + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link3": + { "next_hop_self": true } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link3": + { "next_hop_self": true } + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link4": + { "next_hop_self": true } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link4": + { "next_hop_self": true } + } + } + } + } + } + } + } + ] + }, + "r2": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": {} + } + }, + "r3": { + "dest_link": { + "r2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": {} + } + }, + "r3": { + "dest_link": { + "r2-link2": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": {} + } + }, + "r3": { + "dest_link": { + "r2-link3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": {} + } + }, + "r3": { + "dest_link": { + "r2-link3": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": {} + } + }, + "r3": { + "dest_link": { + "r2-link4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": {} + } + }, + "r3": { + "dest_link": { + "r2-link4": {} + } + } + } + } + } + } + } + ] + }, + "r3": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "red2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "red2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "blue2-link1": {"ipv4": "auto", "ipv6": "autor3", "vrf": "BLUE_A"}, + "blue2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "red2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "red2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": {} + } + }, + "red2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": {} + } + }, + "red2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": {} + } + }, + "blue2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": {} + } + }, + "blue2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": {} + } + }, + "blue2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": {} + } + }, + "blue2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + } + } + } + ] + }, + "red2": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": {} + } + } + } + } + } + } + } + ] + }, + "blue2": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "BLUE_A", + "id": "1" + }, + { + "name": "BLUE_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": {} + } + } + } + } + } + } + } + ] + } + } +} diff --git a/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py new file mode 100644 index 0000000..0d92a3c --- /dev/null +++ b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py @@ -0,0 +1,6277 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test BGP Multi-VRF: + +FUNC_1: + Within each VRF, each address must be unambiguous on DUT. +FUNC_2: + Different VRFs can have ambiguous/overlapping + addresses on DUT. +FUNC_3: + Create static routes(IPv4+IPv6) associated to specific VRFs + and verify on DUT that same prefixes are present in corresponding + routing table. +FUNC_4_&_5: + Each VRF should be mapped with a unique VLAN on DUT + for traffic segregation, when using a single physical interface. +FUNC_6: + Advertise same set of prefixes from different VRFs + and verify on remote router that these prefixes are not + leaking to each other +FUNC_7: + Redistribute Static routes and verify on remote routers + that routes are advertised within specific VRF instance, which + those static routes belong to. +FUNC_8: + Test end to end traffic isolation based on VRF tables. +FUNC_9: + Use static routes for inter-vrf communication + (route-leaking) on DUT. +FUNC_10: + Verify intra-vrf and inter-vrf communication between + iBGP peers. +FUNC_11: + Verify intra-vrf and inter-vrf communication + between eBGP peers. +FUNC_12_a: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_12_b: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_12_c: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_12_d: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_12_e: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_12_f: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_13: + Configure a route-map on DUT to match traffic based + on a VRF interfaces. +FUNC_14: + Test VRF-lite with Static+BGP originated routes. +FUNC_15: + Configure prefix-lists on DUT and apply to BGP peers to + permit/deny prefixes. +FUNC_16_1: + Configure a route-map on DUT to match traffic based various + match/set causes. +FUNC_16_2: + Configure a route-map on DUT to match traffic based various + match/set causes. +FUNC_16_3: + Configure a route-map on DUT to match traffic based various + match/set causes. +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import iproute2_is_vrf_capable +from lib.common_config import ( + step, + verify_rib, + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + create_route_maps, + create_static_routes, + create_prefix_lists, + create_interface_in_kernel, + create_bgp_community_lists, + check_router_status, + apply_raw_config, + required_linux_kernel_version, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_rib, + create_router_bgp, + verify_bgp_community, + verify_bgp_convergence, + verify_best_path_as_per_bgp_attribute, +) +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +# Global variables +NETWORK1_1 = {"ipv4": "1.1.1.1/32", "ipv6": "1::1/128"} +NETWORK1_2 = {"ipv4": "1.1.1.2/32", "ipv6": "1::2/128"} +NETWORK2_1 = {"ipv4": "2.1.1.1/32", "ipv6": "2::1/128"} +NETWORK2_2 = {"ipv4": "2.1.1.2/32", "ipv6": "2::2/128"} +NETWORK3_1 = {"ipv4": "3.1.1.1/32", "ipv6": "3::1/128"} +NETWORK3_2 = {"ipv4": "3.1.1.2/32", "ipv6": "3::2/128"} +NETWORK4_1 = {"ipv4": "4.1.1.1/32", "ipv6": "4::1/128"} +NETWORK4_2 = {"ipv4": "4.1.1.2/32", "ipv6": "4::2/128"} +NETWORK5_1 = {"ipv4": "5.1.1.1/32", "ipv6": "5::1/128"} +NETWORK5_2 = {"ipv4": "5.1.1.2/32", "ipv6": "5::2/128"} +NETWORK6_1 = {"ipv4": "6.1.1.1/32", "ipv6": "6::1/128"} +NETWORK6_2 = {"ipv4": "6.1.1.2/32", "ipv6": "6::2/128"} +NETWORK7_1 = {"ipv4": "7.1.1.1/32", "ipv6": "7::1/128"} +NETWORK7_2 = {"ipv4": "7.1.1.2/32", "ipv6": "7::2/128"} +NETWORK8_1 = {"ipv4": "8.1.1.1/32", "ipv6": "8::1/128"} +NETWORK8_2 = {"ipv4": "8.1.1.2/32", "ipv6": "8::2/128"} + +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + +LOOPBACK_1 = { + "ipv4": "10.10.10.10/32", + "ipv6": "10::10:10/128", +} +LOOPBACK_2 = { + "ipv4": "20.20.20.20/32", + "ipv6": "20::20:20/128", +} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + # iproute2 needs to support VRFs for this suite to run. + if not iproute2_is_vrf_capable(): + pytest.skip("Installed iproute2 version does not support VRFs") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_multi_vrf_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_address_unambiguous_within_each_vrf_p0(request): + """ + FUNC_1: + Within each VRF, each address must be unambiguous on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure a set of static routes(IPv4+IPv6) in " "RED_A on router RED-1") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure the same static routes(IPv4+IPv6) with a TAG value" + "of 500 in RED_A on router RED-1" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 500, + "vrf": "RED_A", + } + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = { + "red1": { + "bgp": { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes(IPv4+IPv6) is overridden and doesn't" + " have duplicate entries within VRF RED_A on router RED-1" + ) + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 500, + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, tag=500) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Make sure routes are not present in global routing table") + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Expected Behaviour: Routes are not " + "present on Global Routing table \n Error {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_ambiguous_overlapping_addresses_in_different_vrfs_p0(request): + """ + FUNC_2: + Different VRFs can have ambiguous/overlapping + addresses on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure a set of static routes(IPv4+IPv6) in vrf RED_A" "on router RED-1") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure the same static routes(IPv4+IPv6) with a" + " TAG value of 500 in vrf RED_B on router RED-1" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 500, + "vrf": "RED_B", + } + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that RED_A has the static routes without any" " TAG value") + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_1, tag=500, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Routes are present with tag value 500 \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behavior: {}".format(result)) + + step( + "Verify that RED_B has the same routes with TAG value " + "500 on same device RED-1" + ) + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 500, + "vrf": "RED_B", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, tag=500) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Make sure routes are not present in global routing table") + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Expected Behaviour: Routes are not " + "present on Global Routing table \n Error {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_static_routes_associated_to_specific_vrfs_p0(request): + """ + FUNC_3: + Create static routes(IPv4+IPv6) associated to specific VRFs + and verify on DUT that same prefixes are present in corresponding + routing table. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Configure a set of unique static(IPv4+IPv6) routes in vrf" + " RED_A on router RED-1" + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure set of unique static routes(IPv4+IPv6) in vrf " + "RED_B on router RED-1" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes 1.x.x.x/32 and 1::x/128 appear " "in VRF RED_A table" + ) + step( + "Verify that static routes 2.x.x.x/32 and 2::x/128 appear " "in VRF RED_B table" + ) + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Verify that static routes 1.x.x.x/32 and 1::x/128 appear " + "in VRF BLUE_A table" + ) + step( + "Verify that static routes 2.x.x.x/32 and 2::x/128 appear " + "in VRF BLUE_B table" + ) + + for addr_type in ADDR_TYPES: + dut = "blue1" + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Make sure routes are not present in global routing table") + + for addr_type in ADDR_TYPES: + dut = "blue1" + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Expected Behaviour: Routes are not " + "present on Global Routing table \n Error {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_vrf_with_unique_physical_interface_p0(request): + """ + FUNC_4_&_5: + Each VRF should be mapped with a unique VLAN on DUT + for traffic segregation, when using a single physical interface. + + Each VRF should be mapped to a unique physical + interface(without VLAN tagging) on DUT for traffic segregation. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "R1 is receiving routes in 4 VRFs instances " + "(RED_A, RED_B, BLUE_A, BLUE_B) from RED_1 and BLUE_1." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise a set of unique BGP prefixes(IPv4+IPv6) from " + "routers RED_1 & BLUE_1 in each VRF using static redistribution" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Each VRF table on R2 should maintain it's associated " + "routes and and accordingly install in zebra" + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_prefixes_leaking_p0(request): + """ + FUNC_6: + Advertise same set of prefixes from different VRFs + and verify on remote router that these prefixes are not + leaking to each other + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure a set of static routes(IPv4+IPv6) in vrf " "RED_A on router RED-1") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + }, + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + } + ] + }, + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure a set of static routes(IPv4+IPv6) in vrf " "BLUE_A on router BLUE-1" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + } + ] + }, + "blue1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + } + ] + }, + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure the same set of static routes with a " + "metric value of 123 in vrf RED_B on router RED-1" + ) + step( + "Configure the same set of static routes with a " + "metric value of 123 in vrf BLUE_B on router BLUE-1" + ) + + input_dict_3 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + }, + }, + ] + }, + "blue1": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R1 that RED_A doesn't receive any static " + "route with metric value 123" + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + }, + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + } + ] + }, + } + + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + } + ] + }, + "blue1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + } + ] + }, + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, dut, input_dict_1, metric=123, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Routes are present with metric value 123 \n Error: {}".format( + tc_name, result + ) + ) + logger.info("Expected Behavior: {}".format(result)) + + result = verify_rib(tgen, addr_type, dut, input_dict_2, metric=123) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, dut, input_dict_2, metric=0, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Routes are present with metric value 0 \n Error: {}".format( + tc_name, result + ) + ) + logger.info("Expected Behavior: {}".format(result)) + + write_test_footer(tc_name) + + +def test_static_routes_advertised_within_specific_vrf_p0(request): + """ + FUNC_7: + Redistribute Static routes and verify on remote routers + that routes are advertised within specific VRF instance, which + those static routes belong to. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise a set of unique BGP prefixes(IPv4+IPv6) " + "through static redistribution into VRF RED_A and RED_B" + " from router RED-1." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same as above set of BGP prefixes(IPv4+IPv6) " + "through static redistribution into VRF BLUE_A and BLUE_B" + " from router BLUE-1." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes are installed into vrfs RED_A" + "and RED_B tables only, not in global routing table of RED_1" + ) + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Verify that static routes are installed into vrfs BLUE_A and" + "BLUE_B tables only, not in global routing table of BLUE_1." + ) + + for addr_type in ADDR_TYPES: + dut = "blue1" + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, protocol="static") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Verify on router R1, that each set of prefixes is received" + " into associated vrf tables only." + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_end_to_end_traffic_isolation_p0(request): + """ + FUNC_8: + Test end to end traffic isolation based on VRF tables. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1 " + "in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in" + " vrf instances(BLUE_A and BLUE_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Use below commands to send prefixes with as-path prepend" + "VRF BLUE_A and BLUE_B from router BLUE-1." + ) + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "blue1": { + "route_maps": { + "ASP_{}".format(addr_type): [ + { + "action": "permit", + "set": {"path": {"as_num": 123, "as_action": "prepend"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Apply route-map to neighbours") + + input_dict_5 = { + "blue1": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": { + "route_maps": [ + { + "name": "ASP_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": { + "route_maps": [ + { + "name": "ASP_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": { + "route_maps": [ + { + "name": "ASP_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": { + "route_maps": [ + { + "name": "ASP_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R1 that BLUE_A and BLUE_B VRFs are receiving the" + " prefixes with as-path 123 prepended." + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_6 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_7 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Use below commands to send prefixes with as-path prepend VRF" + " BLUE_A and BLUE_B from router BLUE-1." + ) + + input_dict_6 = { + "red2": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + ] + }, + "blue2": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that router RED-2 receives the prefixes in respective" " VRF tables.") + + for addr_type in ADDR_TYPES: + dut = "red2" + input_dict_6 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + dut = "blue2" + input_dict_7 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_static_routes_for_inter_vrf_route_leaking_p0(request): + """ + FUNC_9: + Use static routes for inter-vrf communication + (route-leaking) on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Configure unique loopback interfaces in VRFs RED_A " + "and RED_B on router RED_1." + ) + + for addr_type in ADDR_TYPES: + create_interface_in_kernel( + tgen, + "red1", + "loopback1", + LOOPBACK_1[addr_type], + "RED_A", + ) + create_interface_in_kernel( + tgen, + "red1", + "loopback2", + LOOPBACK_2[addr_type], + "RED_B", + ) + + step( + "Create a static routes in vrf RED_B on router RED_1 pointing" + " next-hop as interface's IP in vrf RED_A" + ) + + intf_red1_r11 = topo["routers"]["red1"]["links"]["r1-link1"]["interface"] + intf_red1_r10 = topo["routers"]["red1"]["links"]["r1-link2"]["interface"] + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_1[addr_type], + "interface": intf_red1_r10, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_A", + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes are installed into vrfs RED_A" + "and RED_B tables only, not in global routing table of RED_1" + ) + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_1[addr_type], + "interface": intf_red1_r10, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_A", + "vrf": "RED_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_inter_vrf_and_intra_vrf_communication_iBGP_p0(request): + """ + FUNC_10: + Verify intra-vrf and inter-vrf communication between + iBGP peers. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Configure unique loopback IP(IPv4+IPv6) in vrf RED_A on router" + " R1 and advertise it in BGP process using redistribute " + "connected command." + ) + + for addr_type in ADDR_TYPES: + create_interface_in_kernel( + tgen, + "r1", + "loopback1", + LOOPBACK_1[addr_type], + "RED_A", + ) + + create_interface_in_kernel( + tgen, + "r1", + "loopback2", + LOOPBACK_2[addr_type], + "BLUE_A", + ) + + step( + "Create a static routes in vrf RED_B on router RED_1 pointing" + " next-hop as interface's IP in vrf RED_A" + ) + + intf_r2_r12 = topo["routers"]["r2"]["links"]["r1-link1"]["interface"] + intf_r2_r10 = topo["routers"]["r2"]["links"]["r1-link3"]["interface"] + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r2": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_r2_r10, + "nexthop_vrf": "BLUE_A", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_1[addr_type], + "interface": intf_r2_r12, + "nexthop_vrf": "RED_A", + "vrf": "BLUE_A", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute connected..") + + input_dict_3 = {} + for dut in ["r1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + VRFS = ["RED_A", "BLUE_A"] + AS_NUM = [100, 100] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["r2"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + VRFS = ["RED_A", "BLUE_A"] + AS_NUM = [100, 100] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes are installed into vrfs RED_A" + "and RED_B tables only, not in global routing table of RED_1" + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict = { + "r2": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_r2_r10, + "nexthop_vrf": "BLUE_A", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_1[addr_type], + "interface": intf_r2_r12, + "nexthop_vrf": "RED_A", + "vrf": "BLUE_A", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_inter_vrf_and_intra_vrf_communication_eBGP_p0(request): + """ + FUNC_11: + Verify intra-vrf and inter-vrf communication + between eBGP peers. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Configure unique loopback IP(IPv4+IPv6) in vrf RED_A on router" + " R2 and advertise it in BGP process using redistribute " + "connected command." + ) + + step( + "Configure unique loopback IP(IPv4+IPv6) in vrf BLUE_A on router" + " R2 and advertise it in BGP process using redistribute " + "connected command." + ) + + for addr_type in ADDR_TYPES: + create_interface_in_kernel( + tgen, + "r2", + "loopback1", + LOOPBACK_1[addr_type], + "RED_A", + ) + create_interface_in_kernel( + tgen, + "r2", + "loopback2", + LOOPBACK_2[addr_type], + "BLUE_A", + ) + + step( + "Create a static routes in vrf RED_B on router RED_1 pointing" + " next-hop as interface's IP in vrf RED_A" + ) + + intf_r3_r21 = topo["routers"]["r3"]["links"]["r2-link1"]["interface"] + intf_r3_r23 = topo["routers"]["r3"]["links"]["r2-link3"]["interface"] + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_r3_r23, + "nexthop_vrf": "BLUE_A", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_1[addr_type], + "interface": intf_r3_r21, + "nexthop_vrf": "RED_A", + "vrf": "BLUE_A", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["r3"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + VRFS = ["RED_A", "BLUE_A"] + AS_NUM = [200, 200] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Redistribute connected..") + + input_dict_3 = {} + for dut in ["r2"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + VRFS = ["RED_A", "BLUE_A"] + AS_NUM = [100, 100] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes are installed into vrfs RED_A" + "and RED_B tables only, not in global routing table of RED_1" + ) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict = { + "r3": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_r3_r23, + "nexthop_vrf": "BLUE_A", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_1[addr_type], + "interface": intf_r3_r21, + "nexthop_vrf": "RED_A", + "vrf": "BLUE_A", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_map_within_vrf_to_alter_bgp_attribute_nexthop_p0(request): + """ + FUNC_12_a: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and" + " RED_2 in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Delete nexthop-self configure from r1") + + input_dict_4 = { + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"next_hop_self": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"next_hop_self": False} + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link2": {"next_hop_self": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link2": {"next_hop_self": False} + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link3": {"next_hop_self": False} + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link3": {"next_hop_self": False} + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link4": {"next_hop_self": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link4": {"next_hop_self": False} + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected Behaviour: Routes are rejected because nexthop-self config is deleted \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected Behaviour: Routes are rejected because nexthop-self config is deleted \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize("attribute", ["locPrf", "weight", "metric"]) +def test_route_map_within_vrf_to_alter_bgp_attribute_p0(request, attribute): + """ + FUNC_12_b/c/d: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and" + " RED_2 in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + "red2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + "blue2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "red2", "blue1", "blue2"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure a route-maps to influence BGP parameters - " " Local Preference") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "route_maps": { + "rmap_r1_{}".format(addr_type): [ + {"action": "permit", "set": {attribute: 120}} + ], + "rmap_r3_{}".format(addr_type): [ + {"action": "permit", "set": {attribute: 150}} + ], + } + } + } + + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map") + input_dict_4 = { + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r3_ipv4", + "direction": "in", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r3_ipv6", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r3_ipv4", + "direction": "in", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r3_ipv6", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r3_ipv4", + "direction": "in", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r3_ipv6", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r3_ipv4", + "direction": "in", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r3_ipv6", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + dut = "r2" + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict_1, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict_2, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_map_within_vrf_to_alter_bgp_attribute_aspath_p0(request): + """ + FUNC_12_e: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and" + " RED_2 in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + "red2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + "blue2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "red2", "blue1", "blue2"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure a route-maps to influence BGP parameters - " " Local Preference") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "route_maps": { + "rmap_r1_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "path": {"as_num": "111 222", "as_action": "prepend"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map") + input_dict_4 = { + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link1": {}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link1": {}}}, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link2": {}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link2": {}}}, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link3": {}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link3": {}}}, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link4": {}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link4": {}}}, + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + dut = "r2" + attribute = "path" + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict_1, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict_2, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_map_within_vrf_to_alter_bgp_attribute_lcomm_p0(request): + """ + FUNC_12_f: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and" + " RED_2 in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + "red2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + "blue2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "red2", "blue1", "blue2"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure a route-maps to influence BGP parameters - " " Large-community") + + step("Create standard large commumity-list in r2") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r2": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "rmap_lcomm_{}".format(addr_type), + "value": "1:1:1 1:2:3 2:1:1 2:2:2", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Create route-maps in red1 and r1") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "red1": { + "route_maps": { + "rmap_red1_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "large_community": {"num": "1:1:1 1:2:3 2:1:1 2:2:2"} + }, + } + ] + } + }, + "r2": { + "route_maps": { + "rmap_r1_{}".format(addr_type): [ + { + "action": "permit", + "match": { + "large_community_list": { + "id": "rmap_lcomm_" + addr_type + } + }, + } + ] + } + }, + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map in red1") + + input_dict_4 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map in r2") + + input_dict_4 = { + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "All the prefixes advertised from RED_1 and BLUE_1 should carry" + " attributes set by outbound route-maps within specific vrfs. " + "Router R1 should be able to match and permit/deny those " + "prefixes based on received attributes. Please use below " + "commands to verify." + ) + + input_dict = { + "largeCommunity": "1:1:1 1:2:3 2:1:1 2:2:2", + } + + for addr_type in ADDR_TYPES: + vrf = "RED_A" + routes = [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]] + result = verify_bgp_community(tgen, addr_type, "r2", routes, input_dict, vrf) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + vrf = "RED_B" + routes = [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]] + result = verify_bgp_community(tgen, addr_type, "r2", routes, input_dict, vrf) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_map_match_traffic_based_on_vrf_p0(request): + """ + FUNC_13: + Configure a route-map on DUT to match traffic based + on a VRF interfaces. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1 " + "in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in" + " vrf instances(BLUE_A and BLUE_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure a route-map on R1 to match the prefixes " + "coming from vrf RED_A and set as-prepend to these routes." + ) + + input_dict_4 = { + "r1": { + "route_maps": { + "ABC": [ + { + "action": "permit", + "match": {"source-vrf": "RED_A"}, + "set": {"path": {"as_num": 1, "as_action": "prepend"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "On R1, import the routes form vrf RED_A and RED_B to BLUE_A and" + " apply the route-map under vrf BLUE_A while importing" + ) + + raw_config = { + "r1": { + "raw_config": [ + "router bgp 100 vrf BLUE_A", + "address-family ipv4 unicast", + "import vrf RED_A", + "import vrf RED_B", + "import vrf route-map ABC", + "address-family ipv6 unicast", + "import vrf RED_A", + "import vrf RED_B", + "import vrf route-map ABC", + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "All the prefixes advertised from RED_1 and BLUE_1 in vrfs " + "RED_B and BLUE_B must prepend the AS number in as-path on R2." + ) + + for addr_type in ADDR_TYPES: + input_dict_7 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_7) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_vrf_lite_with_static_bgp_originated_routes_p0(request): + """ + FUNC_14: + Test VRF-lite with Static+BGP originated routes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in" + " vrf instances(BLUE_A and BLUE_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_3 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK5_1["ipv4"]] + + [NETWORK5_2["ipv4"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK5_1["ipv6"]] + + [NETWORK5_2["ipv6"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK6_1["ipv4"]] + + [NETWORK6_2["ipv4"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK6_1["ipv6"]] + + [NETWORK6_2["ipv6"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + }, + }, + ] + }, + "blue1": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK7_1["ipv4"]] + + [NETWORK7_2["ipv4"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK7_1["ipv6"]] + + [NETWORK7_2["ipv6"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK8_1["ipv4"]] + + [NETWORK8_2["ipv4"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK8_1["ipv6"]] + + [NETWORK8_2["ipv6"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Static routes must be installed in associated VRF" " table only.") + + for addr_type in ADDR_TYPES: + dut = "r1" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "All the routers must receive advertised as well as " + "redistributed(static) prefixes in associated VRF tables." + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_prefix_list_to_permit_deny_prefixes_p0(request): + """ + FUNC_15: + Configure prefix-lists on DUT and apply to BGP peers to + permit/deny prefixes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in" + " vrf instances(BLUE_A and BLUE_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify routes are present before applying prefix-list") + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "On routers RED_1 and BLUE_1, configure prefix-lists to permit" + " 4 prefixes and deny 1 prefix x.x.x.5. Apply these in outbound" + "direction for each neighbour." + ) + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "red1": { + "prefix_lists": { + addr_type: { + "pflist_red1_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + }, + { + "seqid": 11, + "network": NETWORK2_1[addr_type], + "action": "permit", + }, + { + "seqid": 12, + "network": NETWORK1_2[addr_type], + "action": "deny", + }, + { + "seqid": 13, + "network": NETWORK2_2[addr_type], + "action": "deny", + }, + ] + } + } + }, + "blue1": { + "prefix_lists": { + addr_type: { + "pflist_blue1_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + }, + { + "seqid": 11, + "network": NETWORK2_1[addr_type], + "action": "permit", + }, + { + "seqid": 12, + "network": NETWORK1_2[addr_type], + "action": "deny", + }, + { + "seqid": 13, + "network": NETWORK2_2[addr_type], + "action": "deny", + }, + ] + } + } + }, + "r1": { + "prefix_lists": { + addr_type: { + "pflist_r1_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + }, + { + "seqid": 11, + "network": NETWORK2_1[addr_type], + "action": "deny", + }, + ] + } + } + }, + } + result = create_prefix_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_5 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "prefix_lists": [ + { + "name": "pflist_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "prefix_lists": [ + { + "name": "pflist_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "prefix_lists": [ + { + "name": "pflist_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "prefix_lists": [ + { + "name": "pflist_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + }, + "blue1": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": { + "prefix_lists": [ + { + "name": "pflist_blue1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": { + "prefix_lists": [ + { + "name": "pflist_blue1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": { + "prefix_lists": [ + { + "name": "pflist_blue1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": { + "prefix_lists": [ + { + "name": "pflist_blue1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, each BGP neighbor receives 1" + " prefixes in routing table and drops (x.x.x.2)." + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + permitted_routes = { + "red1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "RED_A"}, + {"network": [NETWORK2_1[addr_type]], "vrf": "RED_B"}, + ] + } + } + + denied_routes = { + "red1": { + "static_routes": [ + {"network": [NETWORK1_2[addr_type]], "vrf": "RED_A"}, + {"network": [NETWORK2_2[addr_type]], "vrf": "RED_B"}, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, permitted_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, denied_routes, expected=False) + assert result is not True, "Testcase {} : Failed \n" + "{}:Expected behaviour: Routes are denied by prefix-list \nError {}".format( + tc_name, result + ) + + step( + "On router R1, configure prefix-lists to permit 2 " + "prefixes(x.x.x.1-2) and deny 2 prefix(x.x.x.3-4). Apply" + " these in inbound direction for each neighbour." + ) + + input_dict_6 = { + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, each BGP neighbor installs" + " only 1 prefix (x.x.x.1)." + ) + for addr_type in ADDR_TYPES: + dut = "r2" + permitted_routes = { + "red1": { + "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "RED_A"}] + } + } + + denied_routes = { + "red1": { + "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "RED_A"}] + } + } + + result = verify_rib(tgen, addr_type, dut, permitted_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, denied_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \nExpected behaviour: Routes are denied by prefix-list \nError {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_map_set_and_match_tag_p0(request): + """ + FUNC_16_1: + Configure a route-map on DUT to match traffic based various + match/set causes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 4001, + "vrf": "RED_A", + }, + { + "network": [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 4001, + "vrf": "BLUE_A", + }, + { + "network": [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure a route-maps to match tag") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "red1": { + "route_maps": { + "rmap1_{}".format(addr_type): [ + {"action": "permit", "match": {addr_type: {"tag": "4001"}}} + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map") + input_dict_4 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + dut = "r1" + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 4001, + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected Behavior: Routes are denied \nError {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_map_set_and_match_metric_p0(request): + """ + FUNC_16_2: + Configure a route-map on DUT to match traffic based various + match/set causes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + }, + ] + }, + "blue1": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure a route-maps to match tag") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "route_maps": { + "rmap1_{}".format(addr_type): [ + {"action": "permit", "match": {"metric": 123}} + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map") + input_dict_4 = { + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + dut = "r1" + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected Behavior: Routes are denied \nError {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_map_set_and_match_community_p0(request): + """ + FUNC_16_3: + Configure a route-map on DUT to match traffic based various + match/set causes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create community-list") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "rmap_lcomm_{}".format(addr_type), + "value": "1:1 1:2 1:3 1:4 1:5", + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure a route-maps to match tag") + + step("Create route-maps in red1 and r1") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "red1": { + "route_maps": { + "rmap_red1_{}".format(addr_type): [ + { + "action": "permit", + "set": {"community": {"num": "1:1 1:2 1:3 1:4 1:5"}}, + } + ] + } + }, + "r1": { + "route_maps": { + "rmap1_{}".format(addr_type): [ + { + "action": "permit", + "match": { + "community_list": {"id": "rmap_lcomm_" + addr_type} + }, + } + ] + } + }, + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map") + input_dict_4 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + }, + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "All the prefixes advertised from RED_1 and BLUE_1 should carry" + " attributes set by outbound route-maps within specific vrfs. " + "Router R1 should be able to match and permit/deny those " + "prefixes based on received attributes. Please use below " + "commands to verify." + ) + + input_dict = { + "community": "1:1 1:2 1:3 1:4 1:5", + } + + for addr_type in ADDR_TYPES: + vrf = "RED_A" + routes = [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]] + result = verify_bgp_community(tgen, addr_type, "r1", routes, input_dict, vrf) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + vrf = "RED_B" + routes = [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]] + result = verify_bgp_community(tgen, addr_type, "r1", routes, input_dict, vrf) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_multi_vrf_topo2/bgp_multi_vrf_topo2.json b/tests/topotests/bgp_multi_vrf_topo2/bgp_multi_vrf_topo2.json new file mode 100644 index 0000000..bcee7e1 --- /dev/null +++ b/tests/topotests/bgp_multi_vrf_topo2/bgp_multi_vrf_topo2.json @@ -0,0 +1,1553 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "red1": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ] + }, + "blue1": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "BLUE_A", + "id": "1" + }, + { + "name": "BLUE_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ] + }, + "r1": { + "links": { + "red1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "red1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "blue1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "blue1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r4-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r4-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r1-link1": + { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + }, + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r1-link1": + { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + }, + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r1-link2": + { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + }, + "r4": { + "dest_link": { + "r1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r1-link2": + { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + }, + "r4": { + "dest_link": { + "r1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r1-link3": + { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + }, + "r4": { + "dest_link": { + "r1-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r1-link3": + { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + }, + "r4": { + "dest_link": { + "r1-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r1-link4": + { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + }, + "r4": { + "dest_link": { + "r1-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r1-link4": + { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + }, + "r4": { + "dest_link": { + "r1-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ] + }, + "r2": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r2-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r2-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r2-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r2-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ] + }, + "r3": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r4-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r4-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "red2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "red2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "blue2-link1": {"ipv4": "auto", "ipv6": "autor3", "vrf": "BLUE_A"}, + "blue2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "red2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }], + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }], + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "red2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }], + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "red2": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }], + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }], + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "red2": { + "dest_link": { + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }], + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "blue2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }], + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }], + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "blue2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }], + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "blue2": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }], + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }], + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "blue2": { + "dest_link": { + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }], + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "400", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r4-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r4-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r4-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r4-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r3": { + "dest_link": { + "r4-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r4-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "red2": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ] + }, + "blue2": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "BLUE_A", + "id": "1" + }, + { + "name": "BLUE_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ] + } + } +} diff --git a/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py new file mode 100644 index 0000000..40a28fb --- /dev/null +++ b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py @@ -0,0 +1,3839 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test BGP Multi-VRF: + +CHAOS_1: + Do a shut and no shut on connecting interface of DUT, + to see if all vrf instances clear their respective BGP tables + during the interface down and restores when interface brought +kCHAOS_3: + VRF leaking - next-hop interface is flapping. +CHAOS_5: + VRF - VLANs - Routing Table ID - combination testcase + on DUT. +CHAOS_9: + Verify that all vrf instances fall back + to backup path, if primary link goes down. +CHAOS_6: + Restart BGPd daemon on DUT to check if all the + routes in respective vrfs are reinstalled.. +CHAOS_2: + Delete a VRF instance from DUT and check if the routes get + deleted from subsequent neighbour routers and appears again once VRF + is re-added. +CHAOS_4: + Verify that VRF names are locally significant + to a router, and end to end connectivity depends on unique + virtual circuits (using VLANs or separate physical interfaces). +CHAOS_8: + Restart all FRR services (reboot DUT) to check if all + the routes in respective vrfs are reinstalled. +""" + +import os +import sys +import time +import pytest +from copy import deepcopy +from time import sleep + + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import iproute2_is_vrf_capable +from lib.common_config import ( + step, + verify_rib, + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + create_route_maps, + shutdown_bringup_interface, + start_router_daemons, + create_static_routes, + create_vrf_cfg, + create_interfaces_cfg, + create_interface_in_kernel, + get_frr_ipv6_linklocal, + check_router_status, + apply_raw_config, + required_linux_kernel_version, + kill_router_daemons, + start_router_daemons, + stop_router, + start_router, +) + +from lib.topolog import logger +from lib.bgp import clear_bgp, verify_bgp_rib, create_router_bgp, verify_bgp_convergence +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +# Global variables +NETWORK1_1 = {"ipv4": "1.1.1.1/32", "ipv6": "1::1/128"} +NETWORK1_2 = {"ipv4": "1.1.1.2/32", "ipv6": "1::2/128"} +NETWORK2_1 = {"ipv4": "2.1.1.1/32", "ipv6": "2::1/128"} +NETWORK2_2 = {"ipv4": "2.1.1.2/32", "ipv6": "2::2/128"} +NETWORK3_1 = {"ipv4": "3.1.1.1/32", "ipv6": "3::1/128"} +NETWORK3_2 = {"ipv4": "3.1.1.2/32", "ipv6": "3::2/128"} +NETWORK4_1 = {"ipv4": "4.1.1.1/32", "ipv6": "4::1/128"} +NETWORK4_2 = {"ipv4": "4.1.1.2/32", "ipv6": "4::2/128"} +NETWORK9_1 = {"ipv4": "100.1.0.1/30", "ipv6": "100::1/126"} +NETWORK9_2 = {"ipv4": "100.1.0.2/30", "ipv6": "100::2/126"} + +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + +LOOPBACK_2 = { + "ipv4": "20.20.20.20/32", + "ipv6": "20::20:20/128", +} + +MAX_PATHS = 2 +KEEPALIVETIMER = 1 +HOLDDOWNTIMER = 3 +PREFERRED_NEXT_HOP = "link_local" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.14") + if result is not True: + pytest.skip("Kernel requirements are not met") + + # iproute2 needs to support VRFs for this suite to run. + if not iproute2_is_vrf_capable(): + pytest.skip("Installed iproute2 version does not support VRFs") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_multi_vrf_topo2.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_vrf_with_multiple_links_p1(request): + """ + CHAOS_9: + Verify that all vrf instances fall back + to backup path, if primary link goes down. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Configure BGP neighborships(IPv4+IPv6) between R1 and R4 " + "using exact same link IPs for all 4 VRFs." + ) + + topo_modify = deepcopy(topo) + build_config_from_json(tgen, topo_modify) + + interfaces = ["link1", "link2", "link3", "link4"] + for interface in interfaces: + topo_modify["routers"]["r1"]["links"]["r4-{}".format(interface)][ + "delete" + ] = True + topo_modify["routers"]["r4"]["links"]["r1-{}".format(interface)][ + "delete" + ] = True + + step("Build interface config from json") + create_interfaces_cfg(tgen, topo_modify["routers"]) + + interfaces = ["link1", "link2", "link3", "link4"] + for interface in interfaces: + del topo_modify["routers"]["r1"]["links"]["r4-{}".format(interface)]["delete"] + del topo_modify["routers"]["r4"]["links"]["r1-{}".format(interface)]["delete"] + + r1_config = [] + r4_config = [] + for addr_type in ADDR_TYPES: + interfaces = ["link1", "link2", "link3", "link4"] + for interface in interfaces: + intf_name_r1 = topo_modify["routers"]["r1"]["links"][ + "r4-{}".format(interface) + ]["interface"] + topo_modify["routers"]["r1"]["links"]["r4-{}".format(interface)][ + addr_type + ] = NETWORK9_1[addr_type] + + intf_name_r4 = topo_modify["routers"]["r4"]["links"][ + "r1-{}".format(interface) + ]["interface"] + topo_modify["routers"]["r4"]["links"]["r1-{}".format(interface)][ + addr_type + ] = NETWORK9_2[addr_type] + + r1_config.append("interface {}".format(intf_name_r1)) + r4_config.append("interface {}".format(intf_name_r4)) + if addr_type == "ipv4": + r1_config.append("no ip address {}".format(NETWORK9_1[addr_type])) + r4_config.append("no ip address {}".format(NETWORK9_2[addr_type])) + else: + r1_config.append("no ipv6 address {}".format(NETWORK9_1[addr_type])) + r4_config.append("no ipv6 address {}".format(NETWORK9_2[addr_type])) + + step("Build interface config from json") + create_interfaces_cfg(tgen, topo_modify["routers"]) + + step("Create bgp config") + result = create_router_bgp(tgen, topo_modify["routers"]) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP convergence") + + result = verify_bgp_convergence(tgen, topo_modify) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + step( + "Advertise below prefixes in BGP using static redistribution" + " for both vrfs (RED_A and BLUE_A) on router R2.." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["r1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + VRFS = ["RED_A", "RED_B", "BLUE_A", "BLUE_B"] + AS_NUM = [100, 100, 100, 100] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo_modify, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify routes are installed with same nexthop in different" " VRFs") + result = verify_bgp_convergence(tgen, topo_modify) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r4" + _input_dict = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + _input_dict = { + "r1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + } + ] + } + } + + R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + _input_dict = { + "r1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + } + ] + } + } + + R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + _input_dict = { + "r1": { + "static_routes": [ + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + } + ] + } + } + + R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Configure a route-map on R3 to prepend as-path and apply" + " for neighbour router R2 in both vrfs, in inbound direction." + ) + + input_dict_4 = { + "r3": { + "route_maps": { + "ASP": [ + { + "action": "permit", + "set": {"path": {"as_num": 123, "as_action": "prepend"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Apply route-map to neighbours") + step( + "Configure ECMP on router R3 using 'max-path' command for both" + " VRFs RED_A and BLUE_A." + ) + + input_dict_5 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + }, + "r4": { + "dest_link": { + "r3-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + }, + "r4": { + "dest_link": { + "r3-link3": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo_modify, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_bgp_convergence(tgen, topo_modify) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r3" + peer = "r2" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + intf = topo_modify["routers"][peer]["links"]["r3-link1"]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="RED_A") + else: + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link1"][ + addr_type + ].split("/")[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + intf = topo["routers"][peer]["links"]["r3-link3"]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="BLUE_A") + else: + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link3"][ + addr_type + ].split("/")[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Configure ECMP on router R3 using max-path command for" + " both VRFs RED_A and BLUE_A." + ) + + input_dict_7 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "maximum_paths": { + "ebgp": MAX_PATHS, + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ebgp": MAX_PATHS, + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "maximum_paths": { + "ebgp": MAX_PATHS, + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ebgp": MAX_PATHS, + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo_modify, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("R3 should install prefixes from both next-hops (R2 and R4)") + result = verify_bgp_convergence(tgen, topo_modify) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r3" + peer = "r2" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + intf = topo_modify["routers"][peer]["links"]["r3-link1"]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="RED_A") + else: + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link1"][ + addr_type + ].split("/")[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + intf = topo_modify["routers"][peer]["links"]["r3-link3"]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="BLUE_A") + else: + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link3"][ + addr_type + ].split("/")[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Shutdown interface between R2 and R3 for vrfs RED_A and " "BLUE_A.") + + intf1 = topo_modify["routers"]["r2"]["links"]["r3-link1"]["interface"] + intf2 = topo_modify["routers"]["r2"]["links"]["r3-link3"]["interface"] + + interfaces = [intf1, intf2] + for intf in interfaces: + shutdown_bringup_interface(tgen, "r2", intf, False) + + for addr_type in ADDR_TYPES: + dut = "r3" + peer = "r4" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link3"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Unshut the interfaces between R2 and R3 for vrfs RED_A and BLUE_A.") + + for intf in interfaces: + shutdown_bringup_interface(tgen, "r2", intf, True) + + for addr_type in ADDR_TYPES: + dut = "r3" + peer = "r2" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link3"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Remove route-map from R3 for vrfs RED_A and BLUE_A.") + + input_dict_6 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [ + { + "name": "ASP_ipv4", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [ + { + "name": "ASP_ipv6", + "direction": "in", + "delete": True, + }, + { + "name": "rmap_global", + "direction": "in", + }, + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "route_maps": [ + { + "name": "ASP_ipv4", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "route_maps": [ + { + "name": "ASP_ipv6", + "direction": "in", + "delete": True, + }, + { + "name": "rmap_global", + "direction": "in", + }, + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo_modify, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_bgp_convergence(tgen, topo_modify) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link3"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Shutdown links between between R2 and R3 for vrfs RED_A and" " BLUE_A.") + + for intf in interfaces: + shutdown_bringup_interface(tgen, "r2", intf, False) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link3"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Bringup links between between R2 and R3 for vrfs RED_A and" " BLUE_A.") + + for intf in interfaces: + shutdown_bringup_interface(tgen, "r2", intf, True) + + step("Deleting manualy assigned ip address from router r1 and r4 interfaces") + raw_config = {"r1": {"raw_config": r1_config}, "r4": {"raw_config": r4_config}} + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_shut_noshut_p1(request): + """ + CHAOS_1: + Do a shut and no shut on connecting interface of DUT, + to see if all vrf instances clear their respective BGP tables + during the interface down and restores when interface brought + back up again. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Build interface config from json") + create_interfaces_cfg(tgen, topo["routers"]) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise unique prefixes in BGP using static redistribution" + " for both vrfs (RED_A and RED_B) on router RED_1." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique prefixes in BGP using static redistribution" + " for both vrfs (BLUE_A and BLUE_B) on router BLUE_1." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Api call to modify BGP timers") + + input_dict_4 = { + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link4": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link4": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + ] + }, + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r1", vrf=["RED_A", "RED_B", "BLUE_A", "BLUE_B"]) + + clear_bgp(tgen, addr_type, "r2", vrf=["RED_A", "RED_B", "BLUE_A", "BLUE_B"]) + + step("Shut down connecting interface between R1<<>>R2 on R1.") + step("Repeat step-3 and step-4 10 times.") + + for count in range(1, 2): + step("Iteration {}".format(count)) + step("Shut down connecting interface between R1<<>>R2 on R1.") + + intf1 = topo["routers"]["r1"]["links"]["r2-link1"]["interface"] + intf2 = topo["routers"]["r1"]["links"]["r2-link2"]["interface"] + intf3 = topo["routers"]["r1"]["links"]["r2-link3"]["interface"] + intf4 = topo["routers"]["r1"]["links"]["r2-link4"]["interface"] + + interfaces = [intf1, intf2, intf3, intf4] + for intf in interfaces: + shutdown_bringup_interface(tgen, "r1", intf, False) + + step( + "On R2, all BGP peering in respective vrf instances go down" + " when the interface is shut" + ) + + step("Sleeping for {}+1 sec..".format(HOLDDOWNTIMER)) + sleep(HOLDDOWNTIMER + 1) + + result = verify_bgp_convergence(tgen, topo, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \nExpected Behaviour: BGP will not be converged \nError {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \nExpected Behaviour: Routes are flushed out \nError {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \nExpected Behaviour: Routes are flushed out \nError {}".format( + tc_name, result + ) + + step("Bring up connecting interface between R1<<>>R2 on R1.") + for intf in interfaces: + shutdown_bringup_interface(tgen, "r1", intf, True) + + step( + "R2 restores BGP peering and routing tables in all vrf " + "instances when interface brought back up again" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_vrf_vlan_routing_table_p1(request): + """ + CHAOS_5: + VRF - VLANs - Routing Table ID - combination testcase + on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique prefixes(IPv4+IPv6) in BGP using" + " network command for vrf RED_A on router R2" + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = { + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes(IPv4+IPv6) is overridden and doesn't" + " have duplicate entries within VRF RED_A on router RED-1" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict_1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Api call to modify BGP timers") + + input_dict_4 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r3", vrf=["RED_A"]) + + step("Repeat for 5 times.") + + for count in range(1, 2): + step("Iteration {}..".format(count)) + step("Delete a specific VRF instance(RED_A) from router R3") + + input_dict = {"r3": {"vrfs": [{"name": "RED_A", "id": "1", "delete": True}]}} + + result = create_vrf_cfg(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Sleeping for {}+1 sec..".format(HOLDDOWNTIMER)) + sleep(HOLDDOWNTIMER + 1) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict_1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected Behaviour: Routes are cleaned \n Error {}".format( + tc_name, result + ) + + step("Add/reconfigure the same VRF instance again") + + result = create_vrf_cfg(tgen, {"r3": topo["routers"]["r3"]}) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "After deleting VRFs ipv6 addresses will be deleted from kernel " + " Adding back ipv6 addresses" + ) + + dut = "r3" + vrf = "RED_A" + + for c_link, c_data in topo["routers"][dut]["links"].items(): + if c_data["vrf"] != vrf: + continue + + intf_name = c_data["interface"] + intf_ipv6 = c_data["ipv6"] + + create_interface_in_kernel( + tgen, dut, intf_name, intf_ipv6, vrf, create=False + ) + + step("Sleeping for {}+1 sec..".format(HOLDDOWNTIMER)) + sleep(HOLDDOWNTIMER + 1) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict_1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_vrf_route_leaking_next_hop_interface_flapping_p1(request): + """ + CHAOS_3: + VRF leaking - next-hop interface is flapping. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Create loopback interface") + + for addr_type in ADDR_TYPES: + create_interface_in_kernel( + tgen, + "red1", + "loopback2", + LOOPBACK_2[addr_type], + "RED_B", + ) + + intf_red1_r11 = topo["routers"]["red1"]["links"]["r1-link2"]["interface"] + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + step("VRF RED_A should install a route for vrf RED_B's " "loopback ip.") + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Repeat step-2 to 4 at least 5 times") + + for count in range(1, 2): + intf1 = topo["routers"]["red1"]["links"]["r1-link2"]["interface"] + + step( + "Iteration {}: Shutdown interface {} on router" + "RED_1.".format(count, intf1) + ) + shutdown_bringup_interface(tgen, "red1", intf1, False) + + step("Verify that RED_A removes static route from routing " "table.") + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib( + tgen, addr_type, dut, input_dict_1, protocol="static", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n Expected Behaviour: Routes are" + " not present Error {}".format(tc_name, result) + ) + + step("Bring up interface {} on router RED_1 again.".format(intf1)) + shutdown_bringup_interface(tgen, "red1", intf1, True) + + step( + "Verify that RED_A reinstalls static route pointing to " + "RED_B's IP in routing table again" + ) + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_restart_bgpd_daemon_p1(request): + """ + CHAOS_6: + Restart BGPd daemon on DUT to check if all the + routes in respective vrfs are reinstalled.. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + reset_config_on_routers(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from BLUE_1 in" + " vrf instances(BLUE_A and BLUE_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed\n Error {}".format(tc_name, result) + + step("Kill BGPd daemon on R1.") + kill_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Routes are still present in VRF RED_A and RED_B \n Error: {}".format( + tc_name, result + ) + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Routes are still present in VRF BLUE_A and BLUE_B \n Error: {}".format( + tc_name, result + ) + ) + + step("Bring up BGPd daemon on R1.") + start_router_daemons(tgen, "r1", ["bgpd"]) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_delete_and_re_add_vrf_p1(request): + """ + CHAOS_2: + Delete a VRF instance from DUT and check if the routes get + deleted from subsequent neighbour routers and appears again once VRF + is re-added. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise unique prefixes in BGP using static redistribution" + "for both vrfs (RED_A and RED_B) on router RED_1" + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique prefixes in BGP using static redistribution" + " for both vrfs (BLUE_A and BLUE_B) on router BLUE_1." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static for vrfs RED_A and RED_B and BLUE_A and BLUE_B") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verifying RIB and FIB before deleting VRFs") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Api call to modify BGP timers") + + input_dict_4 = { + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link4": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link4": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r1", vrf=["RED_A", "RED_B", "BLUE_A", "BLUE_B"]) + + step("Delete vrfs RED_A and BLUE_A from R1.") + + input_dict = { + "r1": { + "vrfs": [ + {"name": "RED_A", "id": "1", "delete": True}, + {"name": "BLUE_A", "id": "3", "delete": True}, + ] + } + } + + result = create_vrf_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step( + "R2 must not receive the prefixes(in respective vrfs)" + "originated from RED_1 and BLUE_1." + ) + + step("Wait for {}+1 sec..".format(HOLDDOWNTIMER)) + sleep(HOLDDOWNTIMER + 1) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + }, + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + } + ] + }, + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert result is not True, ( + "Testcase {} :Failed \n Expected Behaviour:" + " Routes are not present \n Error {}".format(tc_name, result) + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert result is not True, ( + "Testcase {} :Failed \n Expected Behaviour:" + " Routes are not present \n Error {}".format(tc_name, result) + ) + + step("Add vrfs again RED_A and BLUE_A on R1.") + + result = create_vrf_cfg(tgen, {"r1": topo["routers"]["r1"]}) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + create_interfaces_cfg(tgen, {"r1": topo["routers"]["r1"]}) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step( + "After deleting VRFs ipv6 addresses will be deleted from kernel " + " Adding back ipv6 addresses" + ) + + dut = "r1" + vrfs = ["RED_A", "BLUE_A"] + + for vrf in vrfs: + for c_link, c_data in topo["routers"][dut]["links"].items(): + if c_data["vrf"] != vrf: + continue + + intf_name = c_data["interface"] + intf_ipv6 = c_data["ipv6"] + + create_interface_in_kernel( + tgen, dut, intf_name, intf_ipv6, vrf, create=False + ) + + step( + "R2 should now receive the prefixes(in respective vrfs)" + "again. Check the debugging logs as well. For verification" + " use same commands as mention in step-3." + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {}: Failed\n Error {}".format(tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_vrf_name_significance_p1(request): + """ + CHAOS_4: + Verify that VRF names are locally significant + to a router, and end to end connectivity depends on unique + virtual circuits (using VLANs or separate physical interfaces). + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique prefixes in BGP using static redistribution" + "for both vrfs (RED_A and RED_B) on router RED_1" + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique prefixes in BGP using static redistribution" + " for both vrfs (BLUE_A and BLUE_B) on router BLUE_1." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static for vrfs RED_A and RED_B and BLUE_A and BLUE_B") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure allowas-in on red2 and blue2") + + input_dict_4 = { + "red2": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + ] + }, + "blue2": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verifying RIB and FIB before deleting VRFs") + + for addr_type in ADDR_TYPES: + dut = "red2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "blue2" + input_dict_3 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + } + ] + } + } + + input_dict_4 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_3) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_3) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Api call to modify BGP timers") + + input_dict_4 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red2": { + "dest_link": { + "r3-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red2": { + "dest_link": { + "r3-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue2": { + "dest_link": { + "r3-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue2": { + "dest_link": { + "r3-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + ] + }, + "red2": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + ] + }, + "blue2": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r3", vrf=["RED_A", "RED_B", "BLUE_A", "BLUE_B"]) + + clear_bgp(tgen, addr_type, "red2", vrf=["RED_A", "RED_B"]) + + clear_bgp(tgen, addr_type, "blue2", vrf=["BLUE_A", "BLUE_B"]) + + step("Delete vrfs RED_A and BLUE_A from R3") + + input_dict = { + "r3": { + "vrfs": [ + {"name": "RED_A", "id": "1", "delete": True}, + {"name": "BLUE_A", "id": "3", "delete": True}, + ] + } + } + + result = create_vrf_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Waiting for {}+1..".format(HOLDDOWNTIMER)) + sleep(HOLDDOWNTIMER + 1) + + step("Verify RIB and FIB after deleting VRFs") + + for addr_type in ADDR_TYPES: + dut = "red2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert ( + result is not True + ), "Testcase {} :Failed \n Expected Behaviour: Routes are not present \n Error {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert ( + result is not True + ), "Testcase {} :Failed \n Expected Behaviour: Routes are not present \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + dut = "blue2" + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert ( + result is not True + ), "Testcase {} :Failed \n Expected Behaviour: Routes are not present \n Error {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert ( + result is not True + ), "Testcase {} :Failed \n Expected Behaviour: Routes are not present \n Error {}".format( + tc_name, result + ) + + step("Create 2 new VRFs PINK_A and GREY_A IN R3") + + topo_modify = deepcopy(topo) + topo_modify["routers"]["r3"]["vrfs"][0]["name"] = "PINK_A" + topo_modify["routers"]["r3"]["vrfs"][0]["id"] = "1" + topo_modify["routers"]["r3"]["vrfs"][2]["name"] = "GREY_A" + topo_modify["routers"]["r3"]["vrfs"][2]["id"] = "3" + + topo_modify["routers"]["r3"]["links"]["red2-link1"]["vrf"] = "PINK_A" + topo_modify["routers"]["r3"]["links"]["blue2-link1"]["vrf"] = "GREY_A" + + topo_modify["routers"]["r3"]["links"]["r2-link1"]["vrf"] = "PINK_A" + topo_modify["routers"]["r3"]["links"]["r2-link3"]["vrf"] = "GREY_A" + + topo_modify["routers"]["r3"]["links"]["r4-link1"]["vrf"] = "PINK_A" + topo_modify["routers"]["r3"]["links"]["r4-link3"]["vrf"] = "GREY_A" + + topo_modify["routers"]["r3"]["bgp"][0]["vrf"] = "PINK_A" + topo_modify["routers"]["r3"]["bgp"][2]["vrf"] = "GREY_A" + + result = create_vrf_cfg(tgen, {"r3": topo_modify["routers"]["r3"]}) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + create_interfaces_cfg(tgen, {"r3": topo_modify["routers"]["r3"]}) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = create_router_bgp(tgen, topo_modify["routers"]) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Api call to modify BGP timers") + + input_dict_4 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "vrf": "PINK_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red2": { + "dest_link": { + "r3-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red2": { + "dest_link": { + "r3-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "GREY_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue2": { + "dest_link": { + "r3-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue2": { + "dest_link": { + "r3-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo_modify, input_dict_4) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r3", vrf=["PINK_A", "RED_B", "GREY_A", "BLUE_B"]) + + step( + "After deleting VRFs ipv6 addresses will be deleted from kernel " + " Adding back ipv6 addresses" + ) + + dut = "r3" + vrfs = ["GREY_A", "PINK_A"] + + for vrf in vrfs: + for c_link, c_data in topo_modify["routers"][dut]["links"].items(): + if c_data["vrf"] != vrf: + continue + + intf_name = c_data["interface"] + intf_ipv6 = c_data["ipv6"] + + create_interface_in_kernel( + tgen, dut, intf_name, intf_ipv6, vrf, create=False + ) + + step("Waiting for {}+1 sec..".format(HOLDDOWNTIMER)) + sleep(HOLDDOWNTIMER + 1) + + step( + "Advertised prefixes should appear again in respective VRF" + " table on routers RED_2 and BLUE_2. Verify fib and rib entries" + ) + + for addr_type in ADDR_TYPES: + dut = "red2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "blue2" + input_dict_3 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + } + ] + } + } + + input_dict_4 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_3) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_3) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_restart_frr_services_p1(request): + """ + CHAOS_8: + Restart all FRR services (reboot DUT) to check if all + the routes in respective vrfs are reinstalled. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from BLUE_1 in" + " vrf instances(BLUE_A and BLUE_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Restart frr on R1") + stop_router(tgen, "r1") + start_router(tgen, "r1") + + for addr_type in ADDR_TYPES: + dut = "r2" + + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_multiview_topo1/README.md b/tests/topotests/bgp_multiview_topo1/README.md new file mode 100644 index 0000000..c1a1445 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/README.md @@ -0,0 +1,125 @@ +# Simple FRRouting Route-Server Test + +## Topology + +----------+ +----------+ +----------+ +----------+ +----------+ + | peer1 | | peer2 | | peer3 | | peer4 | | peer5 | + | AS 65001 | | AS 65002 | | AS 65003 | | AS 65004 | | AS 65005 | + +-----+----+ +-----+----+ +-----+----+ +-----+----+ +-----+----+ + | .1 | .2 | .3 | .4 | .5 + | ______/ / / _________/ + \ / ________________/ / / + | | / _________________________/ / +----------+ + | | | / __________________________/ ___| peer6 | + | | | | / ____________________________/.6 | AS 65006 | + | | | | | / _________________________ +----------+ + | | | | | | / __________________ \ +----------+ + | | | | | | | / \ \___| peer7 | + | | | | | | | | \ .7 | AS 65007 | + ~~~~~~~~~~~~~~~~~~~~~ \ +----------+ + ~~ SW1 ~~ \ +----------+ + ~~ Switch ~~ \_____| peer8 | + ~~ 172.16.1.0/24 ~~ .8 | AS 65008 | + ~~~~~~~~~~~~~~~~~~~~~ +----------+ + | + | .254 + +---------+---------+ + | FRR R1 | + | BGP Multi-View | + | Peer 1-3 > View 1 | + | Peer 4-5 > View 2 | + | Peer 6-8 > View 3 | + +---------+---------+ + | .1 + | + ~~~~~~~~~~~~~ Stub Network is redistributed + ~~ SW0 ~~ into each BGP view with different + ~~ 172.20.0.1/28 ~~ attributes (using route-map) + ~~ Stub Switch ~~ + ~~~~~~~~~~~~~ + +## FRR Configuration + +Full config as used is in r1 subdirectory + +Simplified `R1` config: + + hostname r1 + ! + interface r1-stub + description Stub Network + ip address 172.20.0.1/28 + no link-detect + ! + interface r1-eth0 + description to PE router - vlan1 + ip address 172.16.1.254/24 + no link-detect + ! + router bgp 100 view 1 + bgp router-id 172.30.1.1 + network 172.20.0.0/28 route-map local1 + timers bgp 60 180 + neighbor 172.16.1.1 remote-as 65001 + neighbor 172.16.1.2 remote-as 65002 + neighbor 172.16.1.5 remote-as 65005 + ! + router bgp 100 view 2 + bgp router-id 172.30.1.1 + network 172.20.0.0/28 route-map local2 + timers bgp 60 180 + neighbor 172.16.1.3 remote-as 65003 + neighbor 172.16.1.4 remote-as 65004 + ! + router bgp 100 view 3 + bgp router-id 172.30.1.1 + network 172.20.0.0/28 + timers bgp 60 180 + neighbor 172.16.1.6 remote-as 65006 + neighbor 172.16.1.7 remote-as 65007 + neighbor 172.16.1.8 remote-as 65008 + ! + route-map local1 permit 10 + set community 100:9999 additive + set metric 0 + ! + route-map local2 permit 10 + set as-path prepend 100 100 100 100 100 + set community 100:1 additive + set metric 9999 + ! + +## Tests executed + +### Check if FRR is running + +Test is executed by running + + vtysh -c "show logging" | grep "Logging configuration for" + +on router `R1`. This should return the logging information for all daemons registered +to Zebra and the list of running daemons is compared to the daemons started for this +test (`zebra` and `bgpd`) + +### Verify for BGP to converge + +BGP is expected to converge on each view within 60s total time. Convergence is verified by executing + + vtysh -c "show ip bgp view 1 summary" + vtysh -c "show ip bgp view 2 summary" + vtysh -c "show ip bgp view 3 summary" + +and expecting 11 routes seen in the last column for each peer. (Each peer sends 11 routes) + +### Verifying BGP Routing Tables + +Routing table is verified by running + + vtysh -c "show ip bgp view 1" + vtysh -c "show ip bgp view 2" + vtysh -c "show ip bgp view 3" + +and comparing the result against the stored table in the r1/show_ip_bgp_view_NN.ref files +(with NN 1, 2, 3) (A few header and trailer lines are cut/adjusted ahead of the compare to +adjust for different output based on recent changes) + + diff --git a/tests/topotests/bgp_multiview_topo1/exabgp.env b/tests/topotests/bgp_multiview_topo1/exabgp.env new file mode 100644 index 0000000..a328e04 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/exabgp.env @@ -0,0 +1,54 @@ + +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' +##daemonize = false + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py new file mode 100755 index 0000000..09f6ea5 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) + +# Announce numRoutes different routes per PE +for i in range(0, numRoutes): + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) + stdout.flush() + +# Announce 1 overlapping route per peer +stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer)) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg new file mode 100644 index 0000000..20e71c8 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 1 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 1"; + receive-routes; + encoder text; + } + + neighbor 172.16.1.254 { + router-id 172.16.1.1; + local-address 172.16.1.1; + local-as 65001; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py new file mode 100755 index 0000000..09f6ea5 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) + +# Announce numRoutes different routes per PE +for i in range(0, numRoutes): + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) + stdout.flush() + +# Announce 1 overlapping route per peer +stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer)) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg new file mode 100644 index 0000000..1e8eef1 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 2 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 2"; + receive-routes; + encoder text; + } + + neighbor 172.16.1.254 { + router-id 172.16.1.2; + local-address 172.16.1.2; + local-as 65002; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py new file mode 100755 index 0000000..09f6ea5 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) + +# Announce numRoutes different routes per PE +for i in range(0, numRoutes): + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) + stdout.flush() + +# Announce 1 overlapping route per peer +stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer)) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg new file mode 100644 index 0000000..ef1b249 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 3 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 3"; + receive-routes; + encoder text; + } + + neighbor 172.16.1.254 { + router-id 172.16.1.3; + local-address 172.16.1.3; + local-as 65003; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py new file mode 100755 index 0000000..09f6ea5 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) + +# Announce numRoutes different routes per PE +for i in range(0, numRoutes): + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) + stdout.flush() + +# Announce 1 overlapping route per peer +stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer)) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg new file mode 100644 index 0000000..7c50f73 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 4 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 4"; + receive-routes; + encoder text; + } + + neighbor 172.16.1.254 { + router-id 172.16.1.4; + local-address 172.16.1.4; + local-as 65004; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py new file mode 100755 index 0000000..09f6ea5 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) + +# Announce numRoutes different routes per PE +for i in range(0, numRoutes): + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) + stdout.flush() + +# Announce 1 overlapping route per peer +stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer)) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg new file mode 100644 index 0000000..22163c7 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 5 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 5"; + receive-routes; + encoder text; + } + + neighbor 172.16.1.254 { + router-id 172.16.1.5; + local-address 172.16.1.5; + local-as 65005; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py new file mode 100755 index 0000000..09f6ea5 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) + +# Announce numRoutes different routes per PE +for i in range(0, numRoutes): + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) + stdout.flush() + +# Announce 1 overlapping route per peer +stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer)) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg new file mode 100644 index 0000000..40b54c3 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 6 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 6"; + receive-routes; + encoder text; + } + + neighbor 172.16.1.254 { + router-id 172.16.1.6; + local-address 172.16.1.6; + local-as 65006; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py new file mode 100755 index 0000000..09f6ea5 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) + +# Announce numRoutes different routes per PE +for i in range(0, numRoutes): + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) + stdout.flush() + +# Announce 1 overlapping route per peer +stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer)) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg new file mode 100644 index 0000000..33312d0 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 7 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 7"; + receive-routes; + encoder text; + } + + neighbor 172.16.1.254 { + router-id 172.16.1.7; + local-address 172.16.1.7; + local-as 65007; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py new file mode 100755 index 0000000..09f6ea5 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) + +# Announce numRoutes different routes per PE +for i in range(0, numRoutes): + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) + stdout.flush() + +# Announce 1 overlapping route per peer +stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer)) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg new file mode 100644 index 0000000..173ccb9 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 8 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 8"; + receive-routes; + encoder text; + } + + neighbor 172.16.1.254 { + router-id 172.16.1.8; + local-address 172.16.1.8; + local-as 65008; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_multiview_topo1/r1/bgpd.conf b/tests/topotests/bgp_multiview_topo1/r1/bgpd.conf new file mode 100644 index 0000000..cd7f44a --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/r1/bgpd.conf @@ -0,0 +1,61 @@ +! +! Zebra configuration saved from vty +! 2015/12/24 21:46:33 +! +log file bgpd.log +! +!debug bgp events +!debug bgp keepalives +!debug bgp updates +!debug bgp fsm +!debug bgp filters +!debug bgp zebra +! +router bgp 100 view 1 + bgp router-id 172.30.1.1 + bgp always-compare-med + no bgp ebgp-requires-policy + network 172.20.0.0/28 route-map local1 + timers bgp 60 180 + neighbor 172.16.1.1 remote-as 65001 + neighbor 172.16.1.1 timers 3 10 + neighbor 172.16.1.2 remote-as 65002 + neighbor 172.16.1.2 timers 3 10 + neighbor 172.16.1.5 remote-as 65005 + neighbor 172.16.1.5 timers 3 10 +! +router bgp 100 view 2 + bgp router-id 172.30.1.1 + bgp always-compare-med + no bgp ebgp-requires-policy + network 172.20.0.0/28 route-map local2 + timers bgp 60 180 + neighbor 172.16.1.3 remote-as 65003 + neighbor 172.16.1.3 timers 3 10 + neighbor 172.16.1.4 remote-as 65004 + neighbor 172.16.1.4 timers 3 10 +! +router bgp 100 view 3 + bgp router-id 172.30.1.1 + bgp always-compare-med + no bgp ebgp-requires-policy + network 172.20.0.0/28 + timers bgp 60 180 + neighbor 172.16.1.6 remote-as 65006 + neighbor 172.16.1.6 timers 3 10 + neighbor 172.16.1.7 remote-as 65007 + neighbor 172.16.1.7 timers 3 10 + neighbor 172.16.1.8 remote-as 65008 + neighbor 172.16.1.8 timers 3 10 +! +route-map local1 permit 10 + set community 100:9999 additive + set metric 0 +! +route-map local2 permit 10 + set as-path prepend 100 100 100 100 100 + set community 100:1 additive + set metric 9999 +! +line vty +! diff --git a/tests/topotests/bgp_multiview_topo1/r1/view_1.json b/tests/topotests/bgp_multiview_topo1/r1/view_1.json new file mode 100644 index 0000000..137b8a3 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/r1/view_1.json @@ -0,0 +1,728 @@ +{ + "vrfName": "1", + "routerId": "172.30.1.1", + "defaultLocPrf": 100, + "localAS": 100, + "routes": { + "10.0.1.0/24": [ + { + "valid": true, + "pathFrom": "external", + "prefix": "10.0.1.0", + "prefixLen": 24, + "network": "10.0.1.0/24", + "metric": 5, + "weight": 0, + "peerId": "172.16.1.5", + "path": "65005", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.5", + "afi": "ipv4", + "used": true + } + ] + }, + { + "valid": true, + "pathFrom": "external", + "prefix": "10.0.1.0", + "prefixLen": 24, + "network": "10.0.1.0/24", + "metric": 2, + "weight": 0, + "peerId": "172.16.1.2", + "path": "65002", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.2", + "afi": "ipv4", + "used": true + } + ] + }, + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.0.1.0", + "prefixLen": 24, + "network": "10.0.1.0/24", + "metric": 1, + "weight": 0, + "peerId": "172.16.1.1", + "path": "65001", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.101.0.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.101.0.0", + "prefixLen": 24, + "network": "10.101.0.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.1", + "path": "65001", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.101.1.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.101.1.0", + "prefixLen": 24, + "network": "10.101.1.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.1", + "path": "65001", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.101.2.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.101.2.0", + "prefixLen": 24, + "network": "10.101.2.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.1", + "path": "65001", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.101.3.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.101.3.0", + "prefixLen": 24, + "network": "10.101.3.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.1", + "path": "65001", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.101.4.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.101.4.0", + "prefixLen": 24, + "network": "10.101.4.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.1", + "path": "65001", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.101.5.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.101.5.0", + "prefixLen": 24, + "network": "10.101.5.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.1", + "path": "65001", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.101.6.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.101.6.0", + "prefixLen": 24, + "network": "10.101.6.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.1", + "path": "65001", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.101.7.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.101.7.0", + "prefixLen": 24, + "network": "10.101.7.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.1", + "path": "65001", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.101.8.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.101.8.0", + "prefixLen": 24, + "network": "10.101.8.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.1", + "path": "65001", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.101.9.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.101.9.0", + "prefixLen": 24, + "network": "10.101.9.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.1", + "path": "65001", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.102.0.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.102.0.0", + "prefixLen": 24, + "network": "10.102.0.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.2", + "path": "65002", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.102.1.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.102.1.0", + "prefixLen": 24, + "network": "10.102.1.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.2", + "path": "65002", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.102.2.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.102.2.0", + "prefixLen": 24, + "network": "10.102.2.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.2", + "path": "65002", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.102.3.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.102.3.0", + "prefixLen": 24, + "network": "10.102.3.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.2", + "path": "65002", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.102.4.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.102.4.0", + "prefixLen": 24, + "network": "10.102.4.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.2", + "path": "65002", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.102.5.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.102.5.0", + "prefixLen": 24, + "network": "10.102.5.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.2", + "path": "65002", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.102.6.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.102.6.0", + "prefixLen": 24, + "network": "10.102.6.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.2", + "path": "65002", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.102.7.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.102.7.0", + "prefixLen": 24, + "network": "10.102.7.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.2", + "path": "65002", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.102.8.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.102.8.0", + "prefixLen": 24, + "network": "10.102.8.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.2", + "path": "65002", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.102.9.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.102.9.0", + "prefixLen": 24, + "network": "10.102.9.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.2", + "path": "65002", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.105.0.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.105.0.0", + "prefixLen": 24, + "network": "10.105.0.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.5", + "path": "65005", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.5", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.105.1.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.105.1.0", + "prefixLen": 24, + "network": "10.105.1.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.5", + "path": "65005", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.5", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.105.2.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.105.2.0", + "prefixLen": 24, + "network": "10.105.2.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.5", + "path": "65005", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.5", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.105.3.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.105.3.0", + "prefixLen": 24, + "network": "10.105.3.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.5", + "path": "65005", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.5", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.105.4.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.105.4.0", + "prefixLen": 24, + "network": "10.105.4.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.5", + "path": "65005", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.5", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.105.5.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.105.5.0", + "prefixLen": 24, + "network": "10.105.5.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.5", + "path": "65005", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.5", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.105.6.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.105.6.0", + "prefixLen": 24, + "network": "10.105.6.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.5", + "path": "65005", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.5", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.105.7.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.105.7.0", + "prefixLen": 24, + "network": "10.105.7.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.5", + "path": "65005", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.5", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.105.8.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.105.8.0", + "prefixLen": 24, + "network": "10.105.8.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.5", + "path": "65005", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.5", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.105.9.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.105.9.0", + "prefixLen": 24, + "network": "10.105.9.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.5", + "path": "65005", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.5", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_multiview_topo1/r1/view_2.json b/tests/topotests/bgp_multiview_topo1/r1/view_2.json new file mode 100644 index 0000000..2ad28c5 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/r1/view_2.json @@ -0,0 +1,489 @@ +{ + "vrfName": "2", + "routerId": "172.30.1.1", + "defaultLocPrf": 100, + "localAS": 100, + "routes": { + "10.0.1.0/24": [ + { + "valid": true, + "pathFrom": "external", + "prefix": "10.0.1.0", + "prefixLen": 24, + "network": "10.0.1.0/24", + "metric": 4, + "weight": 0, + "peerId": "172.16.1.4", + "path": "65004", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.4", + "afi": "ipv4", + "used": true + } + ] + }, + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.0.1.0", + "prefixLen": 24, + "network": "10.0.1.0/24", + "metric": 3, + "weight": 0, + "peerId": "172.16.1.3", + "path": "65003", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.3", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.103.0.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.103.0.0", + "prefixLen": 24, + "network": "10.103.0.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.3", + "path": "65003", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.3", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.103.1.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.103.1.0", + "prefixLen": 24, + "network": "10.103.1.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.3", + "path": "65003", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.3", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.103.2.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.103.2.0", + "prefixLen": 24, + "network": "10.103.2.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.3", + "path": "65003", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.3", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.103.3.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.103.3.0", + "prefixLen": 24, + "network": "10.103.3.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.3", + "path": "65003", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.3", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.103.4.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.103.4.0", + "prefixLen": 24, + "network": "10.103.4.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.3", + "path": "65003", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.3", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.103.5.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.103.5.0", + "prefixLen": 24, + "network": "10.103.5.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.3", + "path": "65003", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.3", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.103.6.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.103.6.0", + "prefixLen": 24, + "network": "10.103.6.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.3", + "path": "65003", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.3", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.103.7.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.103.7.0", + "prefixLen": 24, + "network": "10.103.7.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.3", + "path": "65003", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.3", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.103.8.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.103.8.0", + "prefixLen": 24, + "network": "10.103.8.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.3", + "path": "65003", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.3", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.103.9.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.103.9.0", + "prefixLen": 24, + "network": "10.103.9.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.3", + "path": "65003", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.3", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.104.0.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.104.0.0", + "prefixLen": 24, + "network": "10.104.0.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.4", + "path": "65004", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.4", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.104.1.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.104.1.0", + "prefixLen": 24, + "network": "10.104.1.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.4", + "path": "65004", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.4", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.104.2.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.104.2.0", + "prefixLen": 24, + "network": "10.104.2.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.4", + "path": "65004", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.4", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.104.3.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.104.3.0", + "prefixLen": 24, + "network": "10.104.3.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.4", + "path": "65004", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.4", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.104.4.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.104.4.0", + "prefixLen": 24, + "network": "10.104.4.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.4", + "path": "65004", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.4", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.104.5.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.104.5.0", + "prefixLen": 24, + "network": "10.104.5.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.4", + "path": "65004", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.4", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.104.6.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.104.6.0", + "prefixLen": 24, + "network": "10.104.6.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.4", + "path": "65004", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.4", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.104.7.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.104.7.0", + "prefixLen": 24, + "network": "10.104.7.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.4", + "path": "65004", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.4", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.104.8.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.104.8.0", + "prefixLen": 24, + "network": "10.104.8.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.4", + "path": "65004", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.4", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.104.9.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.104.9.0", + "prefixLen": 24, + "network": "10.104.9.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.4", + "path": "65004", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.4", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_multiview_topo1/r1/view_3.json b/tests/topotests/bgp_multiview_topo1/r1/view_3.json new file mode 100644 index 0000000..d49694e --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/r1/view_3.json @@ -0,0 +1,728 @@ +{ + "vrfName": "3", + "routerId": "172.30.1.1", + "defaultLocPrf": 100, + "localAS": 100, + "routes": { + "10.0.1.0/24": [ + { + "valid": true, + "pathFrom": "external", + "prefix": "10.0.1.0", + "prefixLen": 24, + "network": "10.0.1.0/24", + "metric": 8, + "weight": 0, + "peerId": "172.16.1.8", + "path": "65008", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.8", + "afi": "ipv4", + "used": true + } + ] + }, + { + "valid": true, + "pathFrom": "external", + "prefix": "10.0.1.0", + "prefixLen": 24, + "network": "10.0.1.0/24", + "metric": 7, + "weight": 0, + "peerId": "172.16.1.7", + "path": "65007", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.7", + "afi": "ipv4", + "used": true + } + ] + }, + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.0.1.0", + "prefixLen": 24, + "network": "10.0.1.0/24", + "metric": 6, + "weight": 0, + "peerId": "172.16.1.6", + "path": "65006", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.6", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.106.0.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.106.0.0", + "prefixLen": 24, + "network": "10.106.0.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.6", + "path": "65006", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.6", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.106.1.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.106.1.0", + "prefixLen": 24, + "network": "10.106.1.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.6", + "path": "65006", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.6", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.106.2.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.106.2.0", + "prefixLen": 24, + "network": "10.106.2.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.6", + "path": "65006", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.6", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.106.3.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.106.3.0", + "prefixLen": 24, + "network": "10.106.3.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.6", + "path": "65006", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.6", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.106.4.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.106.4.0", + "prefixLen": 24, + "network": "10.106.4.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.6", + "path": "65006", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.6", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.106.5.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.106.5.0", + "prefixLen": 24, + "network": "10.106.5.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.6", + "path": "65006", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.6", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.106.6.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.106.6.0", + "prefixLen": 24, + "network": "10.106.6.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.6", + "path": "65006", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.6", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.106.7.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.106.7.0", + "prefixLen": 24, + "network": "10.106.7.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.6", + "path": "65006", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.6", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.106.8.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.106.8.0", + "prefixLen": 24, + "network": "10.106.8.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.6", + "path": "65006", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.6", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.106.9.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.106.9.0", + "prefixLen": 24, + "network": "10.106.9.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.6", + "path": "65006", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.6", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.107.0.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.107.0.0", + "prefixLen": 24, + "network": "10.107.0.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.7", + "path": "65007", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.7", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.107.1.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.107.1.0", + "prefixLen": 24, + "network": "10.107.1.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.7", + "path": "65007", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.7", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.107.2.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.107.2.0", + "prefixLen": 24, + "network": "10.107.2.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.7", + "path": "65007", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.7", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.107.3.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.107.3.0", + "prefixLen": 24, + "network": "10.107.3.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.7", + "path": "65007", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.7", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.107.4.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.107.4.0", + "prefixLen": 24, + "network": "10.107.4.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.7", + "path": "65007", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.7", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.107.5.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.107.5.0", + "prefixLen": 24, + "network": "10.107.5.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.7", + "path": "65007", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.7", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.107.6.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.107.6.0", + "prefixLen": 24, + "network": "10.107.6.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.7", + "path": "65007", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.7", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.107.7.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.107.7.0", + "prefixLen": 24, + "network": "10.107.7.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.7", + "path": "65007", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.7", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.107.8.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.107.8.0", + "prefixLen": 24, + "network": "10.107.8.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.7", + "path": "65007", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.7", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.107.9.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.107.9.0", + "prefixLen": 24, + "network": "10.107.9.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.7", + "path": "65007", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.7", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.108.0.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.108.0.0", + "prefixLen": 24, + "network": "10.108.0.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.8", + "path": "65008", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.8", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.108.1.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.108.1.0", + "prefixLen": 24, + "network": "10.108.1.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.8", + "path": "65008", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.8", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.108.2.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.108.2.0", + "prefixLen": 24, + "network": "10.108.2.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.8", + "path": "65008", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.8", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.108.3.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.108.3.0", + "prefixLen": 24, + "network": "10.108.3.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.8", + "path": "65008", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.8", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.108.4.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.108.4.0", + "prefixLen": 24, + "network": "10.108.4.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.8", + "path": "65008", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.8", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.108.5.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.108.5.0", + "prefixLen": 24, + "network": "10.108.5.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.8", + "path": "65008", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.8", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.108.6.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.108.6.0", + "prefixLen": 24, + "network": "10.108.6.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.8", + "path": "65008", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.8", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.108.7.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.108.7.0", + "prefixLen": 24, + "network": "10.108.7.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.8", + "path": "65008", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.8", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.108.8.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.108.8.0", + "prefixLen": 24, + "network": "10.108.8.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.8", + "path": "65008", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.8", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.108.9.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "10.108.9.0", + "prefixLen": 24, + "network": "10.108.9.0/24", + "metric": 100, + "weight": 0, + "peerId": "172.16.1.8", + "path": "65008", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.16.1.8", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_multiview_topo1/r1/zebra.conf b/tests/topotests/bgp_multiview_topo1/r1/zebra.conf new file mode 100644 index 0000000..86343f5 --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/r1/zebra.conf @@ -0,0 +1,23 @@ +! +! Zebra configuration saved from vty +! 2015/12/24 16:48:27 +! +log file zebra.log +! +hostname r1 +! +interface r1-stub + description Stub Network + ip address 172.20.0.1/28 + no link-detect +! +interface r1-eth0 + description to PE router - vlan1 + ip address 172.16.1.254/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py b/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py new file mode 100644 index 0000000..2f3421d --- /dev/null +++ b/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_multiview_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2016 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +r""" +test_bgp_multiview_topo1.py: Simple FRR Route-Server Test + ++----------+ +----------+ +----------+ +----------+ +----------+ +| peer1 | | peer2 | | peer3 | | peer4 | | peer5 | +| AS 65001 | | AS 65002 | | AS 65003 | | AS 65004 | | AS 65005 | ++-----+----+ +-----+----+ +-----+----+ +-----+----+ +-----+----+ + | .1 | .2 | .3 | .4 | .5 + | ______/ / / _________/ + \ / ________________/ / / + | | / _________________________/ / +----------+ + | | | / __________________________/ ___| peer6 | + | | | | / ____________________________/.6 | AS 65006 | + | | | | | / _________________________ +----------+ + | | | | | | / __________________ \ +----------+ + | | | | | | | / \ \___| peer7 | + | | | | | | | | \ .7 | AS 65007 | + ~~~~~~~~~~~~~~~~~~~~~ \ +----------+ + ~~ SW1 ~~ \ +----------+ + ~~ Switch ~~ \_____| peer8 | + ~~ 172.16.1.0/24 ~~ .8 | AS 65008 | + ~~~~~~~~~~~~~~~~~~~~~ +----------+ + | + | .254 + +---------+---------+ + | FRR R1 | + | BGP Multi-View | + | Peer 1-3 > View 1 | + | Peer 4-5 > View 2 | + | Peer 6-8 > View 3 | + +---------+---------+ + | .1 + | + ~~~~~~~~~~~~~ Stub Network is redistributed + ~~ SW0 ~~ into each BGP view with different + ~~ 172.20.0.1/28 ~~ attributes (using route-map) + ~~ Stub Switch ~~ + ~~~~~~~~~~~~~ +""" + +import json +import os +import sys +import pytest +import json +from time import sleep + +from functools import partial + +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from lib import topotest +from lib.topogen import get_topogen, Topogen +from lib.common_config import step + + +pytestmark = [pytest.mark.bgpd] + + +fatal_error = "" + + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +def build_topo(tgen): + # Setup Routers + router = tgen.add_router("r1") + + # Setup Provider BGP peers + peer = {} + for i in range(1, 9): + peer[i] = tgen.add_exabgp_peer( + "peer%s" % i, ip="172.16.1.%s/24" % i, defaultRoute="via 172.16.1.254" + ) + + # First switch is for a dummy interface (for local network) + switch = tgen.add_switch("sw0") + switch.add_link(router, nodeif="r1-stub") + + # Second switch is for connection to all peering routers + switch = tgen.add_switch("sw1") + switch.add_link(router, nodeif="r1-eth0") + for j in range(1, 9): + switch.add_link(peer[j], nodeif="peer%s-eth0" % j) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + thisDir = os.path.dirname(os.path.realpath(__file__)) + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + # Starting Routers + router = tgen.net["r1"] + router.loadConf("zebra", "%s/r1/zebra.conf" % thisDir) + router.loadConf("bgpd", "%s/r1/bgpd.conf" % thisDir) + tgen.gears["r1"].start() + + # Starting PE Hosts and init ExaBGP on each of them + peer_list = tgen.exabgp_peers() + for pname, peer in peer_list.items(): + peer_dir = os.path.join(thisDir, pname) + env_file = os.path.join(thisDir, "exabgp.env") + peer.start(peer_dir, env_file) + + +def teardown_module(module): + tgen = get_topogen() + tgen.stop_topology() + + +def test_router_running(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + +def test_bgp_converge(): + "Check for BGP converged on all peers and BGP views" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Wait for BGP to converge (All Neighbors in either Full or TwoWay State) + step("Verify for BGP to converge") + + timeout = 125 + while timeout > 0: + print("Timeout in %s: " % timeout), + sys.stdout.flush() + # Look for any node not yet converged + for i in range(1, 2): + for view in range(1, 4): + notConverged = tgen.net["r%s" % i].cmd( + r'vtysh -c "show ip bgp view %s summary" 2> /dev/null | grep ^[0-9] | grep -vP " 11\s+(\d+)"' + % view + ) + if notConverged: + print("Waiting for r%s, view %s" % (i, view)) + sys.stdout.flush() + break + if notConverged: + break + if notConverged: + sleep(5) + timeout -= 5 + else: + print("Done") + break + else: + # Bail out with error if a router fails to converge + bgpStatus = tgen.net["r%s" % i].cmd( + 'vtysh -c "show ip bgp view %s summary"' % view + ) + assert False, "BGP did not converge:\n%s" % bgpStatus + + tgen.routers_have_failure() + + +def test_bgp_routingTable(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + step("Verifying BGP Routing Tables") + + router = tgen.gears["r1"] + for view in range(1, 4): + json_file = "{}/{}/view_{}.json".format(thisDir, router.name, view) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip bgp view {} json".format(view), + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=5, wait=1) + assertmsg = "Routing Table verification failed for router {}, view {}".format( + router.name, view + ) + assert result is None, assertmsg + + tgen.routers_have_failure() + + +def test_shutdown_check_memleak(): + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli + # retval = pytest.main(["-s", "--tb=no"]) + retval = pytest.main(["-s"]) + sys.exit(retval) diff --git a/tests/topotests/bgp_node_target_extcommunities/__init__.py b/tests/topotests/bgp_node_target_extcommunities/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf new file mode 100644 index 0000000..8698338 --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf @@ -0,0 +1,21 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +router bgp 65001 + bgp router-id 192.168.1.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.3 remote-as external + neighbor 192.168.1.4 remote-as external + address-family ipv4 unicast + network 10.10.10.10/32 + neighbor 192.168.1.2 route-map rmap out + neighbor 192.168.1.3 route-map rmap out + neighbor 192.168.1.4 route-map rmap out + exit-address-family +! +route-map rmap permit 10 + set extcommunity nt 192.168.1.3:0 192.168.1.4:0 +exit diff --git a/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf new file mode 100644 index 0000000..09fda78 --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf @@ -0,0 +1,8 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! +router bgp 65002 + bgp router-id 192.168.1.2 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external diff --git a/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf new file mode 100644 index 0000000..4883f1f --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf @@ -0,0 +1,8 @@ +! +int r3-eth0 + ip address 192.168.1.3/24 +! +router bgp 65003 + bgp router-id 192.168.1.3 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external diff --git a/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf new file mode 100644 index 0000000..f518bd1 --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf @@ -0,0 +1,8 @@ +! +int r4-eth0 + ip address 192.168.1.4/24 +! +router bgp 65004 + bgp router-id 192.168.1.4 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external diff --git a/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py b/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py new file mode 100644 index 0000000..23e820b --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if Node Target Extended Communities works. + +At r1 we set NT to 192.168.1.3 and 192.168.1.4 (this is the R3/R4 router-id), +and that means 10.10.10.10/32 MUST be installed on R3 and R4, but not on R2, +because this route does not have NT:192.168.1.2. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_node_target_extended_communities(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + r4 = tgen.gears["r4"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp summary json")) + expected = { + "ipv4Unicast": { + "peers": { + "192.168.1.2": { + "pfxSnt": 1, + "state": "Established", + }, + "192.168.1.3": { + "pfxSnt": 1, + "state": "Established", + }, + "192.168.1.4": { + "pfxSnt": 1, + "state": "Established", + }, + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed announcing 10.10.10.10/32 to r2, r3, and r4" + + def _bgp_check_route(router, exists): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) + if exists: + expected = { + "routes": { + "10.10.10.10/32": [ + { + "valid": True, + } + ] + } + } + else: + expected = { + "routes": { + "10.10.10.10/32": None, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_route, r3, True) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.10/32 is not installed, but SHOULD be" + + test_func = functools.partial(_bgp_check_route, r4, True) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.10/32 is not installed, but SHOULD be" + + test_func = functools.partial(_bgp_check_route, r2, False) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.10/32 is installed, but SHOULD NOT be" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_orf/__init__.py b/tests/topotests/bgp_orf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_orf/r1/bgpd.conf b/tests/topotests/bgp_orf/r1/bgpd.conf new file mode 100644 index 0000000..800bf1b --- /dev/null +++ b/tests/topotests/bgp_orf/r1/bgpd.conf @@ -0,0 +1,9 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + address-family ipv4 unicast + redistribute connected + neighbor 192.168.1.2 capability orf prefix-list both + exit-address-family +! diff --git a/tests/topotests/bgp_orf/r1/zebra.conf b/tests/topotests/bgp_orf/r1/zebra.conf new file mode 100644 index 0000000..85ab531 --- /dev/null +++ b/tests/topotests/bgp_orf/r1/zebra.conf @@ -0,0 +1,8 @@ +! +int lo + ip address 10.10.10.1/32 + ip address 10.10.10.2/32 +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_orf/r2/bgpd.conf b/tests/topotests/bgp_orf/r2/bgpd.conf new file mode 100644 index 0000000..8c488aa --- /dev/null +++ b/tests/topotests/bgp_orf/r2/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + address-family ipv4 unicast + neighbor 192.168.1.1 capability orf prefix-list both + neighbor 192.168.1.1 prefix-list r1 in + exit-address-family +! +ip prefix-list r1 seq 5 permit 10.10.10.1/32 diff --git a/tests/topotests/bgp_orf/r2/zebra.conf b/tests/topotests/bgp_orf/r2/zebra.conf new file mode 100644 index 0000000..cffe827 --- /dev/null +++ b/tests/topotests/bgp_orf/r2/zebra.conf @@ -0,0 +1,4 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_orf/test_bgp_orf.py b/tests/topotests/bgp_orf/test_bgp_orf.py new file mode 100644 index 0000000..7f45a24 --- /dev/null +++ b/tests/topotests/bgp_orf/test_bgp_orf.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Test if BGP ORF filtering is working correctly when modifying +prefix-list. + +Initially advertise 10.10.10.1/32 from R1 to R2. Add new prefix +10.10.10.2/32 to r1 prefix list on R2. Test if we updated ORF +prefix-list correctly. +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_orf(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge_r1(): + output = json.loads( + r1.vtysh_cmd( + "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json" + ) + ) + expected = {"advertisedRoutes": {"10.10.10.1/32": {}, "10.10.10.2/32": None}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge_r1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't apply ORF from R1 to R2" + + def _bgp_converge_r2(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast summary json")) + expected = { + "peers": { + "192.168.1.1": { + "pfxRcd": 1, + "pfxSnt": 1, + "state": "Established", + "peerState": "OK", + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge_r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "ORF filtering is not working from R1 to R2" + + r2.vtysh_cmd( + """ + configure terminal + ip prefix-list r1 seq 10 permit 10.10.10.2/32 + """ + ) + + def _bgp_orf_changed_r1(): + output = json.loads( + r1.vtysh_cmd( + "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json" + ) + ) + expected = {"advertisedRoutes": {"10.10.10.1/32": {}, "10.10.10.2/32": {}}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_orf_changed_r1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't apply new ORF from R1 to R2" + + def _bgp_orf_changed_r2(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "10.10.10.1/32": [{"valid": True}], + "10.10.10.2/32": [{"valid": True}], + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_orf_changed_r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "New ORF filtering is not working from R1 to R2" + + r2.vtysh_cmd( + """ + configure terminal + no ip prefix-list r1 seq 10 permit 10.10.10.2/32 + """ + ) + + test_func = functools.partial(_bgp_converge_r1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't apply initial ORF from R1 to R2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_path_attribute_discard/__init__.py b/tests/topotests/bgp_path_attribute_discard/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_path_attribute_discard/exabgp.env b/tests/topotests/bgp_path_attribute_discard/exabgp.env new file mode 100644 index 0000000..28e6423 --- /dev/null +++ b/tests/topotests/bgp_path_attribute_discard/exabgp.env @@ -0,0 +1,53 @@ +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' +##daemonize = false + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg b/tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg new file mode 100644 index 0000000..7fb9210 --- /dev/null +++ b/tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg @@ -0,0 +1,24 @@ +neighbor 10.0.0.1 { + router-id 10.0.0.2; + local-address 10.0.0.2; + local-as 65001; + peer-as 65002; + + capability { + route-refresh; + } + + static { + route 192.168.100.101/32 { + atomic-aggregate; + community 65001:101; + next-hop 10.0.0.2; + } + + route 192.168.100.102/32 { + originator-id 10.0.0.2; + community 65001:102; + next-hop 10.0.0.2; + } + } +} diff --git a/tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf b/tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf new file mode 100644 index 0000000..c96f354 --- /dev/null +++ b/tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf @@ -0,0 +1,6 @@ +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 10.0.0.2 remote-as external + neighbor 10.0.0.2 timers 3 10 +! diff --git a/tests/topotests/bgp_path_attribute_discard/r1/zebra.conf b/tests/topotests/bgp_path_attribute_discard/r1/zebra.conf new file mode 100644 index 0000000..51a1b26 --- /dev/null +++ b/tests/topotests/bgp_path_attribute_discard/r1/zebra.conf @@ -0,0 +1,4 @@ +! +interface r1-eth0 + ip address 10.0.0.1/24 +! diff --git a/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py b/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py new file mode 100644 index 0000000..c97cd0b --- /dev/null +++ b/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Test if `neighbor path-attribute discard` command works correctly, +can discard unwanted attributes from UPDATE messages, and ignore them +by continuing to process UPDATE messages. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + r1 = tgen.add_router("r1") + peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.2", defaultRoute="via 10.0.0.1") + + switch = tgen.add_switch("s1") + switch.add_link(r1) + switch.add_link(peer1) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router = tgen.gears["r1"] + router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf")) + router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf")) + router.start() + + peer = tgen.gears["peer1"] + peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env")) + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_path_attribute_discard(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json detail")) + expected = { + "routes": { + "192.168.100.101/32": { + "paths": [ + { + "valid": True, + "atomicAggregate": True, + "community": { + "string": "65001:101", + }, + } + ], + }, + "192.168.100.102/32": { + "paths": [ + { + "valid": True, + "originatorId": None, + "community": { + "string": "65001:102", + }, + } + ], + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed bgp convergence" + + step("Discard atomic-aggregate, and community attributes from peer1") + r1.vtysh_cmd( + """ + configure terminal + router bgp + neighbor 10.0.0.2 path-attribute discard 6 8 + """ + ) + + def _bgp_check_if_attributes_discarded(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json detail")) + expected = { + "routes": { + "192.168.100.101/32": { + "paths": [ + { + "valid": True, + "atomicAggregate": None, + "community": None, + } + ], + }, + "192.168.100.102/32": { + "paths": [ + { + "valid": True, + "originatorId": None, + "community": None, + } + ], + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_if_attributes_discarded) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert ( + result is None + ), "Failed to discard path attributes (atomic-aggregate, community)" + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/__init__.py b/tests/topotests/bgp_path_attribute_treat_as_withdraw/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/bgpd.conf b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/bgpd.conf new file mode 100644 index 0000000..4286b98 --- /dev/null +++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/bgpd.conf @@ -0,0 +1,14 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.0.2 remote-as external + neighbor 10.0.0.2 timers 3 10 + address-family ipv4 unicast + network 10.10.10.10/32 route-map atomic + network 10.10.10.20/32 + exit-address-family +! +route-map atomic permit 10 + set atomic-aggregate +! diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/zebra.conf b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/zebra.conf new file mode 100644 index 0000000..51a1b26 --- /dev/null +++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/zebra.conf @@ -0,0 +1,4 @@ +! +interface r1-eth0 + ip address 10.0.0.1/24 +! diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/bgpd.conf b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/bgpd.conf new file mode 100644 index 0000000..2e63fd8 --- /dev/null +++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/bgpd.conf @@ -0,0 +1,6 @@ +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 10.0.0.1 remote-as external + neighbor 10.0.0.1 timers 3 10 +! diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/zebra.conf b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/zebra.conf new file mode 100644 index 0000000..12d3731 --- /dev/null +++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/zebra.conf @@ -0,0 +1,4 @@ +! +interface r2-eth0 + ip address 10.0.0.2/24 +! diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/test_bgp_path_attribute_treat_as_withdraw.py b/tests/topotests/bgp_path_attribute_treat_as_withdraw/test_bgp_path_attribute_treat_as_withdraw.py new file mode 100644 index 0000000..a9d678a --- /dev/null +++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/test_bgp_path_attribute_treat_as_withdraw.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Test if `neighbor path-attribute treat-as-withdraw` command works correctly, +can withdraw unwanted prefixes from BGP table. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + r1 = tgen.add_router("r1") + r2 = tgen.add_router("r2") + + switch = tgen.add_switch("s1") + switch.add_link(r1) + switch.add_link(r2) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + r1 = tgen.gears["r1"] + r1.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf")) + r1.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf")) + r1.start() + + r2 = tgen.gears["r2"] + r2.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r2/zebra.conf")) + r2.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r2/bgpd.conf")) + r2.start() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_path_attribute_treat_as_withdraw(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast json detail")) + expected = { + "routes": { + "10.10.10.10/32": { + "paths": [ + { + "valid": True, + "atomicAggregate": True, + } + ], + }, + "10.10.10.20/32": { + "paths": [ + { + "valid": True, + } + ], + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed bgp convergence" + + step("Withdraw prefixes with atomic-aggregate from r1") + r2.vtysh_cmd( + """ + configure terminal + router bgp + neighbor 10.0.0.1 path-attribute treat-as-withdraw 6 + """ + ) + + def _bgp_check_if_route_withdrawn(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast json detail")) + expected = { + "routes": { + "10.10.10.10/32": None, + "10.10.10.20/32": { + "paths": [ + { + "valid": True, + } + ], + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_if_route_withdrawn) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to withdraw prefixes with atomic-aggregate attribute" + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_path_attributes_topo1/__init__.py b/tests/topotests/bgp_path_attributes_topo1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_path_attributes_topo1/bgp_path_attributes.json b/tests/topotests/bgp_path_attributes_topo1/bgp_path_attributes.json new file mode 100644 index 0000000..de2bffa --- /dev/null +++ b/tests/topotests/bgp_path_attributes_topo1/bgp_path_attributes.json @@ -0,0 +1,363 @@ +{ + "ipv4base":"10.0.0.0", + "ipv4mask":30, + "ipv6base":"fd00::", + "ipv6mask":64, + "link_ip_start":{"ipv4":"10.0.0.0", "v4mask":30, "ipv6":"fd00::", "v6mask":64}, + "lo_prefix":{"ipv4":"1.0.", "v4mask":32, "ipv6":"2001:DB8:F::", "v6mask":128}, + "routers":{ + "r1":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2":{"ipv4":"auto", "ipv6":"auto"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + } + ] + }, + "bgp":{ + "local_as":"555", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp":{ + "local_as":"555", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + } + }, + "r3":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1":{"ipv4":"auto", "ipv6":"auto"}, + "r2":{"ipv4":"auto", "ipv6":"auto"}, + "r5":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"555", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r6": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "666", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": {} + } + }, + "r6": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": {} + } + }, + "r6": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + }, + "r5":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r7": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp":{ + "local_as":"666", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + }, + "r7": { + "dest_link": { + "r5": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + }, + "r7": { + "dest_link": { + "r5": {} + } + } + } + } + } + } + } + }, + "r6":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r4": {"ipv4": "auto", "ipv6": "auto"}, + "r7": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp":{ + "local_as":"777", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": {} + } + }, + "r7": { + "dest_link": { + "r6": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": {} + } + }, + "r7": { + "dest_link": { + "r6": {} + } + } + } + } + } + } + } + }, + "r7":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r5": {"ipv4": "auto", "ipv6": "auto"}, + "r6": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp":{ + "local_as":"888", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r7": {} + } + }, + "r6": { + "dest_link": { + "r7": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r7": {} + } + }, + "r6": { + "dest_link": { + "r7": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_path_attributes_topo1/test_bgp_path_attributes.py b/tests/topotests/bgp_path_attributes_topo1/test_bgp_path_attributes.py new file mode 100644 index 0000000..df39032 --- /dev/null +++ b/tests/topotests/bgp_path_attributes_topo1/test_bgp_path_attributes.py @@ -0,0 +1,1528 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Modified work Copyright (c) 2019 by VMware, Inc. ("VMware") +# Original work Copyright (c) 2018 by Network Device Education +# Foundation, Inc. ("NetDEF") +# + +""" +Following tests are covered to test AS-Path functionality: + +Setup module: +- Create topology (setup module) +- Bring up topology +- Verify BGP convergence + +Test cases: +1. Test next_hop attribute and verify best path is installed as per + reachable next_hop +2. Test aspath attribute and verify best path is installed as per + shortest AS-Path +3. Test localpref attribute and verify best path is installed as per + shortest local-preference +4. Test weight attribute and and verify best path is installed as per + highest weight +5. Test origin attribute and verify best path is installed as per + IGP>EGP>INCOMPLETE rule +6. Test med attribute and verify best path is installed as per lowest + med value +7. Test admin distance and verify best path is installed as per lowest + admin distance + +Teardown module: +- Bring down the topology +- stop routers + +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +# Required to instantiate the topology builder class. +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + create_prefix_lists, + create_route_maps, + check_address_types, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_best_path_as_per_bgp_attribute, + verify_best_path_as_per_admin_distance, +) +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +# Address read from env variables +ADDR_TYPES = check_address_types() + +#### +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: %s", testsuite_run_time) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_path_attributes.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Checking BGP convergence + result = verify_bgp_convergence(tgen, topo) + assert result is True, "setup_module :Failed \n Error:" " {}".format(result) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info("Testsuite end time: %s", time.asctime(time.localtime(time.time()))) + logger.info("=" * 40) + + +##################################################### +## +## Testcases +## +##################################################### + + +def test_next_hop_attribute(request): + """ + Verifying route are not getting installed in, as next_hop is + unreachable, Making next hop reachable using next_hop_self + command and verifying routes are installed. + """ + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict = { + "r7": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"}, + {"network": "200.60.2.0/32"}, + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "200:50:2::/128"}, + {"network": "200:60:2::/128"}, + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r1" + protocol = "bgp" + # Verification should fail as nexthop-self is not enabled + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "{} routes are not present in RIB".format( + addr_type, tc_name + ) + + # Configure next-hop-self to bgp neighbor + input_dict_1 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_aspath_attribute(request): + "Verifying AS_PATH attribute functionality" + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict = { + "r7": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"}, + {"network": "200.60.2.0/32"}, + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "200:50:2::/128"}, + {"network": "200:60:2::/128"}, + ] + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "path" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Modify AS-Path and verify best path is changed + # Create Prefix list + + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_ls_1_ipv4": [ + { + "seqid": 10, + "network": "200.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + }, + "ipv6": { + "pf_ls_1_ipv6": [ + { + "seqid": 10, + "network": "200::/8", + "le": "128", + "action": "permit", + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + input_dict_3 = { + "r3": { + "route_maps": { + "RMAP_AS_PATH": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_ls_1_ipv4"}}, + "set": {"path": {"as_num": "111 222", "as_action": "prepend"}}, + }, + { + "action": "permit", + "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}}, + "set": {"path": {"as_num": "111 222", "as_action": "prepend"}}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "RMAP_AS_PATH", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "RMAP_AS_PATH", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "path" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_localpref_attribute(request): + "Verifying LOCAL PREFERENCE attribute functionality" + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict = { + "r7": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"}, + {"network": "200.60.2.0/32"}, + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "200:50:2::/128"}, + {"network": "200:60:2::/128"}, + ] + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create Prefix list + input_dict_2 = { + "r2": { + "prefix_lists": { + "ipv4": { + "pf_ls_1_ipv4": [ + { + "seqid": 10, + "network": "200.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + }, + "ipv6": { + "pf_ls_1_ipv6": [ + { + "seqid": 10, + "network": "200::/8", + "le": "128", + "action": "permit", + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + input_dict_3 = { + "r2": { + "route_maps": { + "RMAP_LOCAL_PREF": [ + { + "action": "permit", + "seq_id": "10", + "match": {"ipv4": {"prefix_lists": "pf_ls_1_ipv4"}}, + "set": {"locPrf": 1111}, + }, + { + "action": "permit", + "seq_id": "20", + "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}}, + "set": {"locPrf": 1111}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "RMAP_LOCAL_PREF", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "RMAP_LOCAL_PREF", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "locPrf" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Modify route map + input_dict_3 = { + "r2": { + "route_maps": { + "RMAP_LOCAL_PREF": [ + { + "action": "permit", + "seq_id": "10", + "match": {"ipv4": {"prefix_lists": "pf_ls_1_ipv4"}}, + "set": {"locPrf": 50}, + }, + { + "action": "permit", + "seq_id": "20", + "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}}, + "set": {"locPrf": 50}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "locPrf" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_weight_attribute(request): + """ + Test configure/modify weight attribute and + verify best path is installed as per highest weight + """ + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict = { + "r7": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"}, + {"network": "200.60.2.0/32"}, + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "200:50:2::/128"}, + {"network": "200:60:2::/128"}, + ] + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create Prefix list + input_dict_2 = { + "r1": { + "prefix_lists": { + "ipv4": { + "pf_ls_1_ipv4": [ + { + "seqid": 10, + "network": "200.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + }, + "ipv6": { + "pf_ls_1_ipv6": [ + { + "seqid": 10, + "network": "200::/8", + "le": "128", + "action": "permit", + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + input_dict_3 = { + "r1": { + "route_maps": { + "RMAP_WEIGHT": [ + { + "action": "permit", + "seq_id": "5", + "match": {"ipv4": {"prefix_lists": "pf_ls_1_ipv4"}}, + "set": {"weight": 500}, + }, + { + "action": "permit", + "seq_id": "10", + "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}}, + "set": {"weight": 500}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "RMAP_WEIGHT", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "RMAP_WEIGHT", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "weight" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Modify route map + input_dict_3 = { + "r1": { + "route_maps": { + "RMAP_WEIGHT": [ + { + "action": "permit", + "seq_id": "5", + "match": {"ipv4": {"prefix_lists": "pf_ls_1_ipv4"}}, + "set": {"weight": 1000}, + }, + { + "action": "permit", + "seq_id": "10", + "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}}, + "set": {"weight": 1000}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "weight" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_origin_attribute(request): + """ + Test origin attribute and verify best path is + installed as per IGP>EGP>INCOMPLETE rule + """ + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"}, + {"network": "200.60.2.0/32"}, + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "200:50:2::/128"}, + {"network": "200:60:2::/128"}, + ] + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + } + } + }, + "r5": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to create static routes + input_dict_3 = { + "r5": { + "static_routes": [ + {"network": "200.50.2.0/32", "next_hop": "Null0"}, + {"network": "200.60.2.0/32", "next_hop": "Null0"}, + {"network": "200:50:2::/128", "next_hop": "Null0"}, + {"network": "200:60:2::/128", "next_hop": "Null0"}, + ] + } + } + result = create_static_routes(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "origin" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, {"r4": input_dict["r4"]}, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_med_attribute(request): + """ + Test configure/modify MED attribute and verify best path + is installed as per lowest med value + """ + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"}, + {"network": "200.60.2.0/32"}, + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "200:50:2::/128"}, + {"network": "200:60:2::/128"}, + ] + } + }, + } + } + }, + "r5": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"}, + {"network": "200.60.2.0/32"}, + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "200:50:2::/128"}, + {"network": "200:60:2::/128"}, + ] + } + }, + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create Prefix list + input_dict_2 = { + "r2": { + "prefix_lists": { + "ipv4": { + "pf_ls_r2_ipv4": [ + { + "seqid": 10, + "network": "200.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + }, + "ipv6": { + "pf_ls_r2_ipv6": [ + { + "seqid": 20, + "network": "200::/8", + "le": "128", + "action": "permit", + } + ] + }, + } + }, + "r3": { + "prefix_lists": { + "ipv4": { + "pf_ls_r3_ipv4": [ + { + "seqid": 10, + "network": "200.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + }, + "ipv6": { + "pf_ls_r3_ipv6": [ + { + "seqid": 20, + "network": "200::/8", + "le": "128", + "action": "permit", + } + ] + }, + } + }, + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + input_dict_3 = { + "r2": { + "route_maps": { + "RMAP_MED_R2": [ + { + "action": "permit", + "seq_id": "10", + "match": {"ipv4": {"prefix_lists": "pf_ls_r2_ipv4"}}, + "set": {"metric": 100}, + }, + { + "action": "permit", + "seq_id": "20", + "match": {"ipv6": {"prefix_lists": "pf_ls_r2_ipv6"}}, + "set": {"metric": 100}, + }, + ] + } + }, + "r3": { + "route_maps": { + "RMAP_MED_R3": [ + { + "action": "permit", + "seq_id": "10", + "match": {"ipv4": {"prefix_lists": "pf_ls_r3_ipv4"}}, + "set": {"metric": 10}, + }, + { + "action": "permit", + "seq_id": "20", + "match": {"ipv6": {"prefix_lists": "pf_ls_r3_ipv6"}}, + "set": {"metric": 10}, + }, + ] + } + }, + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "RMAP_MED_R2", + "direction": "in", + } + ] + } + } + }, + "r1": {"dest_link": {"r2": {"next_hop_self": True}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "RMAP_MED_R2", + "direction": "in", + } + ] + } + } + }, + "r1": {"dest_link": {"r2": {"next_hop_self": True}}}, + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}}, + "r5": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "RMAP_MED_R3", + "direction": "in", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}}, + "r5": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "RMAP_MED_R3", + "direction": "in", + } + ] + } + } + }, + } + } + }, + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "metric" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Modify route-map to set med value + input_dict_3 = { + "r3": { + "route_maps": { + "RMAP_MED_R3": [ + { + "action": "permit", + "seq_id": "10", + "match": {"ipv4": {"prefix_lists": "pf_ls_r3_ipv4"}}, + "set": {"metric": 200}, + }, + { + "action": "permit", + "seq_id": "20", + "match": {"ipv6": {"prefix_lists": "pf_ls_r3_ipv6"}}, + "set": {"metric": 200}, + }, + ] + } + } + } + + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "metric" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_admin_distance(request): + "Verifying admin distance functionality" + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to create static routes + input_dict = { + "r2": { + "static_routes": [ + { + "network": "200.50.2.0/32", + "admin_distance": 80, + "next_hop": "10.0.0.14", + }, + { + "network": "200.50.2.0/32", + "admin_distance": 60, + "next_hop": "10.0.0.18", + }, + { + "network": "200:50:2::/128", + "admin_distance": 80, + "next_hop": "fd00::1", + }, + { + "network": "200:50:2::/128", + "admin_distance": 60, + "next_hop": "fd00::1", + }, + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "admin_distance" + + input_dict = { + "ipv4": { + "r2": { + "static_routes": [ + { + "network": "200.50.2.0/32", + "admin_distance": 80, + "next_hop": "10.0.0.14", + }, + { + "network": "200.50.2.0/32", + "admin_distance": 60, + "next_hop": "10.0.0.18", + }, + ] + } + }, + "ipv6": { + "r2": { + "static_routes": [ + { + "network": "200:50:2::/128", + "admin_distance": 80, + "next_hop": "fd00::1", + }, + { + "network": "200:50:2::/128", + "admin_distance": 60, + "next_hop": "fd00::1", + }, + ] + } + }, + } + + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_admin_distance( + tgen, addr_type, dut, input_dict[addr_type], attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_path_selection/__init__.py b/tests/topotests/bgp_path_selection/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_path_selection/r1/bgpd.conf b/tests/topotests/bgp_path_selection/r1/bgpd.conf new file mode 100644 index 0000000..08eb867 --- /dev/null +++ b/tests/topotests/bgp_path_selection/r1/bgpd.conf @@ -0,0 +1,28 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.0.2.2 remote-as 65002 + neighbor 192.0.2.2 timers 1 3 + neighbor 192.0.2.2 timers connect 1 + neighbor 192.0.2.2 ebgp-multihop 2 + neighbor 192.0.2.3 remote-as 65002 + neighbor 192.0.2.3 timers 1 3 + neighbor 192.0.2.3 timers connect 1 + neighbor 192.0.2.3 ebgp-multihop 2 + address-family ipv4 + redistribute connected + exit-address-family + address-family ipv4 vpn + neighbor 192.0.2.2 activate + neighbor 192.0.2.3 activate + exit-address-family +! +router bgp 65001 vrf vrf1 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + label vpn export 101 + rd vpn export 101:1 + rt vpn both 52:100 + import vpn + export vpn + exit-address-family +! \ No newline at end of file diff --git a/tests/topotests/bgp_path_selection/r1/ldpd.conf b/tests/topotests/bgp_path_selection/r1/ldpd.conf new file mode 100644 index 0000000..04ae068 --- /dev/null +++ b/tests/topotests/bgp_path_selection/r1/ldpd.conf @@ -0,0 +1,26 @@ +hostname r1 +log file ldpd.log +password zebra +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 192.0.2.1 + ! + address-family ipv4 + discovery transport-address 192.0.2.1 + ! + interface r1-eth0 + ! + interface r1-eth1 + ! + ! +! +line vty +! diff --git a/tests/topotests/bgp_path_selection/r1/staticd.conf b/tests/topotests/bgp_path_selection/r1/staticd.conf new file mode 100644 index 0000000..a37f60a --- /dev/null +++ b/tests/topotests/bgp_path_selection/r1/staticd.conf @@ -0,0 +1,2 @@ +ip route 192.0.2.2/32 192.168.1.2 +ip route 192.0.2.3/32 192.168.2.2 \ No newline at end of file diff --git a/tests/topotests/bgp_path_selection/r1/zebra.conf b/tests/topotests/bgp_path_selection/r1/zebra.conf new file mode 100644 index 0000000..e860d6e --- /dev/null +++ b/tests/topotests/bgp_path_selection/r1/zebra.conf @@ -0,0 +1,11 @@ +! +interface lo + ip address 192.0.2.1/32 + ip address 172.16.255.1/32 +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +interface r1-eth1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/bgp_path_selection/r2/bgpd.conf b/tests/topotests/bgp_path_selection/r2/bgpd.conf new file mode 100644 index 0000000..cc878d8 --- /dev/null +++ b/tests/topotests/bgp_path_selection/r2/bgpd.conf @@ -0,0 +1,25 @@ +router bgp 65002 + no bgp network import-check + network 192.0.2.8/32 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as 65001 + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + neighbor 192.168.1.1 ebgp-multihop 2 + neighbor 192.168.1.1 update-source 192.0.2.2 + address-family ipv4 vpn + neighbor 192.168.1.1 activate + exit-address-family +! +router bgp 65002 vrf vrf1 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + label vpn export 102 + rd vpn export 102:1 + rt vpn both 52:100 + import vpn + export vpn + exit-address-family +! \ No newline at end of file diff --git a/tests/topotests/bgp_path_selection/r2/ldpd.conf b/tests/topotests/bgp_path_selection/r2/ldpd.conf new file mode 100644 index 0000000..67973ab --- /dev/null +++ b/tests/topotests/bgp_path_selection/r2/ldpd.conf @@ -0,0 +1,26 @@ +hostname r2 +log file ldpd.log +password zebra +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 192.0.2.2 + ! + address-family ipv4 + discovery transport-address 192.0.2.2 + ! + interface r2-eth0 + ! + interface r2-eth1 + ! + ! +! +line vty +! diff --git a/tests/topotests/bgp_path_selection/r2/staticd.conf b/tests/topotests/bgp_path_selection/r2/staticd.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_path_selection/r2/zebra.conf b/tests/topotests/bgp_path_selection/r2/zebra.conf new file mode 100644 index 0000000..40dfa98 --- /dev/null +++ b/tests/topotests/bgp_path_selection/r2/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 192.0.2.2/32 +! +interface r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_path_selection/r3/bgpd.conf b/tests/topotests/bgp_path_selection/r3/bgpd.conf new file mode 100644 index 0000000..ca8d27a --- /dev/null +++ b/tests/topotests/bgp_path_selection/r3/bgpd.conf @@ -0,0 +1,25 @@ +router bgp 65002 + no bgp network import-check + network 192.0.2.8/32 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as 65001 + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 + neighbor 192.168.2.1 ebgp-multihop 2 + neighbor 192.168.1.1 update-source 192.0.2.3 + address-family ipv4 vpn + neighbor 192.168.2.1 activate + exit-address-family +! +router bgp 65002 vrf vrf1 + bgp router-id 192.0.2.3 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + label vpn export 103 + rd vpn export 102:1 + rt vpn both 52:100 + import vpn + export vpn + exit-address-family +! \ No newline at end of file diff --git a/tests/topotests/bgp_path_selection/r3/ldpd.conf b/tests/topotests/bgp_path_selection/r3/ldpd.conf new file mode 100644 index 0000000..b282b08 --- /dev/null +++ b/tests/topotests/bgp_path_selection/r3/ldpd.conf @@ -0,0 +1,24 @@ +hostname r3 +password zebra +log file ldpd.log +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 192.0.2.3 + ! + address-family ipv4 + discovery transport-address 192.0.2.3 + ! + interface r3-eth0 + ! + ! +! +line vty +! diff --git a/tests/topotests/bgp_path_selection/r3/staticd.conf b/tests/topotests/bgp_path_selection/r3/staticd.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_path_selection/r3/zebra.conf b/tests/topotests/bgp_path_selection/r3/zebra.conf new file mode 100644 index 0000000..a0473b6 --- /dev/null +++ b/tests/topotests/bgp_path_selection/r3/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 192.0.2.3/32 +! +interface r3-eth0 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/bgp_path_selection/test_bgp_path_selection.py b/tests/topotests/bgp_path_selection/test_bgp_path_selection.py new file mode 100644 index 0000000..bf5737b --- /dev/null +++ b/tests/topotests/bgp_path_selection/test_bgp_path_selection.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Louis Scalbert +# + +""" + +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for routern in range(1, 4): + tgen.gears["r{}".format(routern)].cmd("ip link add vrf1 type vrf table 10") + tgen.gears["r{}".format(routern)].cmd("ip link set vrf1 up") + tgen.gears["r{}".format(routern)].cmd("ip address add dev vrf1 {}.{}.{}.{}/32".format(routern, routern, routern,routern)) + tgen.gears["r2"].cmd("ip address add dev vrf1 192.0.2.8/32") + tgen.gears["r3"].cmd("ip address add dev vrf1 192.0.2.8/32") + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)) + ) + + tgen.start_router() + + tgen.gears["r1"].cmd("ip route add 192.0.2.2 via 192.168.1.2 metric 20") + tgen.gears["r1"].cmd("ip route add 192.0.2.3 via 192.168.2.2 metric 20") + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + +def test_bgp_path_selection_ecmp(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_check_path_selection_ecmp(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show bgp ipv4 unicast 192.0.2.8/32 json") + ) + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "65002"}, + "multipath": True, + "nexthops": [{"ip": "192.0.2.2", "metric": 20}], + }, + { + "valid": True, + "aspath": {"string": "65002"}, + "multipath": True, + "nexthops": [{"ip": "192.0.2.3", "metric": 20}], + } + ] + } + + return topotest.json_cmp(output, expected) + + step("Check if two ECMP paths are present") + test_func = functools.partial(_bgp_check_path_selection_ecmp) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefixes on R1" + + +def test_bgp_path_selection_vpn_ecmp(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_check_path_selection_vpn_ecmp(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 ipv4 unicast 192.0.2.8/32 json") + ) + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "65002"}, + "multipath": True, + "nexthops": [{"ip": "192.0.2.2", "metric": 20}], + }, + { + "valid": True, + "aspath": {"string": "65002"}, + "multipath": True, + "nexthops": [{"ip": "192.0.2.3", "metric": 20}], + } + ] + } + + return topotest.json_cmp(output, expected) + + step("Check if two ECMP paths are present") + test_func = functools.partial(_bgp_check_path_selection_vpn_ecmp) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefixes on R1" + + +def test_bgp_path_selection_metric(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_check_path_selection_metric(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show bgp ipv4 unicast 192.0.2.8/32 json") + ) + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "65002"}, + "nexthops": [{"ip": "192.0.2.2", "metric": 10}], + "bestpath":{ "selectionReason":"IGP Metric"}, + }, + { + "valid": True, + "aspath": {"string": "65002"}, + "nexthops": [{"ip": "192.0.2.3", "metric": 20}], + } + ] + } + + return topotest.json_cmp(output, expected) + + tgen.gears["r1"].cmd("ip route add 192.0.2.2 via 192.168.1.2 metric 10") + tgen.gears["r1"].cmd("ip route del 192.0.2.2 via 192.168.1.2 metric 20") + + step("Check if IGP metric best path is selected") + test_func = functools.partial(_bgp_check_path_selection_metric) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefixes on R1" + + +def test_bgp_path_selection_vpn_metric(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_check_path_selection_vpn_metric(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 ipv4 unicast 192.0.2.8/32 json") + ) + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "65002"}, + "nexthops": [{"ip": "192.0.2.2", "metric": 10}], + "bestpath":{ "selectionReason":"IGP Metric"}, + }, + { + "valid": True, + "aspath": {"string": "65002"}, + "nexthops": [{"ip": "192.0.2.3", "metric": 20}], + } + ] + } + + return topotest.json_cmp(output, expected) + + step("Check if IGP metric best path is selected") + test_func = functools.partial(_bgp_check_path_selection_vpn_metric) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefixes on R1" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_peer_graceful_shutdown/__init__.py b/tests/topotests/bgp_peer_graceful_shutdown/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_peer_graceful_shutdown/r1/bgpd.conf b/tests/topotests/bgp_peer_graceful_shutdown/r1/bgpd.conf new file mode 100644 index 0000000..8e59098 --- /dev/null +++ b/tests/topotests/bgp_peer_graceful_shutdown/r1/bgpd.conf @@ -0,0 +1,8 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_peer_graceful_shutdown/r1/zebra.conf b/tests/topotests/bgp_peer_graceful_shutdown/r1/zebra.conf new file mode 100644 index 0000000..376a19a --- /dev/null +++ b/tests/topotests/bgp_peer_graceful_shutdown/r1/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 10.10.10.1/32 +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_peer_graceful_shutdown/r2/bgpd.conf b/tests/topotests/bgp_peer_graceful_shutdown/r2/bgpd.conf new file mode 100644 index 0000000..0ee1821 --- /dev/null +++ b/tests/topotests/bgp_peer_graceful_shutdown/r2/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.2.2 remote-as internal + address-family ipv4 unicast + neighbor 192.168.2.2 next-hop-self + exit-address-family +! diff --git a/tests/topotests/bgp_peer_graceful_shutdown/r2/zebra.conf b/tests/topotests/bgp_peer_graceful_shutdown/r2/zebra.conf new file mode 100644 index 0000000..67ca02f --- /dev/null +++ b/tests/topotests/bgp_peer_graceful_shutdown/r2/zebra.conf @@ -0,0 +1,7 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! +int r2-eth1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/bgp_peer_graceful_shutdown/r3/bgpd.conf b/tests/topotests/bgp_peer_graceful_shutdown/r3/bgpd.conf new file mode 100644 index 0000000..5945a02 --- /dev/null +++ b/tests/topotests/bgp_peer_graceful_shutdown/r3/bgpd.conf @@ -0,0 +1,5 @@ +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as internal +! diff --git a/tests/topotests/bgp_peer_graceful_shutdown/r3/zebra.conf b/tests/topotests/bgp_peer_graceful_shutdown/r3/zebra.conf new file mode 100644 index 0000000..e5a37c9 --- /dev/null +++ b/tests/topotests/bgp_peer_graceful_shutdown/r3/zebra.conf @@ -0,0 +1,4 @@ +! +int r3-eth0 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/bgp_peer_graceful_shutdown/test_bgp_peer_graceful_shutdown.py b/tests/topotests/bgp_peer_graceful_shutdown/test_bgp_peer_graceful_shutdown.py new file mode 100644 index 0000000..2eb936a --- /dev/null +++ b/tests/topotests/bgp_peer_graceful_shutdown/test_bgp_peer_graceful_shutdown.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Check if routes from R1 has local-preference set to 0 and graceful-shutdown +community. +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2"), "s2": ("r2", "r3")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_orf(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + + def _bgp_converge(): + output = json.loads( + r2.vtysh_cmd( + "show bgp ipv4 unicast neighbor 192.168.2.2 advertised-routes json" + ) + ) + expected = {"advertisedRoutes": {"10.10.10.1/32": {"locPrf": 100}}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge at R2" + + step("Mark routes from R1 as graceful-shutdown") + r2.vtysh_cmd( + """ + configure terminal + router bgp + neighbor 192.168.1.1 graceful-shutdown + """ + ) + + def _bgp_check_peer_graceful_shutdown(): + output = json.loads(r3.vtysh_cmd("show bgp ipv4 unicast 10.10.10.1/32 json")) + expected = { + "paths": [ + { + "locPrf": 0, + "community": {"string": "graceful-shutdown"}, + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_peer_graceful_shutdown) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "local-preference is not 0 and/or graceful-shutdown community missing" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_peer_group/__init__.py b/tests/topotests/bgp_peer_group/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_peer_group/r1/bgpd.conf b/tests/topotests/bgp_peer_group/r1/bgpd.conf new file mode 100644 index 0000000..19b490a --- /dev/null +++ b/tests/topotests/bgp_peer_group/r1/bgpd.conf @@ -0,0 +1,8 @@ +! +router bgp 65001 + neighbor PG peer-group + neighbor PG remote-as external + neighbor PG timers 3 10 + neighbor 192.168.255.3 peer-group PG + neighbor r1-eth0 interface peer-group PG +! diff --git a/tests/topotests/bgp_peer_group/r1/zebra.conf b/tests/topotests/bgp_peer_group/r1/zebra.conf new file mode 100644 index 0000000..e2c399e --- /dev/null +++ b/tests/topotests/bgp_peer_group/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_peer_group/r2/bgpd.conf b/tests/topotests/bgp_peer_group/r2/bgpd.conf new file mode 100644 index 0000000..0880ee9 --- /dev/null +++ b/tests/topotests/bgp_peer_group/r2/bgpd.conf @@ -0,0 +1,7 @@ +! +router bgp 65002 + neighbor PG peer-group + neighbor PG remote-as external + neighbor PG timers 3 10 + neighbor r2-eth0 interface peer-group PG +! diff --git a/tests/topotests/bgp_peer_group/r2/zebra.conf b/tests/topotests/bgp_peer_group/r2/zebra.conf new file mode 100644 index 0000000..606c17b --- /dev/null +++ b/tests/topotests/bgp_peer_group/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_peer_group/r3/bgpd.conf b/tests/topotests/bgp_peer_group/r3/bgpd.conf new file mode 100644 index 0000000..5a1340f --- /dev/null +++ b/tests/topotests/bgp_peer_group/r3/bgpd.conf @@ -0,0 +1,11 @@ +! +router bgp 65003 + no bgp ebgp-requires-policy + neighbor PG peer-group + neighbor PG remote-as external + neighbor PG timers 3 10 + neighbor 192.168.255.1 peer-group PG + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_peer_group/r3/zebra.conf b/tests/topotests/bgp_peer_group/r3/zebra.conf new file mode 100644 index 0000000..e9fdfb7 --- /dev/null +++ b/tests/topotests/bgp_peer_group/r3/zebra.conf @@ -0,0 +1,6 @@ +! +interface r3-eth0 + ip address 192.168.255.3/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_peer_group/test_bgp_peer-group.py b/tests/topotests/bgp_peer_group/test_bgp_peer-group.py new file mode 100644 index 0000000..a91fade --- /dev/null +++ b/tests/topotests/bgp_peer_group/test_bgp_peer-group.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by +# Donatas Abraitis +# + +""" +Test if peer-group works for numbered and unnumbered configurations. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_peer_group(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_peer_group_configured(): + output = json.loads(tgen.gears["r1"].vtysh_cmd("show ip bgp neighbor json")) + expected = { + "r1-eth0": {"peerGroup": "PG", "bgpState": "Established"}, + "192.168.255.3": {"peerGroup": "PG", "bgpState": "Established"}, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_peer_group_configured) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed bgp convergence in r1" + + def _bgp_peer_group_check_advertised_routes(): + output = json.loads( + tgen.gears["r3"].vtysh_cmd("show ip bgp neighbor PG advertised-routes json") + ) + expected = { + "advertisedRoutes": { + "192.168.255.0/24": { + "valid": True, + "best": True, + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_peer_group_check_advertised_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed checking advertised routes from r3" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env b/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env new file mode 100644 index 0000000..6c554f5 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env @@ -0,0 +1,53 @@ + +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py new file mode 100644 index 0000000..0f998c1 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python2 +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, "r") + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg new file mode 100644 index 0000000..4a7dc48 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer1.in"; + encoder text; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 1"; + receive-routes; + encoder text; + } + + neighbor 10.0.1.1 { + router-id 10.0.1.2; + local-address 10.0.1.2; + local-as 64510; + peer-as 64510; + } + +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py new file mode 100644 index 0000000..0f998c1 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python2 +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, "r") + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg new file mode 100644 index 0000000..b53b054 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer2.in"; + encoder text; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 2"; + receive-routes; + encoder text; + } + + neighbor 10.0.2.1 { + router-id 10.0.2.2; + local-address 10.0.2.2; + local-as 64511; + peer-as 64511; + } + +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py new file mode 100644 index 0000000..0f998c1 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python2 +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, "r") + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg new file mode 100644 index 0000000..6a1cc2f --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer3.in"; + encoder text; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 3"; + receive-routes; + encoder text; + } + + neighbor 10.0.3.1 { + router-id 10.0.3.2; + local-address 10.0.3.2; + local-as 64502; + peer-as 64501; + } + +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py new file mode 100644 index 0000000..0f998c1 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python2 +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, "r") + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg new file mode 100644 index 0000000..2cc26cb --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer4.in"; + encoder text; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 4"; + receive-routes; + encoder text; + } + + neighbor 10.0.4.1 { + router-id 10.0.4.2; + local-address 10.0.4.2; + local-as 64503; + peer-as 64501; + } + +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/bgpd.conf b/tests/topotests/bgp_peer_type_multipath_relax/r1/bgpd.conf new file mode 100644 index 0000000..038f108 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/bgpd.conf @@ -0,0 +1,16 @@ +! +router bgp 64510 + bgp router-id 10.0.1.1 + no bgp ebgp-requires-policy + bgp confederation identifier 64501 + bgp confederation peers 64511 + bgp bestpath as-path multipath-relax + bgp bestpath compare-routerid + bgp bestpath peer-type multipath-relax + neighbor 10.0.1.2 remote-as 64510 + neighbor 10.0.3.2 remote-as 64502 + neighbor 10.0.4.2 remote-as 64503 + neighbor 10.0.5.2 remote-as 64511 +! +line vty +! diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/multipath.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/multipath.json new file mode 100644 index 0000000..11dad78 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/multipath.json @@ -0,0 +1,50 @@ +{ + "routes": { "203.0.113.0/30": [ + { + "valid":true, + "multipath":true, + "pathFrom":"external", + "peerId":"10.0.5.2" + }, + { + "valid":true, + "bestpath":true, + "selectionReason":"Peer Type", + "pathFrom":"external", + "peerId":"10.0.4.2" + }, + { + "valid":true, + "multipath":true, + "pathFrom":"internal", + "peerId":"10.0.1.2" + } +],"203.0.113.4/30": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"Confed Peer Type", + "pathFrom":"external", + "peerId":"10.0.5.2" + }, + { + "valid":true, + "multipath":true, + "pathFrom":"internal", + "peerId":"10.0.1.2" + } +],"203.0.113.8/30": [ + { + "valid":true, + "multipath":true, + "pathFrom":"external", + "peerId":"10.0.4.2" + }, + { + "valid":true, + "bestpath":true, + "selectionReason":"Router ID", + "pathFrom":"external", + "peerId":"10.0.3.2" + } +] } } diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/not-multipath.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/not-multipath.json new file mode 100644 index 0000000..c621832 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/not-multipath.json @@ -0,0 +1,50 @@ +{ + "routes": { "203.0.113.0/30": [ + { + "valid":true, + "multipath":null, + "pathFrom":"external", + "peerId":"10.0.5.2" + }, + { + "valid":true, + "bestpath":true, + "selectionReason":"Peer Type", + "pathFrom":"external", + "peerId":"10.0.4.2" + }, + { + "valid":true, + "multipath":null, + "pathFrom":"internal", + "peerId":"10.0.1.2" + } +],"203.0.113.4/30": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"Confed Peer Type", + "pathFrom":"external", + "peerId":"10.0.5.2" + }, + { + "valid":true, + "multipath":null, + "pathFrom":"internal", + "peerId":"10.0.1.2" + } +],"203.0.113.8/30": [ + { + "valid":true, + "multipath":true, + "pathFrom":"external", + "peerId":"10.0.4.2" + }, + { + "valid":true, + "bestpath":true, + "selectionReason":"Router ID", + "pathFrom":"external", + "peerId":"10.0.3.2" + } +] } } diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-confed.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-confed.json new file mode 100644 index 0000000..22ec2c2 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-confed.json @@ -0,0 +1,33 @@ +{ + "203.0.113.0\/30":[ + { + "prefix":"203.0.113.0\/30", + "protocol":"bgp", + "installed":true, + "internalNextHopNum":4, + "internalNextHopActiveNum":4, + "nexthops":[ + { + "ip":"198.51.100.2", + "active":true, + "recursive":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "active":true + }, + { + "ip":"198.51.100.10", + "active":true, + "recursive":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-iBGP.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-iBGP.json new file mode 100644 index 0000000..facddcd --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-iBGP.json @@ -0,0 +1,33 @@ +{ + "203.0.113.0\/30":[ + { + "prefix":"203.0.113.0\/30", + "protocol":"bgp", + "installed":true, + "internalNextHopNum":4, + "internalNextHopActiveNum":4, + "nexthops":[ + { + "ip":"198.51.100.1", + "active":true, + "recursive":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "active":true + }, + { + "ip":"198.51.100.10", + "active":true, + "recursive":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-no-recursive.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-no-recursive.json new file mode 100644 index 0000000..5399cee --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-no-recursive.json @@ -0,0 +1,35 @@ +{ + "prefix":"203.0.113.0\/30", + "paths":[ + { + "valid":false, + "peer":{ + "peerId":"10.0.4.2", + "routerId":"10.0.4.2", + "type":"external" + } + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true, + "selectionReason":"Confed Peer Type" + }, + "peer":{ + "peerId":"10.0.5.2", + "routerId":"10.0.5.2", + "type":"confed-external" + } + }, + { + "valid":true, + "multipath":true, + "peer":{ + "peerId":"10.0.1.2", + "routerId":"10.0.1.2", + "type":"confed-internal" + } + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-recursive.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-recursive.json new file mode 100644 index 0000000..7da95ae --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-recursive.json @@ -0,0 +1,36 @@ +{ + "prefix":"203.0.113.0\/30", + "paths":[ + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true, + "selectionReason":"Peer Type" + }, + "peer":{ + "peerId":"10.0.4.2", + "routerId":"10.0.4.2", + "type":"external" + } + }, + { + "valid":true, + "multipath":true, + "peer":{ + "peerId":"10.0.5.2", + "routerId":"10.0.5.2", + "type":"confed-external" + } + }, + { + "valid":true, + "multipath":true, + "peer":{ + "peerId":"10.0.1.2", + "routerId":"10.0.1.2", + "type":"confed-internal" + } + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1.json new file mode 100644 index 0000000..a90669a --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1.json @@ -0,0 +1,33 @@ +{ + "prefix":"203.0.113.0\/30", + "paths":[ + { + "multipath":true, + "peer":{ + "peerId":"10.0.5.2", + "routerId":"10.0.5.2", + "type":"confed-external" + } + }, + { + "multipath":true, + "bestpath":{ + "overall":true, + "selectionReason":"Peer Type" + }, + "peer":{ + "peerId":"10.0.4.2", + "routerId":"10.0.4.2", + "type":"external" + } + }, + { + "multipath":true, + "peer":{ + "peerId":"10.0.1.2", + "routerId":"10.0.1.2", + "type":"confed-internal" + } + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-ip-route.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-ip-route.json new file mode 100644 index 0000000..1bf38ef --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-ip-route.json @@ -0,0 +1,23 @@ +{ + "203.0.113.8\/30":[ + { + "prefix":"203.0.113.8\/30", + "protocol":"bgp", + "installed":true, + "internalNextHopNum":2, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.2", + "active":true + }, + { + "fib":null, + "ip":"198.51.100.10", + "active":null + } + ] + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-no-recursive.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-no-recursive.json new file mode 100644 index 0000000..33d0f2d --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-no-recursive.json @@ -0,0 +1,21 @@ +{ + "prefix":"203.0.113.8\/30", + "paths":[ + { + "valid":false, + "peer":{ + "peerId":"10.0.4.2", + "routerId":"10.0.4.2", + "type":"external" + } + }, + { + "valid":true, + "peer":{ + "peerId":"10.0.3.2", + "routerId":"10.0.3.2", + "type":"external" + } + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-recursive.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-recursive.json new file mode 100644 index 0000000..6ac2512 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-recursive.json @@ -0,0 +1,23 @@ +{ + "prefix":"203.0.113.8\/30", + "paths":[ + { + "valid":true, + "multipath":true, + "peer":{ + "peerId":"10.0.4.2", + "routerId":"10.0.4.2", + "type":"external" + } + }, + { + "valid":true, + "multipath":true, + "peer":{ + "peerId":"10.0.3.2", + "routerId":"10.0.3.2", + "type":"external" + } + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/zebra.conf b/tests/topotests/bgp_peer_type_multipath_relax/r1/zebra.conf new file mode 100644 index 0000000..911aa1c --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/zebra.conf @@ -0,0 +1,27 @@ +! +hostname r1 +! +interface r1-eth0 + description ExaBGP iBGP peer1 + ip address 10.0.1.1/24 + no link-detect +! +interface r1-eth1 + description ExaBGP peer3 + ip address 10.0.3.1/24 + no link-detect +! +interface r1-eth2 + description ExaBGP peer4 + ip address 10.0.4.1/24 + no link-detect +! +interface r1-eth3 + description r2 confed peer + ip address 10.0.5.1/24 + no link-detect +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r2/bgpd.conf b/tests/topotests/bgp_peer_type_multipath_relax/r2/bgpd.conf new file mode 100644 index 0000000..2362a19 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r2/bgpd.conf @@ -0,0 +1,19 @@ +! +!log file bgpd.log +! +router bgp 64511 + bgp confederation identifier 64501 + bgp confederation peers 64510 + bgp router-id 10.0.5.2 + no bgp ebgp-requires-policy + neighbor 10.0.2.2 remote-as 64511 + neighbor 10.0.5.1 remote-as 64510 + ! + address-family ipv4 unicast + neighbor 10.0.5.1 route-map dropall in + exit-address-family +! +route-map dropall deny 10 +! +line vty +! diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r2/staticd.conf b/tests/topotests/bgp_peer_type_multipath_relax/r2/staticd.conf new file mode 100644 index 0000000..35ebe0d --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r2/staticd.conf @@ -0,0 +1,4 @@ +hostname r2 +! +ip route 198.51.100.0/24 10.0.2.2 +! diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r2/zebra.conf b/tests/topotests/bgp_peer_type_multipath_relax/r2/zebra.conf new file mode 100644 index 0000000..900e7d4 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r2/zebra.conf @@ -0,0 +1,19 @@ +! +! +hostname r2 +! +interface r2-eth0 + description ExaBGP peer + ip address 10.0.2.1/24 + no link-detect +! +interface r2-eth1 + description r1 confed peer + ip address 10.0.5.2/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py b/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py new file mode 100755 index 0000000..ad6674c --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py @@ -0,0 +1,372 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 Arista Networks, Inc. +# + +""" +test_bgp_peer-type_multipath-relax.py: + +Test the effects of the "bgp bestpath peer-type multipath-relax" command + +- enabling the command allows eBGP, iBGP, and confed routes to be multipath +- the choice of best path is not affected +- disabling the command removes iBGP/confed routes from multipath +- enabling the command does not forgive eBGP routes of the requirement + (when enabled) that next hops resolve over connected routes +- a mixed-type multipath next hop, when published to zebra, does not + require resolving next hops over connected routes +- with the command enabled, an all-eBGP multipath next hop still requires + resolving next hops over connected routes when published to zebra + +Topology used by the test: + + eBGP +------+ iBGP + peer1 ---- | r1 | ---- peer3 + | | +peer2 ---- r2 ---- | | ---- peer4 + iBGP confed +------+ eBGP + +r2 is present in this topology because ExaBGP does not currently support +confederations so we use FRR to advertise the required AS_CONFED_SEQUENCE. + +Routes are advertised from different peers to form interesting multipaths. + + peer1 peer2 peer3 peer4 multipath on r1 + +203.0.113.0/30 x x x all 3 +203.0.113.4/30 x x confed-iBGP +203.0.113.8/30 x x eBGP-only + +There is also a BGP-advertised route used only for recursively resolving +next hops. +""" + +import functools +import json +import os +import pytest +import sys + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +def build_topo(tgen): + "Build function" + + # Set up routers + tgen.add_router("r1") # DUT + tgen.add_router("r2") + + # Set up peers + for peern in range(1, 5): + peer = tgen.add_exabgp_peer( + "peer{}".format(peern), + ip="10.0.{}.2/24".format(peern), + defaultRoute="via 10.0.{}.1".format(peern), + ) + if peern == 2: + tgen.add_link(tgen.gears["r2"], peer) + else: + tgen.add_link(tgen.gears["r1"], peer) + tgen.add_link(tgen.gears["r1"], tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + # For all registered routers, load the zebra configuration file + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/setup_vrfs".format(CWD)) + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + # Start up exabgp peers + peers = tgen.exabgp_peers() + for peer in peers: + fifo_in = "/var/run/exabgp_{}.in".format(peer) + if os.path.exists(fifo_in): + os.remove(fifo_in) + os.mkfifo(fifo_in, 0o777) + logger.info("Starting ExaBGP on peer {}".format(peer)) + peer_dir = os.path.join(CWD, peer) + env_file = os.path.join(CWD, "exabgp.env") + peers[peer].start(peer_dir, env_file) + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_bgp_peer_type_multipath_relax(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def exabgp_cmd(peer, cmd): + pipe = open("/run/exabgp_{}.in".format(peer), "w") + with pipe: + pipe.write(cmd) + pipe.close() + + # Prefixes used in the test + prefix1 = "203.0.113.0/30" + prefix2 = "203.0.113.4/30" + prefix3 = "203.0.113.8/30" + # Next hops used for iBGP/confed routes + resolved_nh1 = "198.51.100.1" + resolved_nh2 = "198.51.100.2" + # BGP route used for recursive resolution + bgp_resolving_prefix = "198.51.100.0/24" + # Next hop that will require non-connected recursive resolution + ebgp_resolved_nh = "198.51.100.10" + + # Send a non-connected route to resolve others + exabgp_cmd( + "peer3", "announce route {} next-hop self\n".format(bgp_resolving_prefix) + ) + router = tgen.gears["r1"] + + # It seems that if you write to the exabgp socket too quickly in + # succession, requests get lost. So verify prefix1 now instead of + # after all the prefixes are advertised. + logger.info("Create and verify mixed-type multipaths") + exabgp_cmd( + "peer1", + "announce route {} next-hop {} as-path [ 64499 ]\n".format( + prefix1, resolved_nh1 + ), + ) + exabgp_cmd( + "peer2", + "announce route {} next-hop {} as-path [ 64499 ]\n".format( + prefix1, resolved_nh2 + ), + ) + exabgp_cmd("peer4", "announce route {} next-hop self\n".format(prefix1)) + reffile = os.path.join(CWD, "r1/prefix1.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip bgp {} json".format(prefix1), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Mixed-type multipath not found" + assert res is None, assertMsg + + logger.info("Create and verify eBGP and iBGP+confed multipaths") + exabgp_cmd( + "peer1", + "announce route {} next-hop {} as-path [ 64499 ]\n".format( + prefix2, resolved_nh1 + ), + ) + exabgp_cmd( + "peer2", + "announce route {} next-hop {} as-path [ 64499 ]\n".format( + prefix2, resolved_nh2 + ), + ) + exabgp_cmd("peer3", "announce route {} next-hop self".format(prefix3)) + exabgp_cmd("peer4", "announce route {} next-hop self".format(prefix3)) + reffile = os.path.join(CWD, "r1/multipath.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Not all expected multipaths found" + assert res is None, assertMsg + + logger.info("Toggle peer-type multipath-relax and verify the changes") + router.vtysh_cmd( + "conf\n router bgp 64510\n no bgp bestpath peer-type multipath-relax\n" + ) + # This file verifies "multipath" is not set + reffile = os.path.join(CWD, "r1/not-multipath.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Disabling peer-type multipath-relax did not take effect" + assert res is None, assertMsg + + router.vtysh_cmd( + "conf\n router bgp 64510\n bgp bestpath peer-type multipath-relax\n" + ) + reffile = os.path.join(CWD, "r1/multipath.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Reenabling peer-type multipath-relax did not take effect" + assert res is None, assertMsg + + logger.info("Check recursive resolution of eBGP next hops is not affected") + # eBGP next hop resolution rejects recursively resolved next hops by + # default, even with peer-type multipath-relax + exabgp_cmd( + "peer4", "announce route {} next-hop {}\n".format(prefix3, ebgp_resolved_nh) + ) + reffile = os.path.join(CWD, "r1/prefix3-no-recursive.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip bgp {} json".format(prefix3), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix3) + assert res is None, assertMsg + + exabgp_cmd( + "peer4", "announce route {} next-hop {}\n".format(prefix1, ebgp_resolved_nh) + ) + reffile = os.path.join(CWD, "r1/prefix1-no-recursive.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip bgp {} json".format(prefix1), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix1) + assert res is None, assertMsg + + # When other config allows recursively resolved eBGP next hops, + # such next hops in all-eBGP multipaths should be valid + router.vtysh_cmd("conf\n router bgp 64510\n neighbor 10.0.4.2 ebgp-multihop\n") + reffile = os.path.join(CWD, "r1/prefix3-recursive.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip bgp {} json".format(prefix3), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix3) + assert res is None, assertMsg + + reffile = os.path.join(CWD, "r1/prefix1-recursive.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip bgp {} json".format(prefix1), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix1) + assert res is None, assertMsg + + logger.info("Check mixed-type multipath next hop recursive resolution in FIB") + # There are now two eBGP-learned routes with a recursively resolved next; + # hop; one is all-eBGP multipath, and the other is iBGP/eBGP/ + # confed-external. The peer-type multipath-relax feature only enables + # recursive resolution in FIB if any next hop is iBGP/confed-learned. The + # all-eBGP multipath will have only one valid next hop in the FIB. + reffile = os.path.join(CWD, "r1/prefix3-ip-route.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip route {} json".format(prefix3), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "FIB next hops mismatch for all-eBGP multipath" + assert res is None, assertMsg + + # check confed-external enables recursively resolved next hops by itself + exabgp_cmd( + "peer1", + "withdraw route {} next-hop {} as-path [ 64499 ]\n".format( + prefix1, resolved_nh1 + ), + ) + reffile = os.path.join(CWD, "r1/prefix1-eBGP-confed.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip route {} json".format(prefix1), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "FIB next hops mismatch for eBGP+confed-external multipath" + assert res is None, assertMsg + + # check iBGP by itself + exabgp_cmd( + "peer1", + "announce route {} next-hop {} as-path [ 64499 ]\n".format( + prefix1, resolved_nh1 + ), + ) + exabgp_cmd( + "peer2", + "withdraw route {} next-hop {} as-path [ 64499 ]\n".format( + prefix1, resolved_nh2 + ), + ) + reffile = os.path.join(CWD, "r1/prefix1-eBGP-iBGP.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip route {} json".format(prefix1), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "FIB next hops mismatch for eBGP+iBGP multipath" + assert res is None, assertMsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_prefix_list_any/__init__.py b/tests/topotests/bgp_prefix_list_any/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_prefix_list_any/r1/bgpd.conf b/tests/topotests/bgp_prefix_list_any/r1/bgpd.conf new file mode 100644 index 0000000..14c28ca --- /dev/null +++ b/tests/topotests/bgp_prefix_list_any/r1/bgpd.conf @@ -0,0 +1,15 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as external + neighbor 2001:db8:1::2 remote-as external + address-family ipv4 unicast + network 192.168.0.1/32 + no neighbor 2001:db8:1::2 activate + exit-address-family + address-family ipv6 unicast + neighbor 2001:db8:1::2 activate + network 2001:db8::1/128 + exit-address-family +! diff --git a/tests/topotests/bgp_prefix_list_any/r1/zebra.conf b/tests/topotests/bgp_prefix_list_any/r1/zebra.conf new file mode 100644 index 0000000..c01a8cf --- /dev/null +++ b/tests/topotests/bgp_prefix_list_any/r1/zebra.conf @@ -0,0 +1,5 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bgp_prefix_list_any/r2/bgpd.conf b/tests/topotests/bgp_prefix_list_any/r2/bgpd.conf new file mode 100644 index 0000000..7332059 --- /dev/null +++ b/tests/topotests/bgp_prefix_list_any/r2/bgpd.conf @@ -0,0 +1,50 @@ +! +!debug bgp updates +! +router bgp 65002 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.1 remote-as external + neighbor 2001:db8:1::1 remote-as external + address-family ipv4 unicast + network 10.10.10.1/32 + network 10.10.10.2/32 + network 10.10.10.3/32 + network 10.10.10.10/32 + no neighbor 2001:db8:1::1 activate + neighbor 192.168.1.1 route-map r1-v4 out + exit-address-family + address-family ipv6 unicast + network 2001:db8:10::1/128 + network 2001:db8:10::2/128 + network 2001:db8:10::3/128 + network 2001:db8:10::10/128 + neighbor 2001:db8:1::1 activate + neighbor 2001:db8:1::1 route-map r1-v6 out + exit-address-family +! +ip prefix-list r1-1 seq 5 permit 10.10.10.1/32 +ip prefix-list r1-1 seq 10 permit 10.10.10.2/32 +ip prefix-list r1-1 seq 15 permit 10.10.10.3/32 +ip prefix-list r1-2 seq 5 permit 10.10.10.10/32 +! +ipv6 prefix-list r1-1 seq 5 permit 2001:db8:10::1/128 +ipv6 prefix-list r1-1 seq 10 permit 2001:db8:10::2/128 +ipv6 prefix-list r1-1 seq 15 permit 2001:db8:10::3/128 +ipv6 prefix-list r1-2 seq 5 permit 2001:db8:10::10/128 +! +route-map r1-v4 permit 10 + match ip address prefix-list r1-1 +exit +! +route-map r1-v4 permit 20 + match ip address prefix-list r1-2 +exit +! +route-map r1-v6 permit 10 + match ipv6 address prefix-list r1-1 +exit +! +route-map r1-v6 permit 20 + match ipv6 address prefix-list r1-2 +exit diff --git a/tests/topotests/bgp_prefix_list_any/r2/zebra.conf b/tests/topotests/bgp_prefix_list_any/r2/zebra.conf new file mode 100644 index 0000000..e90135c --- /dev/null +++ b/tests/topotests/bgp_prefix_list_any/r2/zebra.conf @@ -0,0 +1,5 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/bgp_prefix_list_any/test_bgp_prefix_list_any.py b/tests/topotests/bgp_prefix_list_any/test_bgp_prefix_list_any.py new file mode 100644 index 0000000..0eb2447 --- /dev/null +++ b/tests/topotests/bgp_prefix_list_any/test_bgp_prefix_list_any.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if route-map works correctly when modifying prefix-list +from deny to permit with any, and vice-versa. +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_route_map_prefix_list(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + + def _bgp_prefixes_sent(count): + output = json.loads(r2.vtysh_cmd("show bgp summary json")) + expected = { + "ipv4Unicast": { + "peers": {"192.168.1.1": {"pfxSnt": count, "state": "Established"}} + }, + "ipv6Unicast": { + "peers": {"2001:db8:1::1": {"pfxSnt": count, "state": "Established"}} + }, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_prefixes_sent, 4) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge initial topology" + + r2.vtysh_cmd( + """ + configure terminal + ip prefix-list r1-2 seq 5 deny any + ipv6 prefix-list r1-2 seq 5 deny any + """ + ) + + test_func = functools.partial(_bgp_prefixes_sent, 3) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Only 3 prefixes MUST be advertised, seeing more" + + r2.vtysh_cmd( + """ + configure terminal + ip prefix-list r1-2 seq 5 permit 10.10.10.10/32 + ipv6 prefix-list r1-2 seq 5 permit 2001:db8:10::10/128 + """ + ) + + test_func = functools.partial(_bgp_prefixes_sent, 4) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "More or less prefixes advertised to r1, MUST be 4" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_prefix_list_topo1/__init__.py b/tests/topotests/bgp_prefix_list_topo1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_prefix_list_topo1/prefix_lists.json b/tests/topotests/bgp_prefix_list_topo1/prefix_lists.json new file mode 100644 index 0000000..3bb07ad --- /dev/null +++ b/tests/topotests/bgp_prefix_list_topo1/prefix_lists.json @@ -0,0 +1,123 @@ +{ + "address_types": ["ipv4"], + "ipv4base":"10.0.0.0", + "ipv4mask":30, + "ipv6base":"fd00::", + "ipv6mask":64, + "link_ip_start":{"ipv4":"10.0.0.0", "v4mask":30, "ipv6":"fd00::", "v6mask":64}, + "lo_prefix":{"ipv4":"1.0.", "v4mask":32, "ipv6":"2001:DB8:F::", "v6mask":128}, + "routers":{ + "r1":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback", "add_static_route":"yes"}, + "r2":{"ipv4":"auto", "ipv6":"auto"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback", "add_static_route":"yes"}, + "r1":{"ipv4":"auto", "ipv6":"auto"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback", "add_static_route":"yes"}, + "r1":{"ipv4":"auto", "ipv6":"auto"}, + "r2":{"ipv4":"auto", "ipv6":"auto"}, + "r4":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback", "add_static_route":"yes"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_prefix_list_topo1/prefix_modify.json b/tests/topotests/bgp_prefix_list_topo1/prefix_modify.json new file mode 100644 index 0000000..4a066ae --- /dev/null +++ b/tests/topotests/bgp_prefix_list_topo1/prefix_modify.json @@ -0,0 +1,120 @@ +{ + "address_types": ["ipv4"], + "ipv4base":"192.120.1.0", + "ipv4mask":24, + "link_ip_start":{"ipv4":"192.120.1.0", "v4mask":30, "ipv6":"fd00::", "v6mask":64}, + "routers":{ + "r1":{ + "links":{ + "lo": {"ipv4": "auto", "type": "loopback", "add_static_route":"yes"}, + "r2":{"ipv4":"auto"}, + "r3":{"ipv4":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback", "add_static_route":"yes"}, + "r1":{"ipv4":"auto", "ipv6":"auto"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3":{ + "links":{ + "lo": {"ipv4": "auto", "type": "loopback", "add_static_route":"yes"}, + "r1":{"ipv4":"auto"}, + "r2":{"ipv4":"auto"}, + "r4":{"ipv4":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4":{ + "links":{ + "lo": {"ipv4": "auto", "type": "loopback", "add_static_route":"yes"}, + "r3":{"ipv4":"auto"} + }, + "bgp":{ + "local_as":"200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py b/tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py new file mode 100644 index 0000000..f351dde --- /dev/null +++ b/tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py @@ -0,0 +1,1341 @@ +#!/usr/bin/python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test prefix-list functionality: + +Test steps +- Create topology (setup module) + Creating 4 routers topology, r1, r2, r3 are in IBGP and + r3, r4 are in EBGP +- Bring up topology +- Verify for bgp to converge + +IP prefix-list tests +- Test ip prefix-lists IN permit +- Test ip prefix-lists OUT permit +- Test ip prefix-lists IN deny and permit any +- Test delete ip prefix-lists +- Test ip prefix-lists OUT deny and permit any +- Test modify ip prefix-lists IN permit to deny +- Test modify ip prefix-lists IN deny to permit +- Test modify ip prefix-lists OUT permit to deny +- Test modify prefix-lists OUT deny to permit +- Test ip prefix-lists implicit deny +""" + +import sys +import time +import os +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + create_prefix_lists, + verify_prefix_lists, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp_and_verify +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd] + + +# Global variables +bgp_convergence = False + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/prefix_lists.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global BGP_CONVERGENCE + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_ip_prefix_lists_in_permit(request): + """ + Create ip prefix list and test permit prefixes IN direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "20.0.20.1/32", "no_of_ip": 1, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [{"seqid": 10, "network": "any", "action": "permit"}] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure bgp neighbor with prefix list + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "prefix_lists": [ + {"name": "pf_list_1", "direction": "in"} + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ip_prefix_lists_out_permit(request): + """ + Create ip prefix list and test permit prefixes out direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 1, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create Static routes + input_dict_1 = { + "r1": { + "static_routes": [ + {"network": "20.0.20.1/32", "no_of_ip": 1, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_5 = { + "r3": { + "static_routes": [ + {"network": "10.0.0.2/30", "no_of_ip": 1, "next_hop": "10.0.0.9"} + ] + } + } + result = create_static_routes(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + + # Create ip prefix list + input_dict_2 = { + "r1": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": 10, "network": "20.0.20.1/32", "action": "permit"} + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure prefix list to bgp neighbor + # Configure bgp neighbor with prefix list + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "prefix_lists": [ + { + "name": "pf_list_1", + "direction": "out", + } + ] + } + } + } + }, + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ], + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict_1, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + +def test_ip_prefix_lists_in_deny_and_permit_any(request): + """ + Create ip prefix list and test permit/deny prefixes IN direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static Routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 1, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + # Create ip prefix list + input_dict_2 = { + "r1": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": "10", "network": "10.0.20.1/32", "action": "deny"}, + {"seqid": "11", "network": "any", "action": "permit"}, + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure bgp neighbor with prefix list + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "prefix_lists": [ + {"name": "pf_list_1", "direction": "in"} + ] + } + } + } + } + } + } + } + } + }, + } + # Configure prefix list to bgp neighbor + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + +def test_delete_prefix_lists(request): + """ + Delete ip prefix list + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r1": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": "10", "network": "10.0.20.1/32", "action": "deny"} + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_prefix_lists(tgen, input_dict_2) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Delete prefix list + input_dict_2 = { + "r1": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.20.1/32", + "action": "deny", + "delete": True, + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ip_prefix_lists_out_deny_and_permit_any(request): + """ + Create ip prefix list and test deny/permit any prefixes OUT direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static Routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create Static Routes + input_dict_1 = { + "r2": { + "static_routes": [ + {"network": "20.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.1"} + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + + # Create ip prefix list + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "deny", + }, + {"seqid": "11", "network": "any", "action": "permit"}, + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure prefix list to bgp neighbor + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "prefix_lists": [ + { + "name": "pf_list_1", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict_1, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + +def test_modify_prefix_lists_in_permit_to_deny(request): + """ + Modify ip prefix list and test permit to deny prefixes IN direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static Routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure prefix list to bgp neighbor + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "prefix_lists": [ + {"name": "pf_list_1", "direction": "in"} + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Modify prefix list + input_dict_1 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "deny", + }, + {"seqid": "11", "network": "any", "action": "permit"}, + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to clear bgp, so config changes would be reflected + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + +def test_modify_prefix_lists_in_deny_to_permit(request): + """ + Modify ip prefix list and test deny to permit prefixes IN direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static Routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + + # Create ip prefix list + input_dict_1 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "deny", + }, + {"seqid": "11", "network": "any", "action": "permit"}, + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure prefix list to bgp neighbor + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "prefix_lists": [ + {"name": "pf_list_1", "direction": "in"} + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Modify ip prefix list + input_dict_1 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to clear bgp, so config changes would be reflected + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_modify_prefix_lists_out_permit_to_deny(request): + """ + Modify ip prefix list and test permit to deny prefixes OUT direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static Routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + + # Create ip prefix list + input_dict_1 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure prefix list to bgp neighbor + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "prefix_lists": [ + { + "name": "pf_list_1", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Modify ip prefix list + input_dict_1 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "deny", + }, + {"seqid": "11", "network": "any", "action": "permit"}, + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to clear bgp, so config changes would be reflected + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + +def test_modify_prefix_lists_out_deny_to_permit(request): + """ + Modify ip prefix list and test deny to permit prefixes OUT direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static Routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + # Create ip prefix list + input_dict_1 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "deny", + }, + {"seqid": "11", "network": "any", "action": "permit"}, + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure prefix list to bgp neighbor + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "prefix_lists": [ + { + "name": "pf_list_1", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Modify ip prefix list + input_dict_1 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to clear bgp, so config changes would be reflected + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ip_prefix_lists_implicit_deny(request): + """ + Create ip prefix list and test implicit deny + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static Routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create Static Routes + input_dict_1 = { + "r2": { + "static_routes": [ + {"network": "20.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.1"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + # Create ip prefix list + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure prefix list to bgp neighbor + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "prefix_lists": [ + { + "name": "pf_list_1", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib( + tgen, "ipv4", dut, input_dict_1, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_prefix_list_topo1/test_prefix_modify.py b/tests/topotests/bgp_prefix_list_topo1/test_prefix_modify.py new file mode 100644 index 0000000..541b9de --- /dev/null +++ b/tests/topotests/bgp_prefix_list_topo1/test_prefix_modify.py @@ -0,0 +1,404 @@ +#!/usr/bin/python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test prefix-list functionality: + +Test steps +- Create topology (setup module) + Creating 4 routers topology, r1, r2, r3 are in IBGP and + r3, r4 are in EBGP +- Bring up topology +- Verify for bgp to converge + +IP prefix-list tests +- Test modify prefix-list action +""" + +import sys +import time +import os +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + create_prefix_lists, + step, + create_route_maps, + check_router_status, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp + +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd] + + +# Global variables +bgp_convergence = False + +IPV4_PF3 = "192.168.0.0/18" +IPV4_PF4 = "192.150.10.0/24" +IPV4_PF5 = "192.168.10.1/32" +IPV4_PF6 = "192.168.10.10/32" +IPV4_PF7 = "192.168.10.0/24" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/prefix_lists.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global BGP_CONVERGENCE + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_bug_prefix_lists_deny_to_permit_p1(request): + """ + Verify modification of prefix-list action + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + # base config + step("Configure IPV4 and IPv6 IBGP and EBGP session as mentioned in setup") + step("Configure static routes on R2 with Null 0 nexthop") + input_dict_1 = { + "r2": { + "static_routes": [ + {"network": IPV4_PF7, "no_of_ip": 1, "next_hop": "Null0"}, + {"network": IPV4_PF6, "no_of_ip": 1, "next_hop": "Null0"}, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Advertise static route in BGP using redistribute static command") + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "All the static route advertised in R4 as BGP " + "routes verify using 'show ip bgp'and 'show bgp'" + ) + dut = "r4" + protocol = "bgp" + + input_dict_route = { + "r4": {"static_routes": [{"network": IPV4_PF7}, {"network": IPV4_PF6}]} + } + + result = verify_rib(tgen, "ipv4", dut, input_dict_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure IPv4 and IPv6 prefix-list") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": "5", "network": IPV4_PF7, "action": "deny"} + ], + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": "10", "network": IPV4_PF7, "action": "permit"} + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "configure route-map seq to permit IPV4 prefix list and seq" + "2 to permit IPV6 prefix list and apply it to out direction on R3" + ) + + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_7 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R4 should not have any IPv4 and IPv6 BGP routes using " + "show ip bgp show bgp" + ) + + dut = "r4" + protocol = "bgp" + + result = verify_rib( + tgen, "ipv4", dut, input_dict_route, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n" "Error : Routes are still present \n {}".format( + tc_name, result + ) + + step("Modify IPv4/IPv6 prefix-list sequence 5 to another value on R3") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": "5", "network": IPV4_PF4, "action": "deny"} + ], + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify /24 and /120 routes present on" + "R4 BGP table using show ip bgp show bgp" + ) + input_dict = {"r4": {"static_routes": [{"network": IPV4_PF7}]}} + + dut = "r4" + protocol = "bgp" + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change prefix-list to same as original on R3") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": "5", "network": IPV4_PF7, "action": "deny"} + ], + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify /24 and /120 routes removed on" + "R4 BGP table using show ip bgp show bgp" + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n" "Error : Routes are still present \n {}".format( + tc_name, result + ) + + step("Modify IPv4/IPv6 prefix-list sequence 5 to another value") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": "5", "network": IPV4_PF4, "action": "deny"} + ], + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Clear BGP on R3 and verify the routes") + clear_bgp(tgen, "ipv4", "r3") + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("On R3 add prefix-list permit any for IPv4 and IPv6 seq 15") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": "15", "network": "any", "action": "permit"} + ], + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify /24 and /32 /120 and /128 routes are present on R4") + result = verify_rib(tgen, "ipv4", dut, input_dict_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_prefix_sid/__init__.py b/tests/topotests/bgp_prefix_sid/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_prefix_sid/exabgp.env b/tests/topotests/bgp_prefix_sid/exabgp.env new file mode 100644 index 0000000..6c554f5 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/exabgp.env @@ -0,0 +1,53 @@ + +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg b/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg new file mode 100644 index 0000000..5b55366 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg @@ -0,0 +1,103 @@ +group controller { + neighbor 10.0.0.1 { + router-id 10.0.0.101; + local-address 10.0.0.101; + local-as 2; + peer-as 1; + + family { + ipv4 nlri-mpls; + } + + static { + # ref: draft-ietf-idr-bgp-prefix-sid-27 + # + # IANA temporarily assigned the following: + # attribute code type (suggested value: 40) to + # the BGP Prefix-SID attribute + # + # 0 1 2 3 + # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Type | Length | RESERVED | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Flags | Label Index | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Label Index | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # Figure. Label-Index TLV (Prefix-SID type-1) + # + # 0 1 2 3 + # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Type | Length | Flags | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Flags | + # +-+-+-+-+-+-+-+-+ + # + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | SRGB 1 (6 octets) | + # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | SRGB n (6 octets) | + # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # Figure. Originator SRGB TLV (Prefix-SID type-3) + + # ExaBGP generic-attribute binary pattern: + # Attribute-type: 0x28 (40:BGP_PREFIX_SID) + # Attribute-flag: 0xc0 (Option, Transitive) + # Attribute-body: Label-Index TLV and Originator SRGB TLV + # Label-Index TLV: 0x01000700000000000001 + # Type (08bit): 0x01 + # Length (16bit): 0x0007 + # RESERVED (08bit): 0x00 + # Flags (16bit): 0x0000 + # Label Index (32bit): 0x00000001 + # Originator SRGB TLV: 0x03000800000c350000000a + # Type (08bit): 0x03 + # Length (16bit): 0x0008 (nb-SRGB is 1) + # Flags (16bit): 0x0000 + # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) + route 3.0.0.1/32 next-hop 10.0.0.101 label [800001] attribute [0x28 0xc0 0x0100070000000000000103000800000c350000000a]; + + # ExaBGP generic-attribute binary pattern: + # Attribute-type: 0x28 (40:BGP_PREFIX_SID) + # Attribute-flag: 0xc0 (Option, Transitive) + # Attribute-body: Label-Index TLV and Originator SRGB TLV + # Label-Index TLV: 0x01000700000000000001 + # Type (08bit): 0x01 + # Length (16bit): 0x0007 + # RESERVED (08bit): 0x00 + # Flags (16bit): 0x0000 + # Label Index (32bit): 0x00000002 + # Originator SRGB TLV: 0x03000800000c350000000a + # Type (08bit): 0x03 + # Length (16bit): 0x0008 (nb-SRGB is 1) + # Flags (16bit): 0x0000 + # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) + route 3.0.0.2/32 next-hop 10.0.0.101 label [800002] attribute [0x28 0xc0 0x0100070000000000000203000800000c350000000a]; + + # ExaBGP generic-attribute binary pattern: + # Attribute-type: 0x28 (40:BGP_PREFIX_SID) + # Attribute-flag: 0xc0 (Option, Transitive) + # Attribute-body: Label-Index TLV and Originator SRGB TLV + # Label-Index TLV: 0x01000700000000000001 + # Type (08bit): 0x01 + # Length (16bit): 0x0007 + # RESERVED (08bit): 0x00 + # Flags (16bit): 0x0000 + # Label Index (32bit): 0x00000003 + # Originator SRGB TLV: 0x03000800000c350000000a + # Type (08bit): 0x03 + # Length (16bit): 0x0008 (nb-SRGB is 1) + # Flags (16bit): 0x0000 + # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) + route 3.0.0.3/32 next-hop 10.0.0.101 label [800003] attribute [0x28 0xc0 0x0100070000000000000303000800000c350000000a]; + } + } +} diff --git a/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg b/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg new file mode 100644 index 0000000..379d0a3 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg @@ -0,0 +1,19 @@ +group controller { + + process receive-routes { + run "/etc/exabgp/exa-receive.py --no-timestamp 2"; + receive-routes; + encoder json; + } + + neighbor 10.0.0.1 { + router-id 10.0.0.102; + local-address 10.0.0.102; + local-as 3; + peer-as 1; + + family { + ipv4 nlri-mpls; + } + } +} diff --git a/tests/topotests/bgp_prefix_sid/r1/bgpd.conf b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf new file mode 100644 index 0000000..e02226f --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf @@ -0,0 +1,17 @@ +log stdout notifications +log commands +! +router bgp 1 + bgp router-id 10.0.0.1 + no bgp default ipv4-unicast + no bgp ebgp-requires-policy + neighbor 10.0.0.101 remote-as 2 + neighbor 10.0.0.101 timers 3 10 + neighbor 10.0.0.102 remote-as 3 + neighbor 10.0.0.102 timers 3 10 + ! + address-family ipv4 labeled-unicast + neighbor 10.0.0.101 activate + neighbor 10.0.0.102 activate + exit-address-family +! diff --git a/tests/topotests/bgp_prefix_sid/r1/zebra.conf b/tests/topotests/bgp_prefix_sid/r1/zebra.conf new file mode 100644 index 0000000..0cd2605 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/r1/zebra.conf @@ -0,0 +1,7 @@ +hostname r1 +! +interface r1-eth0 + ip address 10.0.0.1/24 + no shutdown +! +line vty diff --git a/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py new file mode 100644 index 0000000..bfc083b --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_prefix_sid.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by LINE Corporation +# Copyright (c) 2020 by Hiroki Shirokura +# + +""" +test_bgp_prefix_sid.py: Test BGP topology with EBGP on prefix-sid +""" + +import json +import os +import sys +import functools +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + router = tgen.add_router("r1") + switch = tgen.add_switch("s1") + switch.add_link(router) + + switch = tgen.gears["s1"] + peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.101", defaultRoute="via 10.0.0.1") + peer2 = tgen.add_exabgp_peer("peer2", ip="10.0.0.102", defaultRoute="via 10.0.0.1") + switch.add_link(peer1) + switch.add_link(peer2) + + +def setup_module(module): + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + router = tgen.gears["r1"] + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format("r1")) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format("r1")) + ) + router.start() + + logger.info("starting exaBGP on peer1") + peer_list = tgen.exabgp_peers() + for pname, peer in peer_list.items(): + peer_dir = os.path.join(CWD, pname) + env_file = os.path.join(CWD, "exabgp.env") + logger.info("Running ExaBGP peer") + peer.start(peer_dir, env_file) + logger.info(pname) + + +def teardown_module(module): + tgen = get_topogen() + tgen.stop_topology() + + +def test_r1_receive_and_advertise_prefix_sid_type1(): + tgen = get_topogen() + router = tgen.gears["r1"] + + def _check_type1_r1(router, prefix, remoteLabel, labelIndex): + output = router.vtysh_cmd( + "show bgp ipv4 labeled-unicast {} json".format(prefix) + ) + output = json.loads(output) + expected = { + "prefix": prefix, + "advertisedTo": {"10.0.0.101": {}, "10.0.0.102": {}}, + "paths": [ + { + "valid": True, + "remoteLabel": remoteLabel, + "labelIndex": labelIndex, + } + ], + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_check_type1_r1, router, "3.0.0.1/32", 800001, 1) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, 'Failed _check_type1_r1 in "{}"'.format(router) + + test_func = functools.partial(_check_type1_r1, router, "3.0.0.2/32", 800002, 2) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, 'Failed _check_type1_r1 in "{}"'.format(router) + + +def exabgp_get_update_prefix(filename, afi, nexthop, prefix): + with open(filename) as f: + for line in f.readlines(): + output = json.loads(line) + ret = output.get("neighbor") + if ret is None: + continue + ret = ret.get("message") + if ret is None: + continue + ret = ret.get("update") + if ret is None: + continue + ret = ret.get("announce") + if ret is None: + continue + ret = ret.get(afi) + if ret is None: + continue + ret = ret.get(nexthop) + if ret is None: + continue + ret = ret.get(prefix) + if ret is None: + continue + return output + return "Not found" + + +def test_peer2_receive_prefix_sid_type1(): + tgen = get_topogen() + peer2 = tgen.gears["peer2"] + logfile = "{}/{}-received.log".format(peer2.gearlogdir, peer2.name) + + def _check_type1_peer2(prefix, labelindex): + output = exabgp_get_update_prefix( + logfile, "ipv4 nlri-mpls", "10.0.0.101", prefix + ) + expected = { + "type": "update", + "neighbor": { + "ip": "10.0.0.1", + "message": { + "update": { + "attribute": { + "attribute-0x28-0xE0": "0x010007000000{:08x}".format( + labelindex + ) + }, + "announce": {"ipv4 nlri-mpls": {"10.0.0.101": {}}}, + } + }, + }, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_check_type1_peer2, "3.0.0.1/32", labelindex=1) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, 'Failed _check_type1_peer2 in "{}"'.format("peer2") + + test_func = functools.partial(_check_type1_peer2, "3.0.0.2/32", labelindex=2) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, 'Failed _check_type1_peer2 in "{}"'.format("peer2") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + ret = pytest.main(args) + sys.exit(ret) diff --git a/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg new file mode 100644 index 0000000..3819179 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg @@ -0,0 +1,29 @@ +group controller { + neighbor 10.0.0.1 { + router-id 10.0.0.101; + local-address 10.0.0.101; + local-as 2; + peer-as 1; + + family { + ipv6 mpls-vpn; + } + + static { + route 2001:1::/64 { + rd 2:10; + next-hop 2001::2; + extended-community [ target:2:10 ]; + label 3; + attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ]; + } + route 2001:2::/64 { + rd 2:10; + next-hop 2001::2; + extended-community [ target:2:10 ]; + label 3; + attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ]; + } + } + } +} diff --git a/tests/topotests/bgp_prefix_sid2/peer1/exabgp.env b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.env new file mode 100644 index 0000000..6c554f5 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.env @@ -0,0 +1,53 @@ + +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_prefix_sid2/r1/bgpd.conf b/tests/topotests/bgp_prefix_sid2/r1/bgpd.conf new file mode 100644 index 0000000..ddc1f07 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid2/r1/bgpd.conf @@ -0,0 +1,26 @@ +log stdout notifications +log monitor notifications +!log commands +! +!debug bgp zebra +!debug bgp neighbor-events +!debug bgp vnc verbose +!debug bgp update-groups +!debug bgp updates in +!debug bgp updates out +!debug bgp vpn label +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp vpn rmap-event +! +router bgp 1 + bgp router-id 10.0.0.1 + no bgp default ipv4-unicast + no bgp ebgp-requires-policy + neighbor 10.0.0.101 remote-as 2 + neighbor 10.0.0.101 timers 3 10 + ! + address-family ipv6 vpn + neighbor 10.0.0.101 activate + exit-address-family +! diff --git a/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json new file mode 100644 index 0000000..42293b1 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json @@ -0,0 +1,50 @@ +{ + "2:10":{ + "prefix":"2001:1::\/64", + "advertisedTo":{ + "10.0.0.101":{ + } + }, + "paths":[ + { + "aspath":{ + "string":"2", + "segments":[ + { + "type":"as-sequence", + "list":[ + 2 + ] + } + ], + "length":1 + }, + "origin":"IGP", + "valid":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"RT:2:10" + }, + "remoteLabel":3, + "remoteSid":"2001:db8:1:1::1", + "nexthops":[ + { + "ip":"2001::2", + "afi":"ipv6", + "scope":"global", + "metric":0, + "accessible":true, + "used":true + } + ], + "peer":{ + "peerId":"10.0.0.101", + "routerId":"10.0.0.101", + "type":"external" + } + } + ] + } +} diff --git a/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json new file mode 100644 index 0000000..c9ad871 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json @@ -0,0 +1,50 @@ +{ + "2:10":{ + "prefix":"2001:2::\/64", + "advertisedTo":{ + "10.0.0.101":{ + } + }, + "paths":[ + { + "aspath":{ + "string":"2", + "segments":[ + { + "type":"as-sequence", + "list":[ + 2 + ] + } + ], + "length":1 + }, + "origin":"IGP", + "valid":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"RT:2:10" + }, + "remoteLabel":3, + "remoteSid":"2001:db8:1:1::1", + "nexthops":[ + { + "ip":"2001::2", + "afi":"ipv6", + "scope":"global", + "metric":0, + "accessible":true, + "used":true + } + ], + "peer":{ + "peerId":"10.0.0.101", + "routerId":"10.0.0.101", + "type":"external" + } + } + ] + } +} diff --git a/tests/topotests/bgp_prefix_sid2/r1/zebra.conf b/tests/topotests/bgp_prefix_sid2/r1/zebra.conf new file mode 100644 index 0000000..0cd2605 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid2/r1/zebra.conf @@ -0,0 +1,7 @@ +hostname r1 +! +interface r1-eth0 + ip address 10.0.0.1/24 + no shutdown +! +line vty diff --git a/tests/topotests/bgp_prefix_sid2/test_bgp_prefix_sid2.py b/tests/topotests/bgp_prefix_sid2/test_bgp_prefix_sid2.py new file mode 100755 index 0000000..d6b9432 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid2/test_bgp_prefix_sid2.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_prefix_sid2.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by LINE Corporation +# Copyright (c) 2020 by Hiroki Shirokura +# + +""" +test_bgp_prefix_sid2.py: Test BGP topology with EBGP on prefix-sid +""" + +import json +import os +import sys +import functools +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + router = tgen.add_router("r1") + switch = tgen.add_switch("s1") + switch.add_link(router) + + switch = tgen.gears["s1"] + peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.101", defaultRoute="via 10.0.0.1") + switch.add_link(peer1) + + +def setup_module(module): + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + router = tgen.gears["r1"] + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format("r1")) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format("r1")) + ) + router.start() + + logger.info("starting exaBGP") + peer_list = tgen.exabgp_peers() + for pname, peer in peer_list.items(): + logger.info("starting exaBGP on {}".format(pname)) + peer_dir = os.path.join(CWD, pname) + env_file = os.path.join(CWD, pname, "exabgp.env") + logger.info("Running ExaBGP peer on {}".format(pname)) + peer.start(peer_dir, env_file) + logger.info(pname) + + +def teardown_module(module): + tgen = get_topogen() + tgen.stop_topology() + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def test_r1_rib(): + def _check(name, cmd, expected_file): + logger.info("polling") + tgen = get_topogen() + router = tgen.gears[name] + output = json.loads(router.vtysh_cmd(cmd)) + expected = open_json_file("{}/{}".format(CWD, expected_file)) + return topotest.json_cmp(output, expected) + + def check(name, cmd, expected_file): + logger.info('[+] check {} "{}" {}'.format(name, cmd, expected_file)) + tgen = get_topogen() + func = functools.partial(_check, name, cmd, expected_file) + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "Failed" + + check("r1", "show bgp ipv6 vpn 2001:1::/64 json", "r1/vpnv6_rib_entry1.json") + check("r1", "show bgp ipv6 vpn 2001:2::/64 json", "r1/vpnv6_rib_entry2.json") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + ret = pytest.main(args) + sys.exit(ret) diff --git a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/bgp_recursive_route_ebgp_multi_hop.json b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/bgp_recursive_route_ebgp_multi_hop.json new file mode 100644 index 0000000..52995a0 --- /dev/null +++ b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/bgp_recursive_route_ebgp_multi_hop.json @@ -0,0 +1,321 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": { + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": {} + } + }, + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + } + } +} diff --git a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py new file mode 100644 index 0000000..35459f6 --- /dev/null +++ b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py @@ -0,0 +1,2407 @@ +#!/usr/bin/python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test bgp recursive route and ebgp +multi-hop functionality: + +1. Verify that BGP routes are installed in iBGP peer, only when there + is a recursive route for next-hop reachability. +2. Verify that any BGP prefix received with next hop as self-ip is + not installed in BGP RIB or FIB table. +3. Verify password authentication for eBGP and iBGP peers. +4. Verify that for a BGP prefix next-hop information doesn't change + when same prefix is received from another peer via recursive lookup. +5. Verify that BGP path attributes are present in CLI outputs and + JSON format, even if set to default. +6. Verifying the BGP peering between loopback and physical link's IP + of 2 peer routers. +7. Verify that BGP Active/Standby/Pre-emption/ECMP. +""" + +import os +import sys +import time +import pytest +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + apply_raw_config, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_route_maps, + create_interface_in_kernel, + shutdown_bringup_interface, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, + verify_bgp_convergence_from_running_config, + modify_as_number, + verify_bgp_attributes, + clear_bgp, +) +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +# Global variables +BGP_CONVERGENCE = False +KEEP_ALIVE_TIMER = 2 +HOLD_DOWN_TIMER = 6 +ADDR_TYPES = check_address_types() +NETWORK = { + "ipv4": ["100.1.1.1/32", "100.1.1.2/32"], + "ipv6": ["100::1/128", "100::2/128"], +} + +RECUR_NEXT_HOP = { + "N1": {"ipv4": "20.20.20.20/24", "ipv6": "20:20::20:20/120"}, + "N2": {"ipv4": "30.30.30.30/24", "ipv6": "30:30::30:30/120"}, + "N3": {"ipv4": "40.40.40.40/24", "ipv6": "40:40::40:40/120"}, +} + +CHANGED_NEXT_HOP = { + "4thOctate": {"ipv4": "10.0.1.250/24", "ipv6": "fd00:0:0:1::100/64"}, + "3rdOctate": {"ipv4": "10.0.10.2/24", "ipv6": "fd00:0:0:10::2/64"}, +} + +Loopabck_IP = { + "Lo_R1": {"ipv4": "1.1.1.1/32", "ipv6": "1:1::1:1/128"}, + "Lo_R4": {"ipv4": "4.4.4.4/32", "ipv6": "4:4::4:4/128"}, +} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_recursive_route_ebgp_multi_hop.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error : {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_recursive_routes_iBGP_peer_p1(request): + """ + Verify that BGP routes are installed in iBGP peer, only + when there is a recursive route for next-hop reachability. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Initial config :Configure BGP neighborship between R1 and R3.") + reset_config_on_routers(tgen) + + dut = "r1" + protocol = "static" + + step( + "Configure static routes on R1 pointing next-hop as connected" + "link between R1 & R3's IP" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r3"]["links"]["r1"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = create_static_routes(tgen, input_dict_4) + + step( + "Verify on router R1 that these static routes are " + "installed in RIB+FIB of R1" + ) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Redistribute these static routes in BGP on router R1") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + + step( + "Verify on router R1 that these static routes are installed" + "in RIB table as well" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r3"]["links"]["r1"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = verify_bgp_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0], + ) + assert result is True, "Testcase {}: Failed \n Error : {}".format( + tc_name, result + ) + + step( + "Configure a static routes for next hop IP on R2 via multiple" + "recursive static routes" + ) + dut = "r2" + create_interface_in_kernel( + tgen, dut, "lo", "40.40.40.50", netmask="255.255.255.0", create=True + ) + create_interface_in_kernel( + tgen, dut, "lo", "40:40::40:50", netmask="120", create=True + ) + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r2": { + "static_routes": [ + { + "network": topo["routers"]["r3"]["links"]["r1"][addr_type], + "next_hop": RECUR_NEXT_HOP["N1"][addr_type].split("/")[0], + }, + { + "network": RECUR_NEXT_HOP["N1"][addr_type], + "next_hop": RECUR_NEXT_HOP["N2"][addr_type].split("/")[0], + }, + { + "network": RECUR_NEXT_HOP["N2"][addr_type], + "next_hop": RECUR_NEXT_HOP["N3"][addr_type].split("/")[0], + }, + ] + } + } + result = create_static_routes(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed \n Error : {}".format( + tc_name, result + ) + + step("verify if redistributed routes are now installed in FIB of R2") + result = verify_rib( + tgen, + addr_type, + "r2", + input_dict_4, + next_hop=topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0], + protocol="bgp", + ) + assert result is True, "Testcase {}: Failed \n Error : {}".format( + tc_name, result + ) + + step("Delete 1 route from static recursive for the next-hop IP") + dut = "r2" + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r2": { + "static_routes": [ + { + "network": RECUR_NEXT_HOP["N1"][addr_type], + "next_hop": RECUR_NEXT_HOP["N2"][addr_type].split("/")[0], + "delete": True, + } + ] + } + } + result = create_static_routes(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed \n Error : {}".format( + tc_name, result + ) + + step("Verify that redistributed routes are withdrawn from FIB of R2") + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0], + protocol="bgp", + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format( + tc_name, result + ) + + step("Reconfigure the same static route on R2 again") + dut = "r2" + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r2": { + "static_routes": [ + { + "network": RECUR_NEXT_HOP["N1"][addr_type], + "next_hop": RECUR_NEXT_HOP["N2"][addr_type].split("/")[0], + } + ] + } + } + result = create_static_routes(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed \n Error : {}".format( + tc_name, result + ) + + step("Verify that redistributed routes are again installed" "in FIB of R2") + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0], + protocol="bgp", + ) + assert result is True, "Testcase {}: Failed \n Error : {}".format( + tc_name, result + ) + + step("Configure static route with changed next-hop from same subnet") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r3"]["links"]["r1"][ + addr_type + ].split("/")[0], + "delete": True, + }, + { + "network": NETWORK[addr_type], + "next_hop": CHANGED_NEXT_HOP["4thOctate"][addr_type].split("/")[ + 0 + ], + }, + ] + } + } + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {}: Failed \n Error : {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_dict_4, protocol="static") + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step( + "Verify that redistributed routes are not withdrawn as changed" + "next-hop IP, belongs to the same subnet" + ) + result = verify_rib(tgen, addr_type, "r2", input_dict_4, protocol="bgp") + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Configure static route with changed next-hop from different subnet") + dut = "r1" + create_interface_in_kernel( + tgen, dut, "lo10", "10.0.10.10", netmask="255.255.255.0", create=True + ) + create_interface_in_kernel( + tgen, dut, "lo10", "fd00:0:0:10::104", netmask="64", create=True + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": CHANGED_NEXT_HOP["4thOctate"][addr_type].split("/")[ + 0 + ], + "delete": True, + }, + { + "network": NETWORK[addr_type], + "next_hop": CHANGED_NEXT_HOP["3rdOctate"][addr_type].split("/")[ + 0 + ], + }, + ] + } + } + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {}: Failed \n Error : {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_dict_4, protocol="static") + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step( + "Verify that redistributed routes are withdrawn as changed " + "next-hop IP, belongs to different subnet" + ) + result = verify_rib( + tgen, addr_type, "r2", input_dict_4, protocol="bgp", expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_next_hop_as_self_ip_p1(request): + """ + Verify that any BGP prefix received with next hop as + self-ip is not installed in BGP RIB or FIB table. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Initial config :Configure BGP neighborship between R1 and R3.") + reset_config_on_routers(tgen) + + step( + "Configure static routes on R1 with a next-hop IP belonging" + "to the same subnet of R2's link IP." + ) + dut = "r1" + create_interface_in_kernel( + tgen, + dut, + "lo10", + topo["routers"]["r4"]["links"]["r2"]["ipv4"].split("/")[0], + netmask="255.255.255.0", + create=True, + ) + create_interface_in_kernel( + tgen, + dut, + "lo10", + topo["routers"]["r4"]["links"]["r2"]["ipv6"].split("/")[0], + netmask="64", + create=True, + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r2"]["links"]["r4"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = create_static_routes(tgen, input_dict_4) + + step("Verify that static routes are installed in RIB and FIB of R1") + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + protocol="static", + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Redistribute static routes into BGP on R1") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + + step( + "Verify that R2 denies the prefixes received in update message," + "as next-hop IP belongs to connected interface" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r2"]["links"]["r4"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + input_dict_4, + next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format( + tc_name, result + ) + + step("Shut interface on R2 that has IP from the subnet as BGP next-hop") + intf_r2_r4 = topo["routers"]["r2"]["links"]["r4"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_r4) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r2") + step( + "Verify that redistributed routes now appear only in BGP table," + "as next-hop IP is no more active on R2" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r2"]["links"]["r4"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + input_dict_4, + next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + ) + assert result is True, "Testcase {}: Failed \n Error : {}".format( + tc_name, result + ) + + step("No shutdown interface on R2 which was shut in previous step") + intf_r2_r4 = topo["routers"]["r2"]["links"]["r4"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_r4, ifaceaction=True) + + step( + "Verify that R2 dosn't install prefixes RIB to FIB as next-hop" + "interface is up now" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r2"]["links"]["r4"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + input_dict_4, + next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + ) + assert result is True, "Testcase {}: Failed \n Error : {}".format( + tc_name, result + ) + result = verify_rib( + tgen, + addr_type, + "r2", + input_dict_4, + next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_next_hop_with_recursive_lookup_p1(request): + """ + Verify that for a BGP prefix next-hop information doesn't change + when same prefix is received from another peer via recursive lookup. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Initial config :Configure BGP neighborship between R1 and R3.") + reset_config_on_routers(tgen) + + step("Verify that BGP peering comes up.") + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Do redistribute connected on router R3.") + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify that R1 receives all connected") + for addr_type in ADDR_TYPES: + routes = { + "ipv4": ["1.0.3.17/32", "10.0.1.0/24", "10.0.3.0/24"], + "ipv6": ["2001:db8:f::3:17/128", "fd00:0:0:1::/64", "fd00:0:0:3::/64"], + } + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + result = verify_rib(tgen, addr_type, "r1", input_dict, protocol="bgp") + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step( + "Configure a BGP neighborship between R1 and R4, directly via " + "eBGP multi-hop." + ) + r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"] + r1_r3_addr = topo["routers"]["r1"]["links"]["r3"] + r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"] + r4_r3_addr = topo["routers"]["r4"]["links"]["r3"] + ebgp_multi_hop = 3 + + for addr_type in ADDR_TYPES: + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(r1_local_as), + "neighbor {} remote-as {}".format( + r4_r3_addr[addr_type].split("/")[0], r4_local_as + ), + "neighbor {} timers {} {}".format( + r4_r3_addr[addr_type].split("/")[0], + KEEP_ALIVE_TIMER, + HOLD_DOWN_TIMER, + ), + "neighbor {} ebgp-multihop {}".format( + r4_r3_addr[addr_type].split("/")[0], ebgp_multi_hop + ), + ] + }, + "r4": { + "raw_config": [ + "router bgp {}".format(r4_local_as), + "neighbor {} remote-as {}".format( + r1_r3_addr[addr_type].split("/")[0], r1_local_as + ), + "neighbor {} timers {} {}".format( + r1_r3_addr[addr_type].split("/")[0], + KEEP_ALIVE_TIMER, + HOLD_DOWN_TIMER, + ), + "neighbor {} ebgp-multihop {}".format( + r1_r3_addr[addr_type].split("/")[0], ebgp_multi_hop + ), + ] + }, + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + if addr_type == "ipv4": + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(r1_local_as), + "address-family {} unicast".format(addr_type), + "no neighbor {} activate".format( + r4_r3_addr["ipv6"].split("/")[0] + ), + ] + }, + "r4": { + "raw_config": [ + "router bgp {}".format(r4_local_as), + "address-family {} unicast".format(addr_type), + "no neighbor {} activate".format( + r1_r3_addr["ipv6"].split("/")[0] + ), + ] + }, + } + else: + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(r1_local_as), + "address-family {} unicast".format(addr_type), + "neighbor {} activate".format( + r4_r3_addr[addr_type].split("/")[0] + ), + ] + }, + "r4": { + "raw_config": [ + "router bgp {}".format(r4_local_as), + "address-family {} unicast".format(addr_type), + "neighbor {} activate".format( + r1_r3_addr[addr_type].split("/")[0] + ), + ] + }, + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + step("Verify that BGP session between R1 and R4 comes up" "(recursively via R3).") + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Do redistribute connected on router R4.") + input_dict_1 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step( + "Verify that R1 now receives BGP prefix of link r3-r4 via 2 " + "next-hops R3 and R4. however do not install with NHT R4 in FIB." + ) + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_rib( + tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Clear bgp sessions from R1 using 'clear ip bgp *'") + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r1") + + step( + "Verify that prefix of link r3-r4 is again learned via 2 " + "next-hops (from R3 and R4 directly)" + ) + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_rib( + tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Remove redistribution from router R3.") + input_dict_1 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "connected", "delete": True} + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "connected", "delete": True} + ] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step( + "Verify that peering between R1-R4 goes down and prefix " + "of link r3-r4, with NHT R4 is withdrawn." + ) + + logger.info("Sleeping for holddowntimer: {}".format(HOLD_DOWN_TIMER)) + sleep(HOLD_DOWN_TIMER + 1) + + result = verify_bgp_convergence_from_running_config(tgen, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n" "BGP is converged \n Error : {}".format( + tc_name, result + ) + logger.info("Expected behaviour: {}".format(result)) + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_rib( + tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Re-apply redistribution on R3.") + + input_dict_1 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step( + "Verify that peering between R1-R4 goes down and prefix " + "of link r3-r4 with NHT R4 is withdrawn." + ) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_rib( + tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Remove redistribution from router R4.") + + input_dict_1 = { + "r4": { + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "connected", "delete": True} + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "connected", "delete": True} + ] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step( + "Verify that peering between R1-R4 doesn't go down but prefix " + "of link r3-r4 with NHT R4 is withdrawn." + ) + + logger.info("Sleeping for holddowntimer: {}".format(HOLD_DOWN_TIMER)) + sleep(HOLD_DOWN_TIMER + 1) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r4"]["links"]["r3"][addr_type].split("/")[0] + + result = verify_rib( + tgen, + addr_type, + "r1", + input_dict, + protocol="bgp", + next_hop=next_hop, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Route is still present \n Error : {}".format( + tc_name, result + ) + + step("Re-apply redistribution on R4.") + + input_dict_1 = { + "r4": { + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "connected", "delete": True} + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "connected", "delete": True} + ] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify that prefix of link r3-r4 is re-learned via NHT R4.") + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_rib( + tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Toggle the interface on R3.") + + intf_r3_r4 = topo["routers"]["r3"]["links"]["r4"]["interface"] + shutdown_bringup_interface(tgen, "r3", intf_r3_r4) + + step( + "Verify that peering between R1-R4 goes down and comes up when " + "interface is toggled. Also prefix of link r3-r4(via both NHTs) is" + " withdrawn and re-learned accordingly." + ) + + result = verify_bgp_convergence_from_running_config(tgen, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n" "BGP is converged \n Error : {}".format( + tc_name, result + ) + logger.info("Expected behaviour: {}".format(result)) + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r4"]["links"]["r3"][addr_type].split("/")[0] + + result = verify_rib( + tgen, + addr_type, + "r1", + input_dict, + protocol="bgp", + next_hop=next_hop, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Route is still present \n Error : {}".format( + tc_name, result + ) + + shutdown_bringup_interface(tgen, "r3", intf_r3_r4, True) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_rib( + tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Toggle the interface on R4.") + + intf_r4_r3 = topo["routers"]["r4"]["links"]["r3"]["interface"] + shutdown_bringup_interface(tgen, "r4", intf_r4_r3) + + step( + "Verify that peering between R1-R4 goes down and comes up when" + "interface is toggled. Also prefix of link r3-r4(via R4)" + " is withdrawn and re-learned accordingly." + ) + + result = verify_bgp_convergence_from_running_config(tgen, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n" "BGP is converged \n Error : {}".format( + tc_name, result + ) + logger.info("Expected behaviour: {}".format(result)) + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r4"]["links"]["r3"][addr_type].split("/")[0] + + result = verify_rib( + tgen, + addr_type, + "r1", + input_dict, + protocol="bgp", + next_hop=next_hop, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Route is still present \n Error : {}".format( + tc_name, result + ) + + shutdown_bringup_interface(tgen, "r4", intf_r4_r3, True) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_rib( + tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_path_attributes_default_values_p1(request): + """ + Verify that BGP path attributes are present in CLI + outputs and JSON format, even if set to default. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Initial config: Configure BGP neighborship, between R1-R2 & R1-R3") + reset_config_on_routers(tgen) + + step("Advertise a set of prefixes from R1 to both peers R2 and R3") + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [{"network": NETWORK[addr_type], "next_hop": "null0"}] + } + } + result = create_static_routes(tgen, input_dict_1) + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + + step( + "Verify that advertised prefixes are received on R4 and well" + "known attributes are present in the CLI and JSON outputs with" + "default values without any route-map config." + ) + for addr_type in ADDR_TYPES: + input_dict_3 = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib( + tgen, + addr_type, + "r4", + input_dict_3, + next_hop=[ + topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + ], + ) + assert result is True, "Testcase {}: Failed \n Error : {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r4": { + "route_maps": { + "rmap_pf": [{"set": {"origin": "incomplete", "aspath": "300 100"}}] + } + } + } + + result = verify_bgp_attributes( + tgen, + addr_type, + "r4", + NETWORK[addr_type], + rmap_name="rmap_pf", + input_dict=input_dict_4, + ) + assert result is True, "Testcase {}: Failed \n Error : {}".format( + tc_name, result + ) + + step( + "Configure a route-map to set below attribute value as 500" + "and apply on R4 in an inbound direction" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r4": { + "route_maps": { + "Path_Attribue": [ + { + "action": "permit", + "set": { + "path": {"as_num": 500, "as_action": "prepend"}, + "locPrf": 500, + "origin": "egp", + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + input_dict_5 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step( + "Verify that once the route-map is applied all the attributes" + "part of route-map, changes value to 500" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r4": { + "route_maps": { + "rmap_pf": [ + { + "set": { + "locPrf": 500, + "aspath": "500 300 100", + "origin": "EGP", + } + } + ] + } + } + } + result = verify_bgp_attributes( + tgen, + addr_type, + "r4", + NETWORK[addr_type], + rmap_name="rmap_pf", + input_dict=input_dict_4, + ) + assert result is True, "Testcase {}: Failed \n Error : {}".format( + tc_name, result + ) + + step("Remove the route-map from R4") + input_dict_5 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step( + "Verify on R4 that well known attributes are present in the CLI &" + "JSON outputs again with default values without route-map config" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r4": { + "route_maps": { + "rmap_pf": [{"set": {"aspath": "300 100", "origin": "incomplete"}}] + } + } + } + result = verify_bgp_attributes( + tgen, + addr_type, + "r4", + NETWORK[addr_type], + rmap_name="rmap_pf", + input_dict=input_dict_4, + nexthop=None, + ) + assert result is True, "Testcase {}: Failed \n Error : {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_peering_bw_loopback_and_physical_p1(request): + """ + Verifying the BGP peering between loopback and + physical link's IP of 2 peer routers. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Initial config :Configure BGP neighborship between R1 and R3.") + reset_config_on_routers(tgen) + + step("Configure a loopback interface on R1") + dut = "r1" + create_interface_in_kernel( + tgen, dut, "lo10", "1.1.1.1", netmask="255.255.255.255", create=True + ) + create_interface_in_kernel( + tgen, dut, "lo10", "1:1::1:1", netmask="128", create=True + ) + + step("Configure BGP session between R1's loopbak & R3") + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": Loopabck_IP["Lo_R1"][addr_type], + "next_hop": topo["routers"]["r1"]["links"]["r3"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + result = verify_rib( + tgen, + addr_type, + "r3", + input_dict_1, + protocol="static", + next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0], + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r1"]["bgp"]["local_as"]), + "address-family {} unicast".format(addr_type), + "neighbor {} update-source lo10".format( + topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + ), + "neighbor {} timers 1 3".format( + topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + ), + ] + }, + "r3": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r3"]["bgp"]["local_as"]), + "address-family {} unicast".format(addr_type), + "no neighbor {} remote-as {}".format( + topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0], + topo["routers"]["r1"]["bgp"]["local_as"], + ), + "neighbor {} remote-as {}".format( + Loopabck_IP["Lo_R1"][addr_type].split("/")[0], + topo["routers"]["r1"]["bgp"]["local_as"], + ), + "neighbor {} ebgp-multihop 3".format( + Loopabck_IP["Lo_R1"][addr_type].split("/")[0] + ), + ] + }, + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + if addr_type == "ipv6": + raw_config = { + "r3": { + "raw_config": [ + "router bgp {}".format( + topo["routers"]["r3"]["bgp"]["local_as"] + ), + "address-family {} unicast".format(addr_type), + "neighbor {} activate".format( + Loopabck_IP["Lo_R1"][addr_type].split("/")[0] + ), + ] + } + } + else: + raw_config = { + "r3": { + "raw_config": [ + "router bgp {}".format( + topo["routers"]["r3"]["bgp"]["local_as"] + ), + "address-family {} unicast".format(addr_type), + "no neighbor {} activate".format( + Loopabck_IP["Lo_R1"]["ipv6"].split("/")[0] + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + step("Verify that BGP neighborship between R1 and R3 comes up") + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Remove ebgp-multihop command from R3") + for addr_type in ADDR_TYPES: + raw_config = { + "r3": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r3"]["bgp"]["local_as"]), + "no neighbor {} ebgp-multihop 3".format( + Loopabck_IP["Lo_R1"][addr_type].split("/")[0] + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + step("Verify that once eBGP multi-hop is removed, BGP session goes down") + result = verify_bgp_convergence_from_running_config(tgen, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format( + tc_name, result + ) + + step("Add ebgp-multihop command on R3 again") + for addr_type in ADDR_TYPES: + raw_config = { + "r3": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r3"]["bgp"]["local_as"]), + "neighbor {} ebgp-multihop 3".format( + Loopabck_IP["Lo_R1"][addr_type].split("/")[0] + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + step("Verify that BGP neighborship between R1 and R3 comes up") + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Remove update-source command from R1") + for addr_type in ADDR_TYPES: + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r1"]["bgp"]["local_as"]), + "no neighbor {} update-source lo10".format( + topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + step("Verify that BGP session goes down, when update-source is removed") + result = verify_bgp_convergence_from_running_config(tgen, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format( + tc_name, result + ) + + step("Add update-source command on R1 again") + for addr_type in ADDR_TYPES: + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r1"]["bgp"]["local_as"]), + "neighbor {} update-source lo10".format( + topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + step("Verify that BGP neighborship between R1 and R3 comes up") + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Remove static route from R3") + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": Loopabck_IP["Lo_R1"][addr_type], + "next_hop": topo["routers"]["r1"]["links"]["r3"][ + addr_type + ].split("/")[0], + "delete": True, + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + result = verify_rib( + tgen, + addr_type, + "r3", + input_dict_1, + protocol="static", + next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format( + tc_name, result + ) + + sleep(3) + step("Verify that BGP session goes down, when static route is removed") + result = verify_bgp_convergence_from_running_config(tgen, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format( + tc_name, result + ) + + step("Add static route on R3 again") + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": Loopabck_IP["Lo_R1"][addr_type], + "next_hop": topo["routers"]["r1"]["links"]["r3"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + result = verify_rib( + tgen, + addr_type, + "r3", + input_dict_1, + protocol="static", + next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0], + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Verify that BGP neighborship between R1 and R3 comes up") + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Toggle physical interface on R1") + intf_r1_r3 = topo["routers"]["r1"]["links"]["r3"]["interface"] + shutdown_bringup_interface(tgen, "r1", intf_r1_r3) + sleep(3) + step("Verify that BGP neighborship between R1 and R3 goes down") + result = verify_bgp_convergence_from_running_config(tgen, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format( + tc_name, result + ) + + intf_r1_r3 = topo["routers"]["r1"]["links"]["r3"]["interface"] + shutdown_bringup_interface(tgen, "r1", intf_r1_r3, True) + + step("Verify that BGP neighborship between R1 and R3 comes up") + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_BGP_active_standby_preemption_and_ecmp_p1(request): + """ + Verify that BGP Active/Standby/Pre-emption/ECMP. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Initial config :Configure BGP neighborship between R1 and R3.") + reset_config_on_routers(tgen) + + step("Change the AS number on R2 as 200") + input_dict = {"r2": {"bgp": {"local_as": 200}}} + result = modify_as_number(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify BGP converge after changing the AS number on R2") + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Advertise a set of prefixes from R1 to both peers R2 & R3") + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [{"network": NETWORK[addr_type], "next_hop": "null0"}] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify that R4 receives BGP prefixes via both peer routers R2 & R3") + for addr_type in ADDR_TYPES: + input_dict_3 = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib( + tgen, + addr_type, + "r4", + input_dict_3, + next_hop=[ + topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + ], + ) + assert result is True, "Testcase {}: Failed \n Error : {}".format( + tc_name, result + ) + + step( + "Configure a route-map to set as-path attribute and" + "apply on R3 in an inbound direction:" + ) + + input_dict_4 = { + "r3": { + "route_maps": { + "Path_Attribue": [ + { + "action": "permit", + "set": {"path": {"as_num": 123, "as_action": "prepend"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + input_dict_5 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify on R4, BGP routes with shorter as-path are installed in FIB") + for addr_type in ADDR_TYPES: + dut = "r4" + protocol = "bgp" + input_dict_6 = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_6, + next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Shutdown BGP neighorship between R1-R2") + dut = "r4" + intf_r4_r2 = topo["routers"]["r4"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf_r4_r2) + + step( + "Verify that prefixes from next-hop via R2 are withdrawn" + "and installed via next-hop as R3" + ) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_2, + next_hop=topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Do a no shut for BGP neighorship between R2-R4") + shutdown_bringup_interface(tgen, dut, intf_r4_r2, ifaceaction=True) + + step( + "Verify that prefixes from next-hop via R3 are withdrawn" + "from R4 and installed via next-hop as R2 (preemption)" + ) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_2, + next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Remove the route-map from R3's neighbor statement") + input_dict_5 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Configure multipath-relax and maximum-paths 2 on R4 for ECMP") + input_dict_8 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"maximum_paths": {"ebgp": 2}}}, + "ipv6": {"unicast": {"maximum_paths": {"ebgp": 2}}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_8) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + maxpath_relax = { + "r4": {"bgp": {"local_as": "400", "bestpath": {"aspath": "multipath-relax"}}} + } + + result = create_router_bgp(tgen, topo, maxpath_relax) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify FIB of R4, BGP prefixes with ECMP next-hop via R2 and R3") + for addr_type in ADDR_TYPES: + input_dict = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_rib( + tgen, + addr_type, + "r4", + input_dict, + next_hop=[ + topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + ], + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Remove multipath-relax command from R4") + + del_maxpath_relax = { + "r4": { + "bgp": { + "local_as": "400", + "bestpath": {"aspath": "multipath-relax", "delete": True}, + } + } + } + + result = create_router_bgp(tgen, topo, del_maxpath_relax) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify that ECMP is no longer happening on R4.") + for addr_type in ADDR_TYPES: + input_dict = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_rib( + tgen, + addr_type, + "r4", + input_dict, + next_hop=[ + topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + ], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format( + tc_name, result + ) + + step("Reconfigure multipath-relax command on R4") + result = create_router_bgp(tgen, topo, maxpath_relax) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify FIB of R4, BGP prefixes with ECMP next-hop via R2 and R3") + result = verify_rib( + tgen, + addr_type, + "r4", + input_dict, + next_hop=[ + topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + ], + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Remove maximum-path 2 command from R4") + input_dict_8 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "maximum_paths": { + "ebgp": 1, + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ebgp": 1, + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_8) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify that ECMP is no longer happening on R4") + result = verify_rib( + tgen, + addr_type, + "r4", + input_dict, + next_hop=[ + topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + ], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format( + tc_name, result + ) + + step("Re-configure maximum-path 2 command on R4") + input_dict_8 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "maximum_paths": { + "ebgp": 2, + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ebgp": 2, + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_8) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify FIB of R4, BGP prefixes with ECMP next-hop via R2 and R3") + result = verify_rib( + tgen, + addr_type, + "r4", + input_dict, + next_hop=[ + topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + ], + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_password_authentication_for_eBGP_and_iBGP_peers_p1(request): + """ + Verify password authentication for eBGP and iBGP peers. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Initial config :Configure BGP neighborship between R1 and R3.") + reset_config_on_routers(tgen) + + step( + "Add a static route on R1 for loopbacks IP's reachability of R2, R3" + "and on R2 and R3 for loopback IP of R1" + ) + for addr_type in ADDR_TYPES: + nh1 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + nh2 = topo["routers"]["r1"]["links"]["r2"][addr_type].split("/")[0] + nh3 = topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0] + nh4 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": topo["routers"]["r3"]["links"]["lo"][addr_type], + "next_hop": nh1, + } + ] + } + } + input_dict_2 = { + "r2": { + "static_routes": [ + { + "network": topo["routers"]["r1"]["links"]["lo"][addr_type], + "next_hop": nh2, + } + ] + } + } + input_dict_3 = { + "r3": { + "static_routes": [ + { + "network": topo["routers"]["r1"]["links"]["lo"][addr_type], + "next_hop": nh3, + } + ] + } + } + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": topo["routers"]["r2"]["links"]["lo"][addr_type], + "next_hop": nh4, + } + ] + } + } + dut_list = ["r1", "r2", "r3", "r1"] + nexthop_list = [nh1, nh2, nh3, nh4] + input_dict_list = [input_dict_1, input_dict_2, input_dict_3, input_dict_4] + for dut, next_hop, input_dict in zip(dut_list, nexthop_list, input_dict_list): + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Verify that static routes are installed in FIB of routers") + result = verify_rib( + tgen, addr_type, dut, input_dict, next_hop=next_hop, protocol="static" + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Configure BGP sessions between R1-R2 and R1-R3 over loopback IPs") + for routerN in ["r1", "r3"]: + for addr_type in ADDR_TYPES: + if routerN == "r1": + bgp_neighbor = "r3" + elif routerN == "r3": + bgp_neighbor = "r1" + topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ][bgp_neighbor]["dest_link"] = { + "lo": {"ebgp_multihop": 2, "source_link": "lo"} + } + build_config_from_json(tgen, topo, save_bkup=False) + + for routerN in ["r1", "r2"]: + for addr_type in ADDR_TYPES: + if routerN == "r1": + bgp_neighbor = "r2" + elif routerN == "r2": + bgp_neighbor = "r1" + topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ][bgp_neighbor]["dest_link"] = {"lo": {"source_link": "lo"}} + build_config_from_json(tgen, topo, save_bkup=False) + + for routerN in ["r1", "r2", "r3"]: + for addr_type in ADDR_TYPES: + for bgp_neighbor in topo["routers"][routerN]["bgp"]["address_family"][ + addr_type + ]["unicast"]["neighbor"].keys(): + if routerN in ["r1", "r2", "r3"] and bgp_neighbor == "r4": + continue + if addr_type == "ipv4": + topo["routers"][routerN]["bgp"]["address_family"][addr_type][ + "unicast" + ]["neighbor"][bgp_neighbor]["dest_link"] = { + "lo": {"deactivate": "ipv6"} + } + elif addr_type == "ipv6": + topo["routers"][routerN]["bgp"]["address_family"][addr_type][ + "unicast" + ]["neighbor"][bgp_neighbor]["dest_link"] = { + "lo": {"deactivate": "ipv4"} + } + build_config_from_json(tgen, topo, save_bkup=False) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Configure authentication password on R1 for neighbor statements") + for bgp_neighbor in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + topo["routers"]["r1"]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ][bgp_neighbor]["dest_link"] = {"lo": {"password": "vmware"}} + build_config_from_json(tgen, topo, save_bkup=False) + + step( + "Verify that both sessions go down as only R1 has password" + "configured but not peer routers" + ) + result = verify_bgp_convergence(tgen, topo, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format( + tc_name, result + ) + + step("configure same password on R2 and R3") + for routerN in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ]["r1"]["dest_link"] = {"lo": {"password": "vmware"}} + build_config_from_json(tgen, topo, save_bkup=False) + + step("Verify that all BGP sessions come up due to identical passwords") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Configure same password on R2 and R3, but in CAPs.") + for routerN in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ]["r1"]["dest_link"] = {"lo": {"password": "VMWARE"}} + build_config_from_json(tgen, topo, save_bkup=False) + + step( + "Verify that BGP sessions do not come up as password" + "strings are in CAPs on R2 and R3" + ) + result = verify_bgp_convergence(tgen, topo, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format( + tc_name, result + ) + + step("Configure same password on R2 and R3 without CAPs") + for routerN in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ]["r1"]["dest_link"] = {"lo": {"password": "vmware"}} + build_config_from_json(tgen, topo, save_bkup=False) + + step("Verify all BGP sessions come up again due to identical passwords") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Remove password from R1") + for bgp_neighbor in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + topo["routers"]["r1"]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ][bgp_neighbor]["dest_link"] = {"lo": {"no_password": "vmware"}} + build_config_from_json(tgen, topo, save_bkup=False) + + step("Verify if password is removed from R1, both sessions go down again") + result = verify_bgp_convergence(tgen, topo, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format( + tc_name, result + ) + + step("Configure alphanumeric password on R1 and peer routers R2,R3") + for bgp_neighbor in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + topo["routers"]["r1"]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ][bgp_neighbor]["dest_link"] = {"lo": {"password": "Vmware@123"}} + build_config_from_json(tgen, topo, save_bkup=False) + + for routerN in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ]["r1"]["dest_link"] = {"lo": {"password": "Vmware@123"}} + build_config_from_json(tgen, topo, save_bkup=False) + + step( + "Verify that sessions Come up irrespective of characters" + "used in password string" + ) + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_reject_as_sets/__init__.py b/tests/topotests/bgp_reject_as_sets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf b/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf new file mode 100644 index 0000000..a28b612 --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf @@ -0,0 +1,10 @@ +! exit1 +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65002 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_reject_as_sets/r1/zebra.conf b/tests/topotests/bgp_reject_as_sets/r1/zebra.conf new file mode 100644 index 0000000..9904bb4 --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r1/zebra.conf @@ -0,0 +1,9 @@ +! exit1 +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf b/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf new file mode 100644 index 0000000..4539617 --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf @@ -0,0 +1,13 @@ +! spine +router bgp 65002 + bgp reject-as-sets + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.254.2 remote-as 65003 + neighbor 192.168.254.2 timers 3 10 + address-family ipv4 unicast + aggregate-address 172.16.0.0/16 as-set summary-only + exit-address-family + ! +! diff --git a/tests/topotests/bgp_reject_as_sets/r2/zebra.conf b/tests/topotests/bgp_reject_as_sets/r2/zebra.conf new file mode 100644 index 0000000..f0d357c --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r2/zebra.conf @@ -0,0 +1,9 @@ +! spine +interface r2-eth0 + ip address 192.168.255.1/30 +! +interface r2-eth1 + ip address 192.168.254.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf b/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf new file mode 100644 index 0000000..1dde4f0 --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf @@ -0,0 +1,11 @@ +! exit2 +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.254.1 remote-as 65002 + neighbor 192.168.254.1 timers 3 10 + address-family ipv4 unicast + neighbor 192.168.254.1 allowas-in + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_reject_as_sets/r3/zebra.conf b/tests/topotests/bgp_reject_as_sets/r3/zebra.conf new file mode 100644 index 0000000..f490d97 --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r3/zebra.conf @@ -0,0 +1,9 @@ +! exit2 +interface lo + ip address 172.16.254.254/32 +! +interface r3-eth0 + ip address 192.168.254.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py b/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py new file mode 100644 index 0000000..97366eb --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_reject_as_sets.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Donatas Abraitis +# + +""" +Test if an aggregated route with AS_SET is not sent to peers. +Addressing draft-ietf-idr-deprecate-as-set-confed-set recommendations. + +BGP speakers conforming to this document (i.e., conformant BGP + speakers) MUST NOT locally generate BGP UPDATE messages containing + AS_SET or AS_CONFED_SET. Conformant BGP speakers SHOULD NOT send BGP + UPDATE messages containing AS_SET or AS_CONFED_SET. Upon receipt of + such messages, conformant BGP speakers SHOULD use the "Treat-as- + withdraw" error handling behavior as per [RFC7606]. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_reject_as_sets(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_has_aggregated_route_with_stripped_as_set(router): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.0.0/16 json")) + expected = { + "paths": [{"aspath": {"string": "Local", "segments": [], "length": 0}}] + } + return topotest.json_cmp(output, expected) + + def _bgp_announce_route_without_as_sets(router): + output = json.loads( + router.vtysh_cmd( + "show ip bgp neighbor 192.168.254.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "172.16.0.0/16": {"path": ""}, + "192.168.254.0/30": {"path": "65003"}, + "192.168.255.0/30": {"path": "65001"}, + }, + "totalPrefixCounter": 3, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed bgp convergence in "{}"'.format(router) + + test_func = functools.partial( + _bgp_has_aggregated_route_with_stripped_as_set, router + ) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed to see an aggregated route in "{}"'.format(router) + + test_func = functools.partial(_bgp_announce_route_without_as_sets, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert ( + result is None + ), 'Route 172.16.0.0/16 should be sent without AS_SET to r3 "{}"'.format(router) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_remove_private_as/r1/bgpd.conf b/tests/topotests/bgp_remove_private_as/r1/bgpd.conf new file mode 100644 index 0000000..9953689 --- /dev/null +++ b/tests/topotests/bgp_remove_private_as/r1/bgpd.conf @@ -0,0 +1,53 @@ +router bgp 65001 + bgp router-id 192.0.2.1 + no bgp network import-check + neighbor 203.0.113.1 remote-as 65002 + neighbor 203.0.113.1 description r2 + neighbor 203.0.113.1 timers 3 10 + neighbor 203.0.113.3 remote-as 5555 + neighbor 203.0.113.3 description r5 + neighbor 203.0.113.3 timers 3 10 +! + address-family ipv4 unicast + network 100.64.0.0/32 + network 100.64.0.1/32 + network 100.64.0.2/32 + network 100.64.0.3/32 + network 100.64.0.4/32 + network 100.64.0.5/32 + neighbor 203.0.113.1 route-map set-as-paths out + neighbor 203.0.113.3 route-map set-as-paths out + exit-address-family +! +ip prefix-list match-0 seq 5 permit 100.64.0.0/32 +ip prefix-list match-1 seq 5 permit 100.64.0.1/32 +ip prefix-list match-2 seq 5 permit 100.64.0.2/32 +ip prefix-list match-3 seq 5 permit 100.64.0.3/32 +ip prefix-list match-4 seq 5 permit 100.64.0.4/32 +! +! all private +! at r3/r4, as-path should only have r2's asn +route-map set-as-paths permit 10 + match ip address prefix-list match-0 + set as-path prepend 4200000000 4200000001 4200000002 +! +! all private, include r3's asn +! at r3/r4, as-path should only have r2's asn +route-map set-as-paths permit 20 + match ip address prefix-list match-1 + set as-path prepend 65003 4200000000 4200000001 4200000002 65003 +! +! mix of private/public +route-map set-as-paths permit 30 + match ip address prefix-list match-2 + set as-path prepend 4200000000 1000 4200000001 2000 4200000002 +! +! mix of private/public, include r3's asn multiple times +route-map set-as-paths permit 40 + match ip address prefix-list match-3 + set as-path prepend 65003 4200000000 1000 4200000001 2000 4200000002 65003 +! +! all public +route-map set-as-paths permit 50 + match ip address prefix-list match-4 + set as-path prepend 1000 2000 2000 3000 diff --git a/tests/topotests/bgp_remove_private_as/r1/zebra.conf b/tests/topotests/bgp_remove_private_as/r1/zebra.conf new file mode 100644 index 0000000..35c82d7 --- /dev/null +++ b/tests/topotests/bgp_remove_private_as/r1/zebra.conf @@ -0,0 +1,10 @@ +! to r2 +interface r1-eth0 + ip address 203.0.113.0/31 +! +! to r5 +interface r1-eth1 + ip address 203.0.113.2/31 +! +ip forwarding +! diff --git a/tests/topotests/bgp_remove_private_as/r2/bgpd.conf b/tests/topotests/bgp_remove_private_as/r2/bgpd.conf new file mode 100644 index 0000000..9655046 --- /dev/null +++ b/tests/topotests/bgp_remove_private_as/r2/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 65002 + bgp router-id 192.0.2.2 + neighbor 203.0.113.0 remote-as 65001 + neighbor 203.0.113.0 timers 3 10 + neighbor 203.0.113.0 description r1 + neighbor 203.0.113.4 remote-as 65003 + neighbor 203.0.113.4 solo + neighbor 203.0.113.4 timers 3 10 + neighbor 203.0.113.4 description r3 + neighbor 203.0.113.8 remote-as 4444 + neighbor 203.0.113.8 solo + neighbor 203.0.113.8 timers 3 10 + neighbor 203.0.113.8 description r4 +! + address-family ipv4 unicast + neighbor 203.0.113.0 route-map permit-all in + neighbor 203.0.113.4 route-map permit-all out + neighbor 203.0.113.8 route-map permit-all out + exit-address-family +! +route-map permit-all permit 10 +! diff --git a/tests/topotests/bgp_remove_private_as/r2/zebra.conf b/tests/topotests/bgp_remove_private_as/r2/zebra.conf new file mode 100644 index 0000000..0168614 --- /dev/null +++ b/tests/topotests/bgp_remove_private_as/r2/zebra.conf @@ -0,0 +1,14 @@ +! to r1 +interface r2-eth0 + ip address 203.0.113.1/31 +! +! to r3 +interface r2-eth1 + ip address 203.0.113.5/31 +! +! to r4 +interface r2-eth2 + ip address 203.0.113.9/31 +! +ip forwarding +! diff --git a/tests/topotests/bgp_remove_private_as/r3/bgpd.conf b/tests/topotests/bgp_remove_private_as/r3/bgpd.conf new file mode 100644 index 0000000..0273d09 --- /dev/null +++ b/tests/topotests/bgp_remove_private_as/r3/bgpd.conf @@ -0,0 +1,19 @@ +router bgp 65003 + bgp router-id 192.0.2.3 + neighbor 203.0.113.5 remote-as 65002 + neighbor 203.0.113.5 timers 3 10 + neighbor 203.0.113.5 description r2 + neighbor 203.0.113.7 remote-as 5555 + neighbor 203.0.113.7 timers 3 10 + neighbor 203.0.113.7 description r5 +! + address-family ipv4 unicast + neighbor 203.0.113.5 route-map permit-all in + neighbor 203.0.113.5 allowas-in 10 + neighbor 203.0.113.5 soft-reconfiguration inbound + neighbor 203.0.113.7 route-map permit-all in + neighbor 203.0.113.7 allowas-in 10 + neighbor 203.0.113.7 soft-reconfiguration inbound + exit-address-family +! +route-map permit-all permit 10 diff --git a/tests/topotests/bgp_remove_private_as/r3/zebra.conf b/tests/topotests/bgp_remove_private_as/r3/zebra.conf new file mode 100644 index 0000000..e6a0ce3 --- /dev/null +++ b/tests/topotests/bgp_remove_private_as/r3/zebra.conf @@ -0,0 +1,10 @@ +! to r2 +interface r3-eth0 + ip address 203.0.113.4/31 +! +! to r5 +interface r3-eth1 + ip address 203.0.113.6/31 +! +ip forwarding +! diff --git a/tests/topotests/bgp_remove_private_as/r4/bgpd.conf b/tests/topotests/bgp_remove_private_as/r4/bgpd.conf new file mode 100644 index 0000000..f480105 --- /dev/null +++ b/tests/topotests/bgp_remove_private_as/r4/bgpd.conf @@ -0,0 +1,19 @@ +router bgp 4444 + bgp router-id 192.0.2.4 + neighbor 203.0.113.9 remote-as 65002 + neighbor 203.0.113.9 timers 3 10 + neighbor 203.0.113.9 description r2 + neighbor 203.0.113.11 remote-as 5555 + neighbor 203.0.113.11 timers 3 10 + neighbor 203.0.113.11 description r5 +! + address-family ipv4 unicast + neighbor 203.0.113.9 route-map permit-all in + neighbor 203.0.113.9 allowas-in 10 + neighbor 203.0.113.9 soft-reconfiguration inbound + neighbor 203.0.113.11 route-map permit-all in + neighbor 203.0.113.11 allowas-in 10 + neighbor 203.0.113.11 soft-reconfiguration inbound + exit-address-family +! +route-map permit-all permit 10 diff --git a/tests/topotests/bgp_remove_private_as/r4/zebra.conf b/tests/topotests/bgp_remove_private_as/r4/zebra.conf new file mode 100644 index 0000000..4de3300 --- /dev/null +++ b/tests/topotests/bgp_remove_private_as/r4/zebra.conf @@ -0,0 +1,10 @@ +! to r2 +interface r4-eth0 + ip address 203.0.113.8/31 +! +! to r5 +interface r4-eth1 + ip address 203.0.113.10/31 +! +ip forwarding +! diff --git a/tests/topotests/bgp_remove_private_as/r5/bgpd.conf b/tests/topotests/bgp_remove_private_as/r5/bgpd.conf new file mode 100644 index 0000000..67b4d43 --- /dev/null +++ b/tests/topotests/bgp_remove_private_as/r5/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 5555 + bgp router-id 192.0.2.5 + neighbor 203.0.113.2 remote-as 65001 + neighbor 203.0.113.2 timers 3 10 + neighbor 203.0.113.2 description r1 + neighbor 203.0.113.6 remote-as 65003 + neighbor 203.0.113.6 solo + neighbor 203.0.113.6 timers 3 10 + neighbor 203.0.113.6 description r3 + neighbor 203.0.113.10 remote-as 4444 + neighbor 203.0.113.10 solo + neighbor 203.0.113.10 timers 3 10 + neighbor 203.0.113.10 description r4 +! + address-family ipv4 unicast + neighbor 203.0.113.2 route-map permit-all in + neighbor 203.0.113.6 route-map permit-all out + neighbor 203.0.113.10 route-map permit-all out + exit-address-family +! +route-map permit-all permit 10 +! diff --git a/tests/topotests/bgp_remove_private_as/r5/zebra.conf b/tests/topotests/bgp_remove_private_as/r5/zebra.conf new file mode 100644 index 0000000..f02daf4 --- /dev/null +++ b/tests/topotests/bgp_remove_private_as/r5/zebra.conf @@ -0,0 +1,14 @@ +! to r1 +interface r5-eth0 + ip address 203.0.113.3/31 +! +! to r3 +interface r5-eth1 + ip address 203.0.113.7/31 +! +! to r4 +interface r5-eth2 + ip address 203.0.113.11/31 +! +ip forwarding +! diff --git a/tests/topotests/bgp_remove_private_as/test_bgp_remove_private_as.py b/tests/topotests/bgp_remove_private_as/test_bgp_remove_private_as.py new file mode 100644 index 0000000..e48f81c --- /dev/null +++ b/tests/topotests/bgp_remove_private_as/test_bgp_remove_private_as.py @@ -0,0 +1,415 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_remove_private_as.py +# +# Copyright (C) 2022 NVIDIA Corporation +# Trey Aspelund +# + +""" +test_bgp_remove_private_as.py tests the following conditions: +1. "remove-private-AS" strips all private ASNs from the AS-path unless: + a. the ASN belongs to the peer + b. the ASN is both local + private + c. the AS-path is not completely comprised of public ASNs +2. "remove-private-AS all" strips all private ASNs from the AS-path unless: + a. the ASN belongs to the peer + b. the ASN is both local + private +3. "remove-private-AS replace-AS" swaps private ASNs with local ASN unless: + a. the ASN belongs to the peer + b. the AS-path is not completely comprised of public ASNs +4. "remove-private-AS all replace-AS" swaps private ASNs with local ASN unless: + a. the ASN belongs to the peer + +All conditions are tested while the local ASN is private. +All conditions are tested while the local ASN is public. +All conditions are tested against an eBGP peer in a private ASN. +All conditions are tested against an eBGP peer in a public ASN. +""" + +import os +import sys +import json +import time +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from functools import partial + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + """ + We are effectively creating two hub/spoke topologies with r2 and r5 acting + as hubs. "remove-private-AS" will be configured on r2/r5 towards r3/r4, and + r1 will act as the originator of the test prefixes. AS-Path validation will + be done on r3/r4. + + Topology: + +-----+ +-----+ +-----+ + | r1 |----->|r2/r5|---->| r3 | + +-----+ +-----+ +-----+ + | + v + +-----+ + | r4 | + +-----+ + ASNs: + - r1: 65001 + - r2: 65002 + - r3: 65003 + - r4: 4444 + - r5: 5555 + """ + for routern in range(1, 6): + tgen.add_router(f"r{routern}") + + ####################### + # Connections to r2 + ####################### + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) + + ####################### + # Connections to r5 + ####################### + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r5"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r5"]) + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["r5"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, f"{rname}/zebra.conf") + ) + router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, f"{rname}/bgpd.conf")) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_remove_private_as(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Test routes + prefixes = [ + "100.64.0.0/32", + "100.64.0.1/32", + "100.64.0.2/32", + "100.64.0.3/32", + "100.64.0.4/32", + ] + + # r2/r5 are setup with remove-private-AS configs. + tx_routers = ["r2", "r5"] + + # We will validate the paths received by r3/r4. + rx_routers = ["r3", "r4"] + + # Config options for remove-private-AS + remove_types = [ + "remove-private-AS", + "remove-private-AS all", + "remove-private-AS replace-AS", + "remove-private-AS all replace-AS", + ] + + # Expected as-paths for each test route from the perspective of each + # rx_router, accounting for each variation of remove-private-AS. + # + # Structure: + # expected_paths = { + # rx_router: { + # remove_type: { + # tx_router: { + # prefix: "path" + # } + # } + # } + # } + expected_paths = { + "r3": { + "remove-private-AS": { + "r2": { + "100.64.0.0/32": "65002", + "100.64.0.1/32": "65002 65003 65003", + "100.64.0.2/32": "65002 65001 4200000000 1000 4200000001 2000 4200000002", + "100.64.0.3/32": "65002 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003", + "100.64.0.4/32": "65002 65001 1000 2000 2000 3000", + }, + "r5": { + "100.64.0.0/32": "5555", + "100.64.0.1/32": "5555 65003 65003", + "100.64.0.2/32": "5555 65001 4200000000 1000 4200000001 2000 4200000002", + "100.64.0.3/32": "5555 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003", + "100.64.0.4/32": "5555 65001 1000 2000 2000 3000", + }, + }, + "remove-private-AS all": { + "r2": { + "100.64.0.0/32": "65002", + "100.64.0.1/32": "65002 65003 65003", + "100.64.0.2/32": "65002 1000 2000", + "100.64.0.3/32": "65002 65003 1000 2000 65003", + "100.64.0.4/32": "65002 1000 2000 2000 3000", + }, + "r5": { + "100.64.0.0/32": "5555", + "100.64.0.1/32": "5555 65003 65003", + "100.64.0.2/32": "5555 1000 2000", + "100.64.0.3/32": "5555 65003 1000 2000 65003", + "100.64.0.4/32": "5555 1000 2000 2000 3000", + }, + }, + "remove-private-AS replace-AS": { + "r2": { + "100.64.0.0/32": "65002 65002 65002 65002 65002", + "100.64.0.1/32": "65002 65002 65003 65002 65002 65002 65003", + "100.64.0.2/32": "65002 65001 4200000000 1000 4200000001 2000 4200000002", + "100.64.0.3/32": "65002 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003", + "100.64.0.4/32": "65002 65001 1000 2000 2000 3000", + }, + "r5": { + "100.64.0.0/32": "5555 5555 5555 5555 5555", + "100.64.0.1/32": "5555 5555 65003 5555 5555 5555 65003", + "100.64.0.2/32": "5555 65001 4200000000 1000 4200000001 2000 4200000002", + "100.64.0.3/32": "5555 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003", + "100.64.0.4/32": "5555 65001 1000 2000 2000 3000", + }, + }, + "remove-private-AS all replace-AS": { + "r2": { + "100.64.0.0/32": "65002 65002 65002 65002 65002", + "100.64.0.1/32": "65002 65002 65003 65002 65002 65002 65003", + "100.64.0.2/32": "65002 65002 65002 1000 65002 2000 65002", + "100.64.0.3/32": "65002 65002 65003 65002 1000 65002 2000 65002 65003", + "100.64.0.4/32": "65002 65002 1000 2000 2000 3000", + }, + "r5": { + "100.64.0.0/32": "5555 5555 5555 5555 5555", + "100.64.0.1/32": "5555 5555 65003 5555 5555 5555 65003", + "100.64.0.2/32": "5555 5555 5555 1000 5555 2000 5555", + "100.64.0.3/32": "5555 5555 65003 5555 1000 5555 2000 5555 65003", + "100.64.0.4/32": "5555 5555 1000 2000 2000 3000", + }, + }, + }, + "r4": { + "remove-private-AS": { + "r2": { + "100.64.0.0/32": "65002", + "100.64.0.1/32": "65002", + "100.64.0.2/32": "65002 65001 4200000000 1000 4200000001 2000 4200000002", + "100.64.0.3/32": "65002 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003", + "100.64.0.4/32": "65002 65001 1000 2000 2000 3000", + }, + "r5": { + "100.64.0.0/32": "5555", + "100.64.0.1/32": "5555", + "100.64.0.2/32": "5555 65001 4200000000 1000 4200000001 2000 4200000002", + "100.64.0.3/32": "5555 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003", + "100.64.0.4/32": "5555 65001 1000 2000 2000 3000", + }, + }, + "remove-private-AS all": { + "r2": { + "100.64.0.0/32": "65002", + "100.64.0.1/32": "65002", + "100.64.0.2/32": "65002 1000 2000", + "100.64.0.3/32": "65002 1000 2000", + "100.64.0.4/32": "65002 1000 2000 2000 3000", + }, + "r5": { + "100.64.0.0/32": "5555", + "100.64.0.1/32": "5555", + "100.64.0.2/32": "5555 1000 2000", + "100.64.0.3/32": "5555 1000 2000", + "100.64.0.4/32": "5555 1000 2000 2000 3000", + }, + }, + "remove-private-AS replace-AS": { + "r2": { + "100.64.0.0/32": "65002 65002 65002 65002 65002", + "100.64.0.1/32": "65002 65002 65002 65002 65002 65002 65002", + "100.64.0.2/32": "65002 65001 4200000000 1000 4200000001 2000 4200000002", + "100.64.0.3/32": "65002 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003", + "100.64.0.4/32": "65002 65001 1000 2000 2000 3000", + }, + "r5": { + "100.64.0.0/32": "5555 5555 5555 5555 5555", + "100.64.0.1/32": "5555 5555 5555 5555 5555 5555 5555", + "100.64.0.2/32": "5555 65001 4200000000 1000 4200000001 2000 4200000002", + "100.64.0.3/32": "5555 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003", + "100.64.0.4/32": "5555 65001 1000 2000 2000 3000", + }, + }, + "remove-private-AS all replace-AS": { + "r2": { + "100.64.0.0/32": "65002 65002 65002 65002 65002", + "100.64.0.1/32": "65002 65002 65002 65002 65002 65002 65002", + "100.64.0.2/32": "65002 65002 65002 1000 65002 2000 65002", + "100.64.0.3/32": "65002 65002 65002 65002 1000 65002 2000 65002 65002", + "100.64.0.4/32": "65002 65002 1000 2000 2000 3000", + }, + "r5": { + "100.64.0.0/32": "5555 5555 5555 5555 5555", + "100.64.0.1/32": "5555 5555 5555 5555 5555 5555 5555", + "100.64.0.2/32": "5555 5555 5555 1000 5555 2000 5555", + "100.64.0.3/32": "5555 5555 5555 5555 1000 5555 2000 5555 5555", + "100.64.0.4/32": "5555 5555 1000 2000 2000 3000", + }, + }, + }, + } + + # Simple lookup of remote peer ip by routers in session (local --> remote). + # + # Structure: + # peer_to_ip = { + # local_rtr: { + # peer_rtr: peer_ip + # } + # } + peer_to_ip = { + "r1": {"r2": "203.0.113.1", "r5": "203.0.113.3"}, + "r2": {"r1": "203.0.113.0", "r3": "203.0.113.4", "r4": "203.0.113.8"}, + "r3": {"r2": "203.0.113.5", "r5": "203.0.113.7"}, + "r4": {"r2": "203.0.113.9", "r5": "203.0.113.11"}, + "r5": {"r1": "203.0.113.2", "r3": "203.0.113.6", "r4": "203.0.113.10"}, + } + + def __bgp_up(): + """Return True if all configured peers are Established.""" + for router in tx_routers: + output = json.loads( + tgen.gears[router].vtysh_cmd("show ip bgp summary json") + ) + numPeers = output["ipv4Unicast"]["totalPeers"] + numConverged = 0 + for peer_data in output["ipv4Unicast"]["peers"].values(): + if peer_data["state"] == "Established": + numConverged += 1 + if numConverged == numPeers: + return True + return False + + def __bgp_converged(): + """Return True if all prefixes have been received from tx_routers.""" + for router in rx_routers: + output = json.loads( + tgen.gears[router].vtysh_cmd("show ip bgp summary json") + ) + numPeers = output["ipv4Unicast"]["totalPeers"] + numConverged = 0 + for peer in tx_routers: + peer_ip = peer_to_ip[router][peer] + numPrefixes = output["ipv4Unicast"]["peers"][peer_ip]["pfxRcd"] + if numPrefixes == len(prefixes): + numConverged += 1 + if numConverged == numPeers: + return True + return False + + def _routers_up(tx_rtrs, rx_rtrs): + """Ensure all BGP sessions are up and all routes are installed.""" + # all sessions go through tx_routers, so ensure all their peers are up + test_func = partial(__bgp_up) + _, result = topotest.run_and_expect(test_func, True, count=60, wait=0.5) + assert result == True, "Not all peers in Established state!" + + # ensure correct number of routes are installed + test_func = partial(__bgp_converged) + _, result = topotest.run_and_expect(test_func, True, count=60, wait=0.5) + assert result == True, "Not all routes installed in time!" + + def _change_remove_type(new_type, op): + """Update config with next remove-private-AS config variant.""" + no = "no" if op == "del" else "" + for tr in tx_routers: + for rr in rx_routers: + p_ip = peer_to_ip[tr][rr] + tgen.gears[tr].vtysh_multicmd( + f""" + configure terminal + router bgp + address-family ipv4 unicast + {no} neighbor {p_ip} {new_type} + """ + ) + + def _validate_paths(remove_type): + """Compare actual AS-Path against expected AS-Path.""" + for rtr in rx_routers: + for peer in tx_routers: + p_ip = peer_to_ip[rtr][peer] + adj_rib_in = json.loads( + tgen.gears[rtr].vtysh_cmd( + f"show ip bgp neighbor {p_ip} received-routes json" + ) + ) + for pfx in prefixes: + good_path = expected_paths[rtr][remove_type][peer][pfx] + real_path = adj_rib_in["receivedRoutes"][pfx]["path"] + return real_path == good_path + + ####################### + # Begin Test + ####################### + + # make sure all peers come up and exchange routes + _routers_up(tx_routers, rx_routers) + + # test each variation of remove-private-AS + for rmv_type in remove_types: + _change_remove_type(rmv_type, "add") + + test_func = partial(_validate_paths, rmv_type) + _, result = topotest.run_and_expect(test_func, True, count=60, wait=0.5) + assert result == True, "Not all routes have correct AS-Path values!" + + # each variation sets a separate peer flag in bgpd. we need to clear + # the old flag after each iteration so we only test the flags we expect. + _change_remove_type(rmv_type, "del") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_remove_private_as_route_map/__init__.py b/tests/topotests/bgp_remove_private_as_route_map/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_remove_private_as_route_map/r1/frr.conf b/tests/topotests/bgp_remove_private_as_route_map/r1/frr.conf new file mode 100644 index 0000000..b2dba7d --- /dev/null +++ b/tests/topotests/bgp_remove_private_as_route_map/r1/frr.conf @@ -0,0 +1,10 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 +! diff --git a/tests/topotests/bgp_remove_private_as_route_map/r2/frr.conf b/tests/topotests/bgp_remove_private_as_route_map/r2/frr.conf new file mode 100644 index 0000000..9c423ce --- /dev/null +++ b/tests/topotests/bgp_remove_private_as_route_map/r2/frr.conf @@ -0,0 +1,19 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 + ip address 192.168.2.1/32 +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + address-family ipv4 unicast + redistribute connected + neighbor 192.168.1.1 route-map r1 out + neighbor 192.168.1.1 remove-private-AS all + exit-address-family +! +route-map r1 permit 10 + set as-path prepend 65123 4200000001 +! diff --git a/tests/topotests/bgp_remove_private_as_route_map/test_bgp_remove_private_as_route_map.py b/tests/topotests/bgp_remove_private_as_route_map/test_bgp_remove_private_as_route_map.py new file mode 100644 index 0000000..2ae6f7f --- /dev/null +++ b/tests/topotests/bgp_remove_private_as_route_map/test_bgp_remove_private_as_route_map.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if private AS is removed from AS_PATH attribute when route-map is used (prepend). +""" + +import os +import re +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_remove_private_as_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _check_routes(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "192.168.2.1/32": [ + { + "valid": True, + "path": "65002", + } + ] + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _check_routes, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "65123 4200000001 ASNs should be removed from AS_PATH attribute" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_rfapi_basic_sanity/__init__.py b/tests/topotests/bgp_rfapi_basic_sanity/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/tests/topotests/bgp_rfapi_basic_sanity/customize.py b/tests/topotests/bgp_rfapi_basic_sanity/customize.py new file mode 100644 index 0000000..c789fa8 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/customize.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright (c) 2017-2018 by +# Network Device Education Foundation, Inc. ("NetDEF") +# Modified by LabN Consulting, L.L.C. +# + +r""" +customize.py: Simple FRR MPLS L3VPN test topology + + +---------+ + | r1 | + | 1.1.1.1 | PE Router + +----+----+ + | .1 r1-eth0 + | + ~~~~~~~~~~~~~ + ~~ sw0 ~~ + ~~ 10.0.1.0/24 ~~ + ~~~~~~~~~~~~~ + |10.0.1.0/24 + | + | .2 r2-eth0 + +----+----+ + | r2 | + | 2.2.2.2 | P router + +--+---+--+ + r2-eth2 .2 | | .2 r2-eth1 + ______/ \______ + / \ + ~~~~~~~~~~~~~ ~~~~~~~~~~~~~ +~~ sw2 ~~ ~~ sw1 ~~ +~~ 10.0.3.0/24 ~~ ~~ 10.0.2.0/24 ~~ + ~~~~~~~~~~~~~ ~~~~~~~~~~~~~ + | / | + \ _________/ | + \ / \ +r3-eth1 .3 | | .3 r3-eth0 | .4 r4-eth0 + +----+--+---+ +----+----+ + | r3 | | r4 | + | 3.3.3.3 | | 4.4.4.4 | PE Routers + +-----------+ +---------+ + +""" + +import os + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import get_topogen +from lib.topolog import logger +from lib.ltemplate import ltemplateRtrCmd + +# Required to instantiate the topology builder class. + + +CWD = os.path.dirname(os.path.realpath(__file__)) +# test name based on directory +TEST = os.path.basename(CWD) + + +def build_topo(tgen): + "Build function" + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # Create P/PE routers + tgen.add_router("r1") + for routern in range(2, 5): + tgen.add_router("r{}".format(routern)) + # Create a switch with just one router connected to it to simulate a + # empty network. + switch = {} + switch[0] = tgen.add_switch("sw0") + switch[0].add_link(tgen.gears["r1"], nodeif="r1-eth0") + switch[0].add_link(tgen.gears["r2"], nodeif="r2-eth0") + + switch[1] = tgen.add_switch("sw1") + switch[1].add_link(tgen.gears["r2"], nodeif="r2-eth1") + switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth0") + switch[1].add_link(tgen.gears["r4"], nodeif="r4-eth0") + + switch[2] = tgen.add_switch("sw2") + switch[2].add_link(tgen.gears["r2"], nodeif="r2-eth2") + switch[2].add_link(tgen.gears["r3"], nodeif="r3-eth1") + + +def ltemplatePreRouterStartHook(): + cc = ltemplateRtrCmd() + tgen = get_topogen() + logger.info("pre router-start hook") + return True + + +def ltemplatePostRouterStartHook(): + logger.info("post router-start hook") + return True diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf new file mode 100644 index 0000000..f6e0baa --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf @@ -0,0 +1,50 @@ +frr defaults traditional +! +hostname r1 +password zebra +log stdout notifications +log commands +router bgp 5226 + bgp router-id 1.1.1.1 + bgp cluster-id 1.1.1.1 + no bgp ebgp-requires-policy + neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 update-source 1.1.1.1 +! + address-family ipv4 unicast + redistribute vnc-direct + no neighbor 2.2.2.2 activate + exit-address-family +! + address-family ipv4 vpn + neighbor 2.2.2.2 activate + exit-address-family +! + rfp holddown-factor 0 +! + vnc defaults + rd auto:vn:123 + response-lifetime 45 + rt both 1000:1 1000:2 + exit-vnc +! + vnc nve-group red + prefix vn 10.0.0.0/8 + rd auto:vn:10 + rt both 1000:10 + exit-vnc +! + vnc nve-group blue + prefix vn 20.0.0.0/8 + rd auto:vn:20 + rt both 1000:20 + exit-vnc +! + vnc nve-group green + prefix vn 30.0.0.0/8 + rd auto:vn:20 + rt both 1000:30 + exit-vnc +! +end diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf new file mode 100644 index 0000000..460a8fb --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf @@ -0,0 +1,12 @@ +hostname r1 +log file ospfd.log +! +router ospf + router-id 1.1.1.1 + network 0.0.0.0/4 area 0 + redistribute static +! +int r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r1/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity/r1/zebra.conf new file mode 100644 index 0000000..18f61e0 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/r1/zebra.conf @@ -0,0 +1,24 @@ +log file zebra.log +! +hostname r1 +! +interface lo + ip address 1.1.1.1/32 +! +interface r1-eth0 + description to sw0 + ip address 10.0.1.1/24 + no link-detect +! +interface r1-eth4 + description to ce1 + ip address 192.168.1.1/24 + no link-detect +! +ip route 99.0.0.1/32 192.168.1.2 +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf new file mode 100644 index 0000000..19050e6 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf @@ -0,0 +1,33 @@ +frr defaults traditional +! +hostname r2 +password zebra +log stdout notifications +log commands +router bgp 5226 + bgp router-id 2.2.2.2 + bgp cluster-id 2.2.2.2 + no bgp ebgp-requires-policy + neighbor 1.1.1.1 remote-as 5226 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 update-source 2.2.2.2 + neighbor 3.3.3.3 remote-as 5226 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 update-source 2.2.2.2 + neighbor 4.4.4.4 remote-as 5226 + neighbor 4.4.4.4 timers 3 10 + neighbor 4.4.4.4 update-source 2.2.2.2 + address-family ipv4 unicast + no neighbor 1.1.1.1 activate + no neighbor 3.3.3.3 activate + no neighbor 4.4.4.4 activate + exit-address-family + address-family ipv4 vpn + neighbor 1.1.1.1 activate + neighbor 1.1.1.1 route-reflector-client + neighbor 3.3.3.3 activate + neighbor 3.3.3.3 route-reflector-client + neighbor 4.4.4.4 activate + neighbor 4.4.4.4 route-reflector-client + exit-address-family +end diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf new file mode 100644 index 0000000..dbed618 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf @@ -0,0 +1,19 @@ +hostname r2 +log file ospfd.log +! +router ospf + router-id 2.2.2.2 + network 0.0.0.0/0 area 0 +! +int r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r2/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity/r2/zebra.conf new file mode 100644 index 0000000..dd1dbac --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/r2/zebra.conf @@ -0,0 +1,27 @@ +log file zebra.log +! +hostname r2 +! +interface lo + ip address 2.2.2.2/32 +! +interface r2-eth0 + description to sw0 + ip address 10.0.1.2/24 + no link-detect +! +interface r2-eth1 + description to sw1 + ip address 10.0.2.2/24 + no link-detect +! +interface r2-eth2 + description to sw2 + ip address 10.0.3.2/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf new file mode 100644 index 0000000..2210f24 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf @@ -0,0 +1,48 @@ +frr defaults traditional +! +hostname r3 +password zebra +log stdout notifications +log commands +router bgp 5226 + bgp router-id 3.3.3.3 + bgp cluster-id 3.3.3.3 + no bgp ebgp-requires-policy + neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 update-source 3.3.3.3 +! + address-family ipv4 unicast + no neighbor 2.2.2.2 activate + exit-address-family + address-family ipv4 vpn + neighbor 2.2.2.2 activate + exit-address-family +! + rfp holddown-factor 0 +! + vnc defaults + rd auto:vn:123 + response-lifetime 45 + rt both 1000:1 1000:2 + exit-vnc +! + vnc nve-group red + prefix vn 10.0.0.0/8 + rd auto:vn:10 + rt both 1000:10 + exit-vnc +! + vnc nve-group blue + prefix vn 20.0.0.0/8 + rd auto:vn:20 + rt both 1000:20 + exit-vnc +! + vnc nve-group green + prefix vn 30.0.0.0/8 + rd auto:vn:20 + rt both 1000:30 + exit-vnc +! +end diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf new file mode 100644 index 0000000..0e64ed6 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf @@ -0,0 +1,17 @@ +hostname r3 +password 1 +log file ospfd.log +! +router ospf + router-id 3.3.3.3 + network 0.0.0.0/4 area 0 + redistribute static +! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r3/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity/r3/zebra.conf new file mode 100644 index 0000000..9dbc290 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/r3/zebra.conf @@ -0,0 +1,29 @@ +log file zebra.log +! +hostname r3 +! +interface lo + ip address 3.3.3.3/32 +! +interface r3-eth0 + description to sw1 + ip address 10.0.2.3/24 + no link-detect +! +interface r3-eth1 + description to sw2 + ip address 10.0.3.3/24 + no link-detect +! +interface r3-eth4 + description to ce2 + ip address 192.168.1.1/24 + no link-detect +! +ip route 99.0.0.2/32 192.168.1.2 +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf new file mode 100644 index 0000000..28b5f9c --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf @@ -0,0 +1,49 @@ +frr defaults traditional +! +hostname r4 +password zebra +log stdout notifications +log commands +router bgp 5226 + bgp router-id 4.4.4.4 + bgp cluster-id 4.4.4.4 + no bgp ebgp-requires-policy + neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 update-source 4.4.4.4 +! + address-family ipv4 unicast + no neighbor 2.2.2.2 activate + exit-address-family +! + address-family ipv4 vpn + neighbor 2.2.2.2 activate + exit-address-family +! + rfp holddown-factor 0 +! + vnc defaults + rd auto:vn:123 + response-lifetime 45 + rt both 1000:1 1000:2 + exit-vnc +! + vnc nve-group red + prefix vn 10.0.0.0/8 + rd auto:vn:10 + rt both 1000:10 + exit-vnc +! + vnc nve-group blue + prefix vn 20.0.0.0/8 + rd auto:vn:20 + rt both 1000:20 + exit-vnc +! + vnc nve-group green + prefix vn 30.0.0.0/8 + rd auto:vn:20 + rt both 1000:30 + exit-vnc +! +end diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf new file mode 100644 index 0000000..89e37df --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf @@ -0,0 +1,12 @@ +hostname r4 +log file ospfd.log +! +router ospf + router-id 4.4.4.4 + network 0.0.0.0/4 area 0 + redistribute static +! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r4/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity/r4/zebra.conf new file mode 100644 index 0000000..415f03d --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/r4/zebra.conf @@ -0,0 +1,23 @@ +log file zebra.log +! +hostname r4 +! +interface lo + ip address 4.4.4.4/32 +! +interface r4-eth0 + description to sw1 + ip address 10.0.2.4/24 + no link-detect +! +interface r4-eth4 + description to ce3 + ip address 192.168.1.1/24 + no link-detect +! +ip route 99.0.0.3/32 192.168.1.2 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/add_routes.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/add_routes.py new file mode 100644 index 0000000..bc47dfc --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/add_routes.py @@ -0,0 +1,159 @@ +from lib.lutil import luCommand + +holddownFactorSet = luCommand( + "r1", + 'vtysh -c "show running"', + "rfp holddown-factor", + "none", + "Holddown factor set", +) +if not holddownFactorSet: + to = "-1" + cost = "" +else: + to = "6" + cost = "cost 50" +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev open vn 10.0.0.1 un 1.1.1.1"', + "rfapi_set_response_cb: status 0", + "pass", + "Opened RFAPI", +) +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 11.11.11.11"', + "rc=2", + "pass", + "Clean query", +) +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.1 un 1.1.1.1 prefix 11.11.11.0/24 lifetime {}"'.format( + to + ), + "", + "none", + "Prefix registered", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "1 out of 1", + "wait", + "Local registration", +) +luCommand("r1", 'vtysh -c "debug rfapi-dev response-omit-self off"', ".", "none") +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 11.11.11.11"', + "11.11.11.0/24", + "pass", + "Query self", +) + +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev open vn 10.0.0.2 un 2.2.2.2"', + "rfapi_set_response_cb: status 0", + "pass", + "Opened RFAPI", +) +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.2 un 2.2.2.2 prefix 22.22.22.0/24 lifetime {}"'.format( + to + ), + "", + "none", + "Prefix registered", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations local"', + "1 out of 1", + "wait", + "Local registration", +) +luCommand("r3", 'vtysh -c "debug rfapi-dev response-omit-self on"', ".", "none") +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.2 un 2.2.2.2 target 22.22.22.22"', + "rc=2", + "pass", + "Self excluded", +) +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev open vn 10.0.1.2 un 2.1.1.2"', + "rfapi_set_response_cb: status 0", + "pass", + "Opened query only RFAPI", +) +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev query vn 10.0.1.2 un 2.1.1.2 target 22.22.22.22"', + "22.22.22.0/24", + "pass", + "See local", +) + +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev open vn 10.0.0.3 un 3.3.3.3"', + "rfapi_set_response_cb: status 0", + "pass", + "Opened RFAPI", +) +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.3 un 3.3.3.3 prefix 33.33.33.0/24 lifetime {}"'.format( + to + ), + "", + "none", + "Prefix registered", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations local"', + "1 out of 1", + "wait", + "Local registration", +) +luCommand("r4", 'vtysh -c "debug rfapi-dev response-omit-self off"', ".", "none") +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 33.33.33.33"', + "33.33.33.0/24", + "pass", + "Query self", +) + +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.3 un 3.3.3.3 prefix 11.11.11.0/24 lifetime {} {}"'.format( + to, cost + ), + "", + "none", + "MP Prefix registered", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations local"', + "2 out of 2", + "wait", + "Local registration", +) +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 11.11.11.11"', + "11.11.11.0/24", + "pass", + "Query self MP", +) + +luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none") diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py new file mode 100644 index 0000000..9878cdc --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py @@ -0,0 +1,50 @@ +from lib.lutil import luCommand + +luCommand( + "r1", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r3", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r4", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r2", + 'vtysh -c "show bgp summary"', + " 00:0.* 00:0.* 00:0", + "wait", + "Core adjacencies up", + 180, +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf all summary"', + " 00:0", + "wait", + "All adjacencies up", + 180, +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf all summary"', + " 00:0", + "wait", + "All adjacencies up", + 180, +) +luCommand( + "r4", + 'vtysh -c "show bgp vrf all summary"', + " 00:0", + "wait", + "All adjacencies up", + 180, +) +luCommand( + "r1", "ping 3.3.3.3 -c 1", " 0. packet loss", "wait", "PE->PE3 (loopback) ping" +) +luCommand( + "r1", "ping 4.4.4.4 -c 1", " 0. packet loss", "wait", "PE->PE4 (loopback) ping" +) +# luCommand('r4','ping 3.3.3.3 -c 1',' 0. packet loss','wait','PE->PE3 (loopback) ping') diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_close.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_close.py new file mode 100644 index 0000000..e68fac8 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_close.py @@ -0,0 +1,102 @@ +from lib.lutil import luCommand + +holddownFactorSet = luCommand( + "r1", + 'vtysh -c "show running"', + "rfp holddown-factor", + "none", + "Holddown factor set", +) +if not holddownFactorSet: + to = "-1" +else: + to = "1" +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev open vn 20.0.0.1 un 1.1.1.21"', + "rfapi_set_response_cb: status 0", + "pass", + "Opened RFAPI", +) +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev register vn 20.0.0.1 un 1.1.1.21 prefix 111.111.111.0/24 lifetime {}"'.format( + to + ), + "", + "none", + "Prefix registered", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "111.111.111.0/24", + "wait", + "Local registration", + 1, +) +luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "111.111.111.0/24", + "wait", + "See registration", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "111.111.111.0/24", + "wait", + "See registration", +) +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev close vn 20.0.0.1 un 1.1.1.21"', + "status 0", + "pass", + "Closed RFAPI", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 3", + "wait", + "See cleanup", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 3", + "wait", + "See cleanup", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 2 .* Remotely: *Active: 2", + "wait", + "See cleanup", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 0", + "wait", + "Out of holddown", + 20, +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 0", + "wait", + "Out of holddown", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 0", + "wait", + "Out of holddown", +) diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_routes.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_routes.py new file mode 100644 index 0000000..24b3cba --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_routes.py @@ -0,0 +1,74 @@ +from lib.lutil import luCommand + +luCommand("r1", 'vtysh -c "show bgp ipv4 vpn"', "", "none", "VPN SAFI") +luCommand("r2", 'vtysh -c "show bgp ipv4 vpn"', "", "none", "VPN SAFI") +luCommand("r3", 'vtysh -c "show bgp ipv4 vpn"', "", "none", "VPN SAFI") +luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"', "", "none", "VPN SAFI") +luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 3", + "wait", + "See all registrations", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 3", + "wait", + "See all registrations", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 2 .* Remotely: *Active: 2", + "wait", + "See all registrations", +) +num = "4 routes and 4" +luCommand("r1", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI okay") +luCommand("r2", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI okay") +luCommand("r3", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI okay") +luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI okay") +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 22.22.22.22"', + "pfx=", + "pass", + "Query R2s info", +) +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 33.33.33.33"', + "pfx=", + "pass", + "Query R4s info", +) +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.2 un 2.2.2.2 target 11.11.11.11"', + "11.11.11.0/24.*11.11.11.0/24.*", + "pass", + "Query R1s+R4s info", +) +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.2 un 2.2.2.2 target 33.33.33.33"', + "pfx=", + "pass", + "Query R4s info", +) +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 11.11.11.11"', + "11.11.11.0/24.*11.11.11.0/24.*", + "pass", + "Query R1s+R4s info", +) +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 22.22.22.22"', + "pfx=", + "pass", + "Query R2s info", +) diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_timeout.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_timeout.py new file mode 100644 index 0000000..f5c2db2 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_timeout.py @@ -0,0 +1,325 @@ +from lib.lutil import luCommand + +holddownFactorSet = luCommand( + "r1", + 'vtysh -c "show running"', + "rfp holddown-factor", + "none", + "Holddown factor set", +) +luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none") +if not holddownFactorSet: + luCommand( + "r1", + 'vtysh -c "show vnc summary"', + ".", + "pass", + "Holddown factor not set -- skipping test", + ) +else: + # holddown time test + luCommand( + "r1", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16 lifetime 10"', + "", + "none", + "Prefix registered", + ) + luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "1.111.0.0/16", + "wait", + "Local registration", + ) + + luCommand( + "r3", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16 lifetime 10"', + "", + "none", + "Prefix registered", + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations local"', + "1.222.0.0/16", + "wait", + "Local registration", + ) + + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Remotely: *Active: 4 ", + "wait", + "See registrations, L=10", + ) + + luCommand( + "r4", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.3 un 3.3.3.3 prefix 1.222.0.0/16 lifetime 5 cost 50"', + "", + "none", + "MP Prefix registered", + ) + luCommand( + "r4", + 'vtysh -c "show vnc registrations local"', + "1.222.0.0/16", + "wait", + "Local registration (MP prefix)", + ) + + luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none") + luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none") + + luCommand( + "r4", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 1.111.111.111"', + "pfx=", + "pass", + "Query R1s info", + ) + luCommand( + "r4", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 1.222.222.222"', + "1.222.0.0/16.*1.222.0.0/16", + "pass", + "Query R3s+R4s info", + ) + + luCommand( + "r4", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 1.222.0.0/16"', + "", + "none", + "MP Prefix removed", + ) + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 1 ", + "wait", + "MP prefix in holddown", + ) + luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 1 ", + "wait", + "MP prefix in holddown", + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 1 ", + "wait", + "MP prefix in holddown", + ) + luCommand( + "r1", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 1.222.222.222"', + "1.222.0.0/16", + "pass", + "Query R3s info", + ) + luCommand( + "r1", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16"', + "", + "none", + "Prefix timeout", + ) + luCommand( + "r1", + 'vtysh -c "show vnc registrations holddown"', + "1.111.0.0/16", + "wait", + "Local holddown", + 1, + ) + luCommand( + "r3", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16"', + "", + "none", + "Prefix timeout", + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations holddown"', + "1.222.0.0/16", + "wait", + "Local holddown", + 1, + ) + luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none") + luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none") + + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 2 ", + "wait", + "In holddown", + ) + luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 2 ", + "wait", + "In holddown", + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 2 ", + "wait", + "In holddown", + ) + + luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 0", + "wait", + "Out of holddown", + 20, + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 0", + "wait", + "Out of holddown", + ) + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 0", + "wait", + "Out of holddown", + ) + + # kill test + luCommand( + "r1", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16 lifetime 10"', + "", + "none", + "Prefix registered", + ) + luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "1.111.0.0/16", + "wait", + "Local registration", + ) + + luCommand( + "r3", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16 lifetime 10"', + "", + "none", + "Prefix registered", + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations local"', + "1.222.0.0/16", + "wait", + "Local registration", + ) + + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Remotely: *Active: 4 ", + "wait", + "See registrations L=10 (pre-kill)", + 5, + ) + luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none") + luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none") + luCommand( + "r1", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16 kill"', + "", + "none", + "Prefix kill", + ) + luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 4 .*In Holddown: *Active: 0", + "wait", + "Registration killed", + 1, + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 2 .* Remotely: *Active: 3 .*In Holddown: *Active: 1", + "wait", + "Remote in holddown", + 5, + ) + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 2 .* Remotely: *Active: 3 .*In Holddown: *Active: 1", + "wait", + "Remote in holddown", + 5, + ) + + luCommand( + "r3", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16 kill"', + "", + "none", + "Prefix kill", + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 3 .*In Holddown: *Active: 1", + "wait", + "Registration killed", + 1, + ) + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 2 .* Remotely: *Active: 2 .*In Holddown: *Active: 2", + "wait", + "Remote in holddown", + 5, + ) + + luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 3 .*In Holddown: *Active: 0", + "wait", + "Out of holddown", + 20, + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 3 .*In Holddown: *Active: 0", + "wait", + "Out of holddown", + ) + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 2 .* Remotely: *Active: 2 .*In Holddown: *Active: 0", + "wait", + "Out of holddown", + ) diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py new file mode 100644 index 0000000..7201ac8 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py @@ -0,0 +1,124 @@ +from lib.lutil import luCommand + +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.1 un 1.1.1.1 prefix 11.11.11.0/24"', + "", + "none", + "Prefix removed", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 ", + "wait", + "Local registration removed", +) +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev close vn 10.0.0.1 un 1.1.1.1"', + "status 0", + "pass", + "Closed RFAPI", +) + +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.2 un 2.2.2.2 prefix 22.22.22.0/24"', + "", + "none", + "Prefix removed", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 ", + "wait", + "Local registration removed", +) +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev close vn 10.0.0.2 un 2.2.2.2"', + "status 0", + "pass", + "Closed RFAPI", +) + +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 33.33.33.0/24"', + "", + "none", + "Prefix removed", +) +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 11.11.11.0/24"', + "", + "none", + "MP prefix removed", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 ", + "wait", + "Local registration removed", +) +# luCommand('r4','vtysh -c "debug rfapi-dev close vn 10.0.0.3 un 3.3.3.3"','status 0', 'pass', 'Closed RFAPI') +luCommand("r4", 'vtysh -c "clear vnc nve *"', ".", "pass", "Cleared NVEs") + +luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 .* Remotely: *Active: 0", + "wait", + "All registrations cleared", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 .* Remotely: *Active: 0", + "wait", + "All registrations cleared", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 .* Remotely: *Active: 0", + "wait", + "All registrations cleared", +) + +num = "0 exist" +luCommand("r1", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear") +luCommand("r2", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear") +luCommand("r3", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear") +luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear") + +luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 .* Remotely: *Active: 0 .*In Holddown: *Active: 0", + "wait", + "No holddowns", + 20, +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 .* Remotely: *Active: 0 .*In Holddown: *Active: 0", + "wait", + "No holddowns", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 .* Remotely: *Active: 0 .*In Holddown: *Active: 0", + "wait", + "No holddowns", +) + +luCommand("r1", 'vtysh -c "show vnc summary"', ".", "none") +luCommand("r3", 'vtysh -c "show vnc summary"', ".", "none") +luCommand("r4", 'vtysh -c "show vnc summary"', ".", "none") diff --git a/tests/topotests/bgp_rfapi_basic_sanity/test_bgp_rfapi_basic_sanity.py b/tests/topotests/bgp_rfapi_basic_sanity/test_bgp_rfapi_basic_sanity.py new file mode 100755 index 0000000..7534dce --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity/test_bgp_rfapi_basic_sanity.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright (c) 2018, LabN Consulting, L.L.C. +# Authored by Lou Berger +# + +import os +import sys +import pytest + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) + +from lib.ltemplate import * + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd] + + +def test_add_routes(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/add_routes.py", False, CliOnFail, CheckFunc) + + +def test_adjacencies(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/adjacencies.py", False, CliOnFail, CheckFunc) + + +def test_check_routes(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/check_routes.py", False, CliOnFail, CheckFunc) + + +def test_check_close(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/check_close.py", False, CliOnFail, CheckFunc) + + +def test_check_timeout(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/check_timeout.py", False, CliOnFail, CheckFunc) + + +def test_cleanup_all(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/cleanup_all.py", False, CliOnFail, CheckFunc) + + +if __name__ == "__main__": + retval = pytest.main(["-s"]) + sys.exit(retval) diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/__init__.py b/tests/topotests/bgp_rfapi_basic_sanity_config2/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/customize.py b/tests/topotests/bgp_rfapi_basic_sanity_config2/customize.py new file mode 120000 index 0000000..a6b653a --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/customize.py @@ -0,0 +1 @@ +../bgp_rfapi_basic_sanity/customize.py \ No newline at end of file diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf new file mode 100644 index 0000000..3196a16 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf @@ -0,0 +1,51 @@ +frr defaults traditional +! +hostname r1 +password zebra +log stdout notifications +log commands +router bgp 5226 + bgp router-id 1.1.1.1 + bgp cluster-id 1.1.1.1 + no bgp ebgp-requires-policy + neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 update-source 1.1.1.1 +! + address-family ipv4 unicast + redistribute vnc-direct + no neighbor 2.2.2.2 activate + exit-address-family +! + address-family ipv4 vpn + neighbor 2.2.2.2 activate + exit-address-family +! + rfp holddown-factor 0 + rfp full-table-download off +! + vnc defaults + rd auto:vn:123 + response-lifetime 45 + rt both 1000:1 1000:2 + exit-vnc +! + vnc nve-group red + prefix vn 10.0.0.0/8 + rd auto:vn:10 + rt both 1000:10 + exit-vnc +! + vnc nve-group blue + prefix vn 20.0.0.0/8 + rd auto:vn:20 + rt both 1000:20 + exit-vnc +! + vnc nve-group green + prefix vn 30.0.0.0/8 + rd auto:vn:20 + rt both 1000:30 + exit-vnc +! +end diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/ospfd.conf new file mode 120000 index 0000000..d09b09e --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/ospfd.conf @@ -0,0 +1 @@ +../../bgp_rfapi_basic_sanity/r1/ospfd.conf \ No newline at end of file diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/zebra.conf new file mode 120000 index 0000000..728b1b9 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/zebra.conf @@ -0,0 +1 @@ +../../bgp_rfapi_basic_sanity/r1/zebra.conf \ No newline at end of file diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf new file mode 100644 index 0000000..19050e6 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf @@ -0,0 +1,33 @@ +frr defaults traditional +! +hostname r2 +password zebra +log stdout notifications +log commands +router bgp 5226 + bgp router-id 2.2.2.2 + bgp cluster-id 2.2.2.2 + no bgp ebgp-requires-policy + neighbor 1.1.1.1 remote-as 5226 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 update-source 2.2.2.2 + neighbor 3.3.3.3 remote-as 5226 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 update-source 2.2.2.2 + neighbor 4.4.4.4 remote-as 5226 + neighbor 4.4.4.4 timers 3 10 + neighbor 4.4.4.4 update-source 2.2.2.2 + address-family ipv4 unicast + no neighbor 1.1.1.1 activate + no neighbor 3.3.3.3 activate + no neighbor 4.4.4.4 activate + exit-address-family + address-family ipv4 vpn + neighbor 1.1.1.1 activate + neighbor 1.1.1.1 route-reflector-client + neighbor 3.3.3.3 activate + neighbor 3.3.3.3 route-reflector-client + neighbor 4.4.4.4 activate + neighbor 4.4.4.4 route-reflector-client + exit-address-family +end diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/ospfd.conf new file mode 120000 index 0000000..834554e --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/ospfd.conf @@ -0,0 +1 @@ +../../bgp_rfapi_basic_sanity/r2/ospfd.conf \ No newline at end of file diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/zebra.conf new file mode 120000 index 0000000..2fefda5 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/zebra.conf @@ -0,0 +1 @@ +../../bgp_rfapi_basic_sanity/r2/zebra.conf \ No newline at end of file diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf new file mode 100644 index 0000000..e74fc0b --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf @@ -0,0 +1,49 @@ +frr defaults traditional +! +hostname r3 +password zebra +log stdout notifications +log commands +router bgp 5226 + bgp router-id 3.3.3.3 + bgp cluster-id 3.3.3.3 + no bgp ebgp-requires-policy + neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 update-source 3.3.3.3 +! + address-family ipv4 unicast + no neighbor 2.2.2.2 activate + exit-address-family + address-family ipv4 vpn + neighbor 2.2.2.2 activate + exit-address-family +! + rfp holddown-factor 0 + rfp full-table-download off +! + vnc defaults + rd auto:vn:123 + response-lifetime 45 + rt both 1000:1 1000:2 + exit-vnc +! + vnc nve-group red + prefix vn 10.0.0.0/8 + rd auto:vn:10 + rt both 1000:10 + exit-vnc +! + vnc nve-group blue + prefix vn 20.0.0.0/8 + rd auto:vn:20 + rt both 1000:20 + exit-vnc +! + vnc nve-group green + prefix vn 30.0.0.0/8 + rd auto:vn:20 + rt both 1000:30 + exit-vnc +! +end diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/ospfd.conf new file mode 120000 index 0000000..353b9ad --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/ospfd.conf @@ -0,0 +1 @@ +../../bgp_rfapi_basic_sanity/r3/ospfd.conf \ No newline at end of file diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/zebra.conf new file mode 120000 index 0000000..44a63cf --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/zebra.conf @@ -0,0 +1 @@ +../../bgp_rfapi_basic_sanity/r3/zebra.conf \ No newline at end of file diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf new file mode 100644 index 0000000..56474aa --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf @@ -0,0 +1,50 @@ +frr defaults traditional +! +hostname r4 +password zebra +log stdout notifications +log commands +router bgp 5226 + bgp router-id 4.4.4.4 + bgp cluster-id 4.4.4.4 + no bgp ebgp-requires-policy + neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 update-source 4.4.4.4 +! + address-family ipv4 unicast + no neighbor 2.2.2.2 activate + exit-address-family +! + address-family ipv4 vpn + neighbor 2.2.2.2 activate + exit-address-family +! + rfp holddown-factor 0 + rfp full-table-download off +! + vnc defaults + rd auto:vn:123 + response-lifetime 45 + rt both 1000:1 1000:2 + exit-vnc +! + vnc nve-group red + prefix vn 10.0.0.0/8 + rd auto:vn:10 + rt both 1000:10 + exit-vnc +! + vnc nve-group blue + prefix vn 20.0.0.0/8 + rd auto:vn:20 + rt both 1000:20 + exit-vnc +! + vnc nve-group green + prefix vn 30.0.0.0/8 + rd auto:vn:20 + rt both 1000:30 + exit-vnc +! +end diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/ospfd.conf new file mode 120000 index 0000000..3d7a0aa --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/ospfd.conf @@ -0,0 +1 @@ +../../bgp_rfapi_basic_sanity/r4/ospfd.conf \ No newline at end of file diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/zebra.conf new file mode 120000 index 0000000..c2fcd19 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/zebra.conf @@ -0,0 +1 @@ +../../bgp_rfapi_basic_sanity/r4/zebra.conf \ No newline at end of file diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/scripts b/tests/topotests/bgp_rfapi_basic_sanity_config2/scripts new file mode 120000 index 0000000..8ac974d --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/scripts @@ -0,0 +1 @@ +../bgp_rfapi_basic_sanity/scripts/ \ No newline at end of file diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/test_bgp_rfapi_basic_sanity_config2.py b/tests/topotests/bgp_rfapi_basic_sanity_config2/test_bgp_rfapi_basic_sanity_config2.py new file mode 120000 index 0000000..54a67a8 --- /dev/null +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/test_bgp_rfapi_basic_sanity_config2.py @@ -0,0 +1 @@ +../bgp_rfapi_basic_sanity/test_bgp_rfapi_basic_sanity.py \ No newline at end of file diff --git a/tests/topotests/bgp_rmap_extcommunity_none/__init__.py b/tests/topotests/bgp_rmap_extcommunity_none/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_rmap_extcommunity_none/r1/bgpd.conf b/tests/topotests/bgp_rmap_extcommunity_none/r1/bgpd.conf new file mode 100644 index 0000000..8ccdf9c --- /dev/null +++ b/tests/topotests/bgp_rmap_extcommunity_none/r1/bgpd.conf @@ -0,0 +1,8 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external +! +route-map r2 permit 10 + set extcommunity none +! diff --git a/tests/topotests/bgp_rmap_extcommunity_none/r1/zebra.conf b/tests/topotests/bgp_rmap_extcommunity_none/r1/zebra.conf new file mode 100644 index 0000000..b29940f --- /dev/null +++ b/tests/topotests/bgp_rmap_extcommunity_none/r1/zebra.conf @@ -0,0 +1,4 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_rmap_extcommunity_none/r2/bgpd.conf b/tests/topotests/bgp_rmap_extcommunity_none/r2/bgpd.conf new file mode 100644 index 0000000..9d58078 --- /dev/null +++ b/tests/topotests/bgp_rmap_extcommunity_none/r2/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + address-family ipv4 unicast + redistribute connected + neighbor 192.168.1.1 route-map r1 out + exit-address-family +! +route-map r1 permit 10 + set community 123:123 + set extcommunity bandwidth 200 +! diff --git a/tests/topotests/bgp_rmap_extcommunity_none/r2/zebra.conf b/tests/topotests/bgp_rmap_extcommunity_none/r2/zebra.conf new file mode 100644 index 0000000..dc15cf7 --- /dev/null +++ b/tests/topotests/bgp_rmap_extcommunity_none/r2/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.16.1/32 +! +int r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_rmap_extcommunity_none/test_bgp_rmap_extcommunity_none.py b/tests/topotests/bgp_rmap_extcommunity_none/test_bgp_rmap_extcommunity_none.py new file mode 100644 index 0000000..5c7cc8e --- /dev/null +++ b/tests/topotests/bgp_rmap_extcommunity_none/test_bgp_rmap_extcommunity_none.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2021 by +# Donatas Abraitis +# + +""" +Test if route-map extcommunity none works: + +route-map permit 10 + set extcommunity none +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_extcommunity_none(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + + def _bgp_converge(router): + output = json.loads( + router.vtysh_cmd("show bgp ipv4 unicast 172.16.16.1/32 json") + ) + expected = { + "prefix": "172.16.16.1/32", + "paths": [ + { + "community": { + "string": "123:123", + }, + "extendedCommunity": {"string": "LB:65002:25000000 (200.000 Mbps)"}, + } + ], + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "BGP Converge failed" + + def _bgp_extcommunity_strip(router): + router.vtysh_cmd( + "conf t\nrouter bgp 65001\naddress-family ipv4\nneighbor 192.168.1.2 route-map r2 in" + ) + output = json.loads( + router.vtysh_cmd("show bgp ipv4 unicast 172.16.16.1/32 json") + ) + expected = { + "prefix": "172.16.16.1/32", + "paths": [ + { + "community": { + "string": "123:123", + }, + "extendedCommunity": None, + } + ], + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_extcommunity_strip, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to strip incoming extended communities from r2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_roles_capability/__init__.py b/tests/topotests/bgp_roles_capability/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_roles_capability/r1/bgpd.conf b/tests/topotests/bgp_roles_capability/r1/bgpd.conf new file mode 100644 index 0000000..273f933 --- /dev/null +++ b/tests/topotests/bgp_roles_capability/r1/bgpd.conf @@ -0,0 +1,25 @@ +router bgp 64501 + bgp router-id 192.168.2.1 + network 192.0.2.0/24 +! Correct role pair + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 local-role provider + neighbor 192.168.2.2 timers 3 10 +! Incorrect role pair + neighbor 192.168.3.2 remote-as external + neighbor 192.168.3.2 local-role provider + neighbor 192.168.3.2 timers 3 10 +! Missed neighbor role + neighbor 192.168.4.2 remote-as external + neighbor 192.168.4.2 local-role provider + neighbor 192.168.4.2 timers 3 10 +! Missed neighbor role with strict-mode + neighbor 192.168.5.2 remote-as external + neighbor 192.168.5.2 local-role provider strict-mode + neighbor 192.168.5.2 timers 3 10 +! Testing peer-groups + neighbor PG peer-group + neighbor PG remote-as external + neighbor PG local-role provider + neighbor PG timers 3 10 + neighbor 192.168.6.2 peer-group PG diff --git a/tests/topotests/bgp_roles_capability/r1/zebra.conf b/tests/topotests/bgp_roles_capability/r1/zebra.conf new file mode 100644 index 0000000..301a1e8 --- /dev/null +++ b/tests/topotests/bgp_roles_capability/r1/zebra.conf @@ -0,0 +1,18 @@ +! +interface r1-eth0 + ip address 192.168.2.1/24 +! +interface r1-eth1 + ip address 192.168.3.1/24 +! +interface r1-eth2 + ip address 192.168.4.1/24 +! +interface r1-eth3 + ip address 192.168.5.1/24 +! +interface r1-eth4 + ip address 192.168.6.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_roles_capability/r2/bgpd.conf b/tests/topotests/bgp_roles_capability/r2/bgpd.conf new file mode 100644 index 0000000..aa8ba72 --- /dev/null +++ b/tests/topotests/bgp_roles_capability/r2/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 64502 + bgp router-id 192.168.2.2 + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 local-role customer + neighbor 192.168.2.1 timers 3 10 diff --git a/tests/topotests/bgp_roles_capability/r2/zebra.conf b/tests/topotests/bgp_roles_capability/r2/zebra.conf new file mode 100644 index 0000000..86a9784 --- /dev/null +++ b/tests/topotests/bgp_roles_capability/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.2.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_roles_capability/r3/bgpd.conf b/tests/topotests/bgp_roles_capability/r3/bgpd.conf new file mode 100644 index 0000000..5ed93d6 --- /dev/null +++ b/tests/topotests/bgp_roles_capability/r3/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 64503 + neighbor 192.168.3.1 remote-as external + neighbor 192.168.3.1 local-role peer + neighbor 192.168.3.1 timers 3 10 diff --git a/tests/topotests/bgp_roles_capability/r3/zebra.conf b/tests/topotests/bgp_roles_capability/r3/zebra.conf new file mode 100644 index 0000000..9df578e --- /dev/null +++ b/tests/topotests/bgp_roles_capability/r3/zebra.conf @@ -0,0 +1,6 @@ +! +interface r3-eth0 + ip address 192.168.3.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_roles_capability/r4/bgpd.conf b/tests/topotests/bgp_roles_capability/r4/bgpd.conf new file mode 100644 index 0000000..118132d --- /dev/null +++ b/tests/topotests/bgp_roles_capability/r4/bgpd.conf @@ -0,0 +1,3 @@ +router bgp 64504 + neighbor 192.168.4.1 remote-as external + neighbor 192.168.4.1 timers 3 10 diff --git a/tests/topotests/bgp_roles_capability/r4/zebra.conf b/tests/topotests/bgp_roles_capability/r4/zebra.conf new file mode 100644 index 0000000..03632ee --- /dev/null +++ b/tests/topotests/bgp_roles_capability/r4/zebra.conf @@ -0,0 +1,6 @@ +! +interface r4-eth0 + ip address 192.168.4.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_roles_capability/r5/bgpd.conf b/tests/topotests/bgp_roles_capability/r5/bgpd.conf new file mode 100644 index 0000000..2f62fbf --- /dev/null +++ b/tests/topotests/bgp_roles_capability/r5/bgpd.conf @@ -0,0 +1,3 @@ +router bgp 64505 + neighbor 192.168.5.1 remote-as external + neighbor 192.168.5.1 timers 3 10 diff --git a/tests/topotests/bgp_roles_capability/r5/zebra.conf b/tests/topotests/bgp_roles_capability/r5/zebra.conf new file mode 100644 index 0000000..2e95d77 --- /dev/null +++ b/tests/topotests/bgp_roles_capability/r5/zebra.conf @@ -0,0 +1,6 @@ +! +interface r5-eth0 + ip address 192.168.5.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_roles_capability/r6/bgpd.conf b/tests/topotests/bgp_roles_capability/r6/bgpd.conf new file mode 100644 index 0000000..44b12fe --- /dev/null +++ b/tests/topotests/bgp_roles_capability/r6/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 64506 + neighbor 192.168.6.1 remote-as external + neighbor 192.168.6.1 local-role customer + neighbor 192.168.6.1 timers 3 10 diff --git a/tests/topotests/bgp_roles_capability/r6/zebra.conf b/tests/topotests/bgp_roles_capability/r6/zebra.conf new file mode 100644 index 0000000..c8428c4 --- /dev/null +++ b/tests/topotests/bgp_roles_capability/r6/zebra.conf @@ -0,0 +1,6 @@ +! +interface r6-eth0 + ip address 192.168.6.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_roles_capability/roles_capability_stand.dot b/tests/topotests/bgp_roles_capability/roles_capability_stand.dot new file mode 100644 index 0000000..c0b5c81 --- /dev/null +++ b/tests/topotests/bgp_roles_capability/roles_capability_stand.dot @@ -0,0 +1,15 @@ +graph roles_filtering_stand { + layout="circo" + label="roles capability stand" + fontsize="20" + + r1 [label="r1 provider"]; + r2 [label="r2"]; + r3 [label="r3"]; + r4 [label="r4"]; + r5 [label="r5"]; + r1 -- r2 [headlabel="customer", taillabel="provider"]; + r1 -- r3 [headlabel="peer", taillabel="provider"]; + r1 -- r4 [headlabel="?", taillabel="provider"]; + r1 -- r5 [headlabel="?", taillabel="provider", label="strict-mode"]; +} diff --git a/tests/topotests/bgp_roles_capability/roles_capability_stand.jpg b/tests/topotests/bgp_roles_capability/roles_capability_stand.jpg new file mode 100644 index 0000000..b8dea2f Binary files /dev/null and b/tests/topotests/bgp_roles_capability/roles_capability_stand.jpg differ diff --git a/tests/topotests/bgp_roles_capability/test_bgp_roles_capability.py b/tests/topotests/bgp_roles_capability/test_bgp_roles_capability.py new file mode 100644 index 0000000..52fda69 --- /dev/null +++ b/tests/topotests/bgp_roles_capability/test_bgp_roles_capability.py @@ -0,0 +1,172 @@ +#!/usr/bin/python +# SPDX-License-Identifier: ISC +# +# test_bgp_roles_capability.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2022 by Eugene Bogomazov +# Copyright (c) 2017 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_bgp_roles_capability: test bgp roles negotiation +""" + +import json +import os +import sys +import functools +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +topodef = {f"s{i}": ("r1", f"r{i}") for i in range(2, 7)} + + +@pytest.fixture(scope="module") +def tgen(request): + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + router.load_config(TopoRouter.RD_BGP, "bgpd.conf") + tgen.start_router() + yield tgen + tgen.stop_topology() + + +@pytest.fixture(autouse=True) +def skip_on_failure(tgen): + if tgen.routers_have_failure(): + pytest.skip("skipped because of previous test failure") + + +def find_neighbor_status(router, neighbor_ip): + return json.loads(router.vtysh_cmd(f"show bgp neighbors {neighbor_ip} json"))[ + neighbor_ip + ] + + +def check_role_mismatch(router, neighbor_ip): + return is_role_mismatch(find_neighbor_status(router, neighbor_ip)) + + +def is_role_mismatch(neighbor_status): + return ( + neighbor_status["bgpState"] != "Established" + and neighbor_status.get("lastErrorCodeSubcode") == "020B" # <2, 11> + and "Role Mismatch" in neighbor_status.get("lastNotificationReason", "") + ) + + +def check_session_established(router, neighbor_ip): + neighbor_status = find_neighbor_status(router, neighbor_ip) + return neighbor_status["bgpState"] == "Established" + + +def test_correct_pair(tgen): + # provider-customer pair + router = tgen.gears["r1"] + neighbor_ip = "192.168.2.2" + check_r2_established = functools.partial( + check_session_established, router, neighbor_ip + ) + success, result = topotest.run_and_expect( + check_r2_established, True, count=20, wait=3 + ) + assert success, "Session with r2 is not Established" + + neighbor_status = find_neighbor_status(router, neighbor_ip) + assert neighbor_status["localRole"] == "provider" + assert neighbor_status["remoteRole"] == "customer" + assert ( + neighbor_status["neighborCapabilities"].get("role") == "advertisedAndReceived" + ) + + +def test_role_pair_mismatch(tgen): + # provider-peer mistmatch + router = tgen.gears["r3"] + neighbor_ip = "192.168.3.1" + check_r3_mismatch = functools.partial(check_role_mismatch, router, neighbor_ip) + success, result = topotest.run_and_expect(check_r3_mismatch, True, count=20, wait=3) + assert success, "Session between r1 and r3 was not correctly closed" + + +def test_single_role_advertising(tgen): + # provider-undefined pair; we set role + router = tgen.gears["r1"] + neighbor_ip = "192.168.4.2" + check_r4_established = functools.partial( + check_session_established, router, neighbor_ip + ) + success, result = topotest.run_and_expect( + check_r4_established, True, count=20, wait=3 + ) + assert success, "Session with r4 is not Established" + + neighbor_status = find_neighbor_status(router, neighbor_ip) + assert neighbor_status["localRole"] == "provider" + assert neighbor_status["remoteRole"] == "undefined" + assert neighbor_status["neighborCapabilities"].get("role") == "advertised" + + +def test_single_role_receiving(tgen): + # provider-undefined pair; we receive role + router = tgen.gears["r4"] + neighbor_ip = "192.168.4.1" + check_r1_established = functools.partial( + check_session_established, router, neighbor_ip + ) + success, result = topotest.run_and_expect( + check_r1_established, True, count=20, wait=3 + ) + assert success, "Session with r1 is not Established" + + neighbor_status = find_neighbor_status(router, neighbor_ip) + assert neighbor_status["localRole"] == "undefined" + assert neighbor_status["remoteRole"] == "provider" + assert neighbor_status["neighborCapabilities"].get("role") == "received" + + +def test_role_strict_mode(tgen): + # provider-undefined pair with strict-mode + router = tgen.gears["r5"] + neighbor_ip = "192.168.5.1" + check_r5_mismatch = functools.partial(check_role_mismatch, router, neighbor_ip) + success, result = topotest.run_and_expect(check_r5_mismatch, True, count=20, wait=3) + assert success, "Session between r1 and r5 was not correctly closed" + + +def test_correct_pair_peer_group(tgen): + # provider-customer pair (using peer-groups) + router = tgen.gears["r1"] + neighbor_ip = "192.168.6.2" + check_r6_established = functools.partial( + check_session_established, router, neighbor_ip + ) + success, _ = topotest.run_and_expect(check_r6_established, True, count=20, wait=3) + assert success, "Session with r6 is not Established" + + neighbor_status = find_neighbor_status(router, neighbor_ip) + assert neighbor_status["localRole"] == "provider" + assert neighbor_status["remoteRole"] == "customer" + assert ( + neighbor_status["neighborCapabilities"].get("role") == "advertisedAndReceived" + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_roles_filtering/__init__.py b/tests/topotests/bgp_roles_filtering/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_roles_filtering/r1/bgpd.conf b/tests/topotests/bgp_roles_filtering/r1/bgpd.conf new file mode 100644 index 0000000..99f6211 --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r1/bgpd.conf @@ -0,0 +1,12 @@ +! Provider on this side +router bgp 64501 + bgp router-id 192.168.1.1 + no bgp network import-check + network 192.0.2.1/32 + neighbor 192.168.1.2 remote-as 64510 + neighbor 192.168.1.2 local-role provider + neighbor 192.168.1.2 route-map ALLOW_ALL out + neighbor 192.168.1.2 route-map ALLOW_ALL in + neighbor 192.168.1.2 timers 3 10 + +route-map ALLOW_ALL permit 999 diff --git a/tests/topotests/bgp_roles_filtering/r1/zebra.conf b/tests/topotests/bgp_roles_filtering/r1/zebra.conf new file mode 100644 index 0000000..acf120b --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_roles_filtering/r10/bgpd.conf b/tests/topotests/bgp_roles_filtering/r10/bgpd.conf new file mode 100644 index 0000000..f60bc6e --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r10/bgpd.conf @@ -0,0 +1,21 @@ +! Customer on other side +router bgp 64510 + bgp router-id 192.168.10.1 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as 64501 + neighbor 192.168.1.1 timers 3 10 + neighbor 192.168.2.1 remote-as 64502 + neighbor 192.168.2.1 timers 3 10 + neighbor 192.168.3.1 remote-as 64503 + neighbor 192.168.3.1 timers 3 10 + neighbor 192.168.4.1 remote-as 64504 + neighbor 192.168.4.1 timers 3 10 + neighbor 192.168.5.1 remote-as 64505 + neighbor 192.168.5.1 local-role provider + neighbor 192.168.5.1 timers 3 10 + neighbor 192.168.6.1 remote-as 64506 + neighbor 192.168.6.1 local-role peer + neighbor 192.168.6.1 timers 3 10 + neighbor 192.168.7.1 remote-as 64507 + neighbor 192.168.7.1 local-role customer + neighbor 192.168.7.1 timers 3 10 diff --git a/tests/topotests/bgp_roles_filtering/r10/zebra.conf b/tests/topotests/bgp_roles_filtering/r10/zebra.conf new file mode 100644 index 0000000..f2733fe --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r10/zebra.conf @@ -0,0 +1,24 @@ +! +interface r10-eth0 + ip address 192.168.1.2/24 +! +interface r10-eth1 + ip address 192.168.2.2/24 +! +interface r10-eth2 + ip address 192.168.3.2/24 +! +interface r10-eth3 + ip address 192.168.4.2/24 +! +interface r10-eth4 + ip address 192.168.5.2/24 +! +interface r10-eth5 + ip address 192.168.6.2/24 +! +interface r10-eth6 + ip address 192.168.7.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_roles_filtering/r2/bgpd.conf b/tests/topotests/bgp_roles_filtering/r2/bgpd.conf new file mode 100644 index 0000000..b6db8c1 --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r2/bgpd.conf @@ -0,0 +1,12 @@ +! With peer on this side +router bgp 64502 + bgp router-id 192.168.2.1 + no bgp network import-check + network 192.0.2.2/32 + neighbor 192.168.2.2 remote-as 64510 + neighbor 192.168.2.2 local-role peer + neighbor 192.168.2.2 route-map ALLOW_ALL out + neighbor 192.168.2.2 route-map ALLOW_ALL in + neighbor 192.168.2.2 timers 3 10 + +route-map ALLOW_ALL permit 999 diff --git a/tests/topotests/bgp_roles_filtering/r2/zebra.conf b/tests/topotests/bgp_roles_filtering/r2/zebra.conf new file mode 100644 index 0000000..f785ea1 --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.2.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_roles_filtering/r3/bgpd.conf b/tests/topotests/bgp_roles_filtering/r3/bgpd.conf new file mode 100644 index 0000000..70f10b1 --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r3/bgpd.conf @@ -0,0 +1,12 @@ +! Customer role on this side +router bgp 64503 + bgp router-id 192.168.3.1 + no bgp network import-check + network 192.0.2.3/32 + neighbor 192.168.3.2 remote-as 64510 + neighbor 192.168.3.2 local-role customer + neighbor 192.168.3.2 route-map ALLOW_ALL out + neighbor 192.168.3.2 route-map ALLOW_ALL in + neighbor 192.168.3.2 timers 3 10 + +route-map ALLOW_ALL permit 999 diff --git a/tests/topotests/bgp_roles_filtering/r3/zebra.conf b/tests/topotests/bgp_roles_filtering/r3/zebra.conf new file mode 100644 index 0000000..b347257 --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r3/zebra.conf @@ -0,0 +1,6 @@ +! +interface r3-eth0 + ip address 192.168.3.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_roles_filtering/r4/bgpd.conf b/tests/topotests/bgp_roles_filtering/r4/bgpd.conf new file mode 100644 index 0000000..11e324e --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r4/bgpd.conf @@ -0,0 +1,11 @@ +! Without role on this side +router bgp 64504 + bgp router-id 192.168.4.1 + no bgp network import-check + network 192.0.2.4/32 + neighbor 192.168.4.2 remote-as 64510 + neighbor 192.168.4.2 route-map ALLOW_ALL out + neighbor 192.168.4.2 route-map ALLOW_ALL in + neighbor 192.168.4.2 timers 3 10 + +route-map ALLOW_ALL permit 999 diff --git a/tests/topotests/bgp_roles_filtering/r4/zebra.conf b/tests/topotests/bgp_roles_filtering/r4/zebra.conf new file mode 100644 index 0000000..3543c08 --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r4/zebra.conf @@ -0,0 +1,6 @@ +! +interface r4-eth0 + ip address 192.168.4.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_roles_filtering/r5/bgpd.conf b/tests/topotests/bgp_roles_filtering/r5/bgpd.conf new file mode 100644 index 0000000..39d2a8d --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r5/bgpd.conf @@ -0,0 +1,11 @@ +! Provider on other side +router bgp 64505 + bgp router-id 192.168.5.1 + no bgp network import-check + network 192.0.2.5/32 + neighbor 192.168.5.2 remote-as 64510 + neighbor 192.168.5.2 route-map ALLOW_ALL out + neighbor 192.168.5.2 route-map ALLOW_ALL in + neighbor 192.168.5.2 timers 3 10 + +route-map ALLOW_ALL permit 999 diff --git a/tests/topotests/bgp_roles_filtering/r5/zebra.conf b/tests/topotests/bgp_roles_filtering/r5/zebra.conf new file mode 100644 index 0000000..4a1c273 --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r5/zebra.conf @@ -0,0 +1,6 @@ +! +interface r5-eth0 + ip address 192.168.5.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_roles_filtering/r6/bgpd.conf b/tests/topotests/bgp_roles_filtering/r6/bgpd.conf new file mode 100644 index 0000000..25e5cd8 --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r6/bgpd.conf @@ -0,0 +1,11 @@ +! Peer on other side +router bgp 64506 + bgp router-id 192.168.6.1 + no bgp network import-check + network 192.0.2.6/32 + neighbor 192.168.6.2 remote-as 64510 + neighbor 192.168.6.2 route-map ALLOW_ALL out + neighbor 192.168.6.2 route-map ALLOW_ALL in + neighbor 192.168.6.2 timers 3 10 + +route-map ALLOW_ALL permit 999 diff --git a/tests/topotests/bgp_roles_filtering/r6/zebra.conf b/tests/topotests/bgp_roles_filtering/r6/zebra.conf new file mode 100644 index 0000000..3644a69 --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r6/zebra.conf @@ -0,0 +1,6 @@ +! +interface r6-eth0 + ip address 192.168.6.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_roles_filtering/r7/bgpd.conf b/tests/topotests/bgp_roles_filtering/r7/bgpd.conf new file mode 100644 index 0000000..5f5f257 --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r7/bgpd.conf @@ -0,0 +1,11 @@ +! Customer on other side +router bgp 64507 + bgp router-id 192.168.7.1 + no bgp network import-check + network 192.0.2.7/32 + neighbor 192.168.7.2 remote-as 64510 + neighbor 192.168.7.2 route-map ALLOW_ALL out + neighbor 192.168.7.2 route-map ALLOW_ALL in + neighbor 192.168.7.2 timers 3 10 + +route-map ALLOW_ALL permit 999 diff --git a/tests/topotests/bgp_roles_filtering/r7/zebra.conf b/tests/topotests/bgp_roles_filtering/r7/zebra.conf new file mode 100644 index 0000000..0407a48 --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/r7/zebra.conf @@ -0,0 +1,6 @@ +! +interface r7-eth0 + ip address 192.168.7.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_roles_filtering/roles_filtering_stand.dot b/tests/topotests/bgp_roles_filtering/roles_filtering_stand.dot new file mode 100644 index 0000000..df0f685 --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/roles_filtering_stand.dot @@ -0,0 +1,21 @@ +graph roles_filtering_stand { + layout="circo" + label="roles filtering stand" + fontsize="20" + + r1 [label="r1 192.0.2.1/32"]; + r2 [label="r2 192.0.2.2/32"]; + r3 [label="r3 192.0.2.3/32"]; + r4 [label="r4 192.0.2.4/32"]; + r5 [label="r5 192.0.2.5/32"]; + r6 [label="r6 192.0.2.6/32"]; + r7 [label="r7 192.0.2.7/32"]; + r10 [label="r10 intermediate"]; + r10 -- r1 [headlabel="provider", taillabel="?"]; + r10 -- r2 [headlabel="peer", taillabel="?"]; + r10 -- r3 [headlabel="customer", taillabel="?"]; + r10 -- r4 [headlabel="?", taillabel="?"]; + r10 -- r5 [headlabel="?", taillabel="provider"]; + r10 -- r6 [headlabel="?", taillabel="peer"]; + r10 -- r7 [headlabel="?", taillabel="customer"]; +} diff --git a/tests/topotests/bgp_roles_filtering/roles_filtering_stand.jpg b/tests/topotests/bgp_roles_filtering/roles_filtering_stand.jpg new file mode 100644 index 0000000..dfedcf8 Binary files /dev/null and b/tests/topotests/bgp_roles_filtering/roles_filtering_stand.jpg differ diff --git a/tests/topotests/bgp_roles_filtering/test_bgp_roles_filtering.py b/tests/topotests/bgp_roles_filtering/test_bgp_roles_filtering.py new file mode 100644 index 0000000..b371586 --- /dev/null +++ b/tests/topotests/bgp_roles_filtering/test_bgp_roles_filtering.py @@ -0,0 +1,131 @@ +#!/usr/bin/python +# SPDX-License-Identifier: ISC +# +# test_bgp_roles_filtering.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2022 by Eugene Bogomazov +# Copyright (c) 2017 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_bgp_roles_filtering: test leaks prevention and mitigation with roles +""" + +import json +import os +import sys +import functools +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.bgp import verify_bgp_convergence_from_running_config +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +topodef = {f"s{i}": (f"r{i}", "r10") for i in range(1, 8)} + + +@pytest.fixture(scope="module") +def tgen(request): + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + router.load_config(TopoRouter.RD_BGP, "bgpd.conf") + tgen.start_router() + yield tgen + tgen.stop_topology() + + +@pytest.fixture(autouse=True) +def skip_on_failure(tgen): + if tgen.routers_have_failure(): + pytest.skip("skipped because of previous test failure") + + +def test_r10_routes(tgen): + # provider-undefine pair bur strict-mode was set + def _routes_half_converged(): + routes = json.loads(tgen.gears["r10"].vtysh_cmd("show bgp ipv4 json"))["routes"] + output = sorted(routes.keys()) + expected = [ + "192.0.2.1/32", + "192.0.2.2/32", + "192.0.2.3/32", + "192.0.2.4/32", + "192.0.2.5/32", + "192.0.2.6/32", + "192.0.2.7/32", + ] + return output == expected + + success, result = topotest.run_and_expect( + _routes_half_converged, True, count=20, wait=3 + ) + assert success, "Routes did not converged" + + routes_with_otc = list() + for number in range(1, 8): + prefix = f"192.0.2.{number}/32" + route_details = json.loads( + tgen.gears["r10"].vtysh_cmd(f"show bgp ipv4 {prefix} json") + ) + if route_details["paths"][0].get("otc") is not None: + routes_with_otc.append(prefix) + assert routes_with_otc == [ + "192.0.2.1/32", + "192.0.2.2/32", + "192.0.2.6/32", + "192.0.2.7/32", + ] + + +def test_r1_routes(tgen): + routes = json.loads(tgen.gears["r1"].vtysh_cmd("show bgp ipv4 json"))["routes"] + routes_list = sorted(routes.keys()) + assert routes_list == [ + "192.0.2.1/32", # own + "192.0.2.3/32", + "192.0.2.4/32", + "192.0.2.5/32", + ] + + +def test_r6_routes(tgen): + routes = json.loads(tgen.gears["r6"].vtysh_cmd("show bgp ipv4 json"))["routes"] + routes_list = sorted(routes.keys()) + assert routes_list == [ + "192.0.2.3/32", + "192.0.2.4/32", + "192.0.2.5/32", + "192.0.2.6/32", # own + ] + + +def test_r4_routes(tgen): + routes = json.loads(tgen.gears["r4"].vtysh_cmd("show bgp ipv4 json"))["routes"] + routes_list = sorted(routes.keys()) + assert routes_list == [ + "192.0.2.1/32", + "192.0.2.2/32", + "192.0.2.3/32", + "192.0.2.4/32", + "192.0.2.5/32", + "192.0.2.6/32", + "192.0.2.7/32", + ] + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_route_aggregation/bgp_aggregation.json b/tests/topotests/bgp_route_aggregation/bgp_aggregation.json new file mode 100644 index 0000000..5520f67 --- /dev/null +++ b/tests/topotests/bgp_route_aggregation/bgp_aggregation.json @@ -0,0 +1,249 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "192.168.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "192.168.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": { + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + } + } + }, + "r3": { + "dest_link": { + "r1": { + } + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": { + } + } + }, + "r4": { + "dest_link": { + "r2": { + } + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + } + } + }, + "r4": { + "dest_link": { + "r3": { + } + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": {} + } + }, + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + } + } + }, + "r3": { + "dest_link": { + "r4": { + } + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py b/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py new file mode 100644 index 0000000..412ecc1 --- /dev/null +++ b/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py @@ -0,0 +1,1160 @@ +#!/usr/bin/python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test bgp aggregation functionality: + +1. Verify route summarisation with summary-only for redistributed as well as + locally generated routes. +2. Verify route summarisation with as-set for redistributed routes. + +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_route_maps, + create_prefix_lists, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, + verify_bgp_community, +) +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() + +NETWORK_1_1 = {"ipv4": "10.1.1.0/24", "ipv6": "10:1::1:0/120"} +NETWORK_1_2 = {"ipv4": "10.1.2.0/24", "ipv6": "10:1::2:0/120"} +NETWORK_1_3 = {"ipv4": "10.1.3.0/24", "ipv6": "10:1::3:0/120"} +NETWORK_1_4 = {"ipv4": "10.1.4.0/24", "ipv6": "10:1::4:0/120"} +NETWORK_1_5 = {"ipv4": "10.1.5.0/24", "ipv6": "10:1::5:0/120"} +NETWORK_2_1 = {"ipv4": "10.1.1.100/32", "ipv6": "10:1::1:0/124"} +NETWORK_2_2 = {"ipv4": "10.1.5.0/24", "ipv6": "10:1::5:0/120"} +NETWORK_2_3 = {"ipv4": "10.1.6.0/24", "ipv6": "10:1::6:0/120"} +NETWORK_2_4 = {"ipv4": "10.1.7.0/24", "ipv6": "10:1::7:0/120"} +NETWORK_3_1 = {"ipv4": "10.1.8.0/24", "ipv6": "10:1::8:0/120"} +NETWORK_4_1 = {"ipv4": "10.2.1.0/24", "ipv6": "10:2::1:0/120"} +NEXT_HOP = {"ipv4": "Null0", "ipv6": "Null0"} +AGGREGATE_NW = {"ipv4": "10.1.0.0/20", "ipv6": "10:1::/96"} + +COMMUNITY = [ + "0:1 0:10 0:100", + "0:2 0:20 0:200", + "0:3 0:30 0:300", + "0:4 0:40 0:400", + "0:5 0:50 0:500", + "0:1 0:2 0:3 0:4 0:5 0:10 0:20 0:30 0:40 0:50 0:100 0:200 0:300 0:400 0:500", + "0:3 0:4 0:5 0:30 0:40 0:50 0:300 0:400 0:500", + "0:6 0:60 0:600", + "0:7 0:70 0:700", + "0:3 0:4 0:5 0:6 0:30 0:40 0:50 0:60 0:300 0:400 0:500 0:600", +] + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_aggregation.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + for addr_type in ADDR_TYPES: + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_route_summarisation_with_summary_only_p1(request): + """ + Verify route summarisation with summary-only for redistributed as well as + locally generated routes. + """ + + tgen = get_topogen() + tc_name = request.node.name + reset_config_on_routers(tgen) + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static routes on router R1 and redistribute in " "BGP process.") + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + { + "network": [ + NETWORK_1_1[addr_type], + NETWORK_1_2[addr_type], + NETWORK_1_3[addr_type], + ], + "next_hop": NEXT_HOP[addr_type], + } + ] + } + } + input_redistribute = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + + step("Configuring {} static routes on router R1 ".format(addr_type)) + + result = create_static_routes(tgen, input_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configuring redistribute static for {} address-family on router R1 ".format( + addr_type + ) + ) + + result = create_router_bgp(tgen, topo, input_redistribute) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + { + "network": [ + NETWORK_1_1[addr_type], + NETWORK_1_2[addr_type], + NETWORK_1_3[addr_type], + ] + } + ] + } + } + + result = verify_rib(tgen, addr_type, "r3", input_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Advertise some prefixes using network command") + step( + "Additionally advertise 10.1.4.0/24 & 10.1.5.0/24 and " + "10:1::4:0/120 & 10:1::5:0/120 from R4 to R1." + ) + + for addr_type in ADDR_TYPES: + input_advertise = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": [ + NETWORK_2_1[addr_type], + NETWORK_2_2[addr_type], + NETWORK_2_3[addr_type], + NETWORK_2_4[addr_type], + ] + } + ] + } + } + } + } + }, + "r4": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": [ + NETWORK_1_4[addr_type], + NETWORK_1_5[addr_type], + ] + } + ] + } + } + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_advertise) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that advertised prefixes using network command are being " + "advertised in BGP process" + ) + + for addr_type in ADDR_TYPES: + input_advertise = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": [ + NETWORK_2_1[addr_type], + NETWORK_2_2[addr_type], + NETWORK_2_3[addr_type], + NETWORK_2_4[addr_type], + ] + } + ] + } + } + } + } + } + } + + result = verify_rib(tgen, addr_type, "r3", input_advertise) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure aggregate-address to summarise all the advertised routes.") + + for addr_type in ADDR_TYPES: + route_aggregate = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "aggregate_address": [ + { + "network": AGGREGATE_NW[addr_type], + "summary": True, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, route_aggregate) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that we see 1 summarised route and remaining suppressed " + "routes on advertising router R1 and only 1 summarised route on " + "receiving router R3 for both AFIs." + ) + + for addr_type in ADDR_TYPES: + input_static_agg = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + + input_static = { + "r1": { + "static_routes": [ + { + "network": [ + NETWORK_1_1[addr_type], + NETWORK_1_2[addr_type], + NETWORK_1_3[addr_type], + ] + } + ] + } + } + + result = verify_rib(tgen, addr_type, "r3", input_static_agg, protocol="bgp") + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, "r3", input_static, protocol="bgp", expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_agg, protocol="bgp") + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for action, value in zip(["removed", "add"], [True, False]): + + step( + "{} static routes as below: " + "(no) ip route 10.1.1.0/24 and (no) ip route 10.1.2.0/24" + "(no) ipv6 route 10:1::1:0/120 and (no) ip route 10:1::2:0/120".format( + action + ) + ) + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + { + "network": [NETWORK_1_1[addr_type], NETWORK_1_2[addr_type]], + "next_hop": NEXT_HOP[addr_type], + "delete": value, + } + ] + } + } + + result = create_static_routes(tgen, input_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that there is no impact on R3, as summarised route remains " + "intact. However suppressed routes on R1 disappear and re-appear " + "based on {} static routes.".format(action) + ) + + for addr_type in ADDR_TYPES: + input_static_1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK_1_1[addr_type], NETWORK_1_2[addr_type]]} + ] + } + } + + input_static_2 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + + if value: + result = verify_rib( + tgen, addr_type, "r1", input_static_1, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Routes are still present \n Error: {}".format( + tc_name, result + ) + else: + result = verify_rib(tgen, addr_type, "r1", input_static_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", input_static_2, protocol="bgp") + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "{} prefixes using network command as below:" + "(no) network 10.1.6.1/24 and (no) network 10.1.7.1/24" + "(no) network 10:1::6:0/120 and (no) network 10:1::7:0/120".format(action) + ) + + for addr_type in ADDR_TYPES: + input_advertise = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": [ + NETWORK_2_3[addr_type], + NETWORK_2_4[addr_type], + ], + "delete": value, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_advertise) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that there is no impact on R3, as summarised route remains " + "intact. However suppressed routes on R1 disappear and re-appear " + "based on {} of network command.".format(action) + ) + + for addr_type in ADDR_TYPES: + input_advertise_1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": [ + NETWORK_2_3[addr_type], + NETWORK_2_4[addr_type], + ] + } + ] + } + } + } + } + } + } + + input_advertise_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + {"network": AGGREGATE_NW[addr_type]} + ] + } + } + } + } + } + } + + if value: + result = verify_bgp_rib( + tgen, addr_type, "r1", input_advertise_1, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Routes are still present \n Error: {}".format(tc_name, result) + ) + else: + result = verify_bgp_rib(tgen, addr_type, "r1", input_advertise_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", input_advertise_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Add a new network each one from out of aggregation range and " + "other within aggregation range. " + ) + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + {"network": NETWORK_3_1[addr_type], "next_hop": NEXT_HOP[addr_type]} + ] + } + } + + result = create_static_routes(tgen, input_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_advertise = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": NETWORK_4_1[addr_type], + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_advertise) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that when a network within aggregation range is added, " + "there is no impact on receiving router. However if a network " + "outside aggregation range is added/removed, R3 receives and " + "withdraws it accordingly." + ) + + for addr_type in ADDR_TYPES: + input_static = {"r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]}} + + result = verify_rib(tgen, addr_type, "r3", input_static, protocol="bgp") + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_advertise_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": [ + NETWORK_4_1[addr_type], + AGGREGATE_NW[addr_type], + ] + } + ] + } + } + } + } + } + } + + result = verify_rib(tgen, addr_type, "r3", input_advertise_2, protocol="bgp") + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for action, value in zip(["Delete", "Re-add"], [True, False]): + step("{} aggregation command from R1.".format(action)) + + for addr_type in ADDR_TYPES: + route_aggregate = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "aggregate_address": [ + { + "network": AGGREGATE_NW[addr_type], + "summary": True, + "delete": value, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, route_aggregate) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify on both routers that summarised route is withdrawn from R1 " + "and R3 when aggregate-address command is removed and appears again " + "when aggregate-address command is re-added. Check for both AFIs." + ) + + for addr_type in ADDR_TYPES: + input_static_agg = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + + if value: + result = verify_rib( + tgen, addr_type, "r1", input_static_agg, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Aggregated route is still present \n Error: {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, "r3", input_static_agg, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Aggregated route is still present \n Error: {}".format( + tc_name, result + ) + else: + result = verify_rib(tgen, addr_type, "r1", input_static_agg) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", input_static_agg) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_summarisation_with_as_set_p1(request): + """ + Verify route summarisation with as-set for redistributed routes. + """ + + tgen = get_topogen() + tc_name = request.node.name + reset_config_on_routers(tgen) + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static routes on router R1 and redistribute in " "BGP process.") + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + { + "network": [ + NETWORK_1_1[addr_type], + NETWORK_1_2[addr_type], + NETWORK_1_3[addr_type], + NETWORK_1_4[addr_type], + NETWORK_1_5[addr_type], + ], + "next_hop": NEXT_HOP[addr_type], + } + ] + } + } + input_redistribute = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + + step("Configuring {} static routes on router R1 ".format(addr_type)) + + result = create_static_routes(tgen, input_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configuring redistribute static for {} address-family on router R1 ".format( + addr_type + ) + ) + + result = create_router_bgp(tgen, topo, input_redistribute) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + { + "network": [ + NETWORK_1_1[addr_type], + NETWORK_1_2[addr_type], + NETWORK_1_3[addr_type], + NETWORK_1_4[addr_type], + NETWORK_1_5[addr_type], + ] + } + ] + } + } + + result = verify_rib(tgen, addr_type, "r3", input_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure a route-map to attach a unique community attribute value " + "to each of these prefixes, while re-distributing static." + ) + + for addr_type in ADDR_TYPES: + for pfx, seq_id, network, in zip( + [1, 2, 3, 4, 5], + [10, 20, 30, 40, 50], + [NETWORK_1_1, NETWORK_1_2, NETWORK_1_3, NETWORK_1_4, NETWORK_1_5], + ): + prefix_list = { + "r1": { + "prefix_lists": { + addr_type: { + "pf_list_{}_{}".format(addr_type, pfx): [ + { + "seqid": seq_id, + "network": network[addr_type], + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, prefix_list) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Create route-map for applying prefix-list on r1") + + for addr_type in ADDR_TYPES: + for pfx, comm_id in zip([1, 2, 3, 4, 5], [0, 1, 2, 3, 4]): + route_map = { + "r1": { + "route_maps": { + "rmap_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_{}_{}".format( + addr_type, pfx + ) + } + }, + "set": {"community": {"num": COMMUNITY[comm_id]}}, + } + ] + } + } + } + + result = create_route_maps(tgen, route_map) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Re-configure redistribute static with route-map") + + for addr_type in ADDR_TYPES: + input_redistribute = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": { + "route-map": "rmap_{}".format(addr_type) + }, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_redistribute) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure aggregate-address to summarise all the advertised routes.") + + for addr_type in ADDR_TYPES: + route_aggregate = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "aggregate_address": [ + {"network": AGGREGATE_NW[addr_type], "as_set": True} + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, route_aggregate) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that we see summarised route on router R3 with all the " + "community attribute values combined with that aggregate route." + ) + + for addr_type in ADDR_TYPES: + input_dict = {"community": COMMUNITY[5]} + result = verify_bgp_community( + tgen, addr_type, "r3", [AGGREGATE_NW[addr_type]], input_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Remove static routes as below: " + "(no) ip route 10.1.1.0/24 blackhole " + "(no) ip route 10.1.2.0/24 blackhole " + "(no) ipv6 route 10:1::1:0/120 blackhole " + "(no) ipv6 route 10:1::2:0/120 blackhole " + ) + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + { + "network": [NETWORK_1_1[addr_type], NETWORK_1_2[addr_type]], + "next_hop": NEXT_HOP[addr_type], + "delete": True, + } + ] + } + } + + result = create_static_routes(tgen, input_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify on R3 that whenever we remove the static routes, we still" + " see aggregated route however the corresponding community attribute" + "values are withdrawn." + ) + + for addr_type in ADDR_TYPES: + input_dict = {"community": COMMUNITY[6]} + result = verify_bgp_community( + tgen, addr_type, "r3", [AGGREGATE_NW[addr_type]], input_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Add/remove a new network with community value, each one from out of " + "aggregation range and other within aggregation range. " + ) + + step( + "Add a new network each one from out of aggregation range and " + "other within aggregation range. " + ) + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + { + "network": [NETWORK_3_1[addr_type], NETWORK_4_1[addr_type]], + "next_hop": NEXT_HOP[addr_type], + } + ] + } + } + + result = create_static_routes(tgen, input_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for ( + pfx, + seq_id, + network, + ) in zip([6, 7], [60, 70], [NETWORK_3_1, NETWORK_4_1]): + prefix_list = { + "r1": { + "prefix_lists": { + addr_type: { + "pf_list_{}_{}".format(addr_type, pfx): [ + { + "seqid": seq_id, + "network": network[addr_type], + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, prefix_list) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Create route-map for applying prefix-list on r1") + + for addr_type in ADDR_TYPES: + for pfx, comm_id in zip([6, 7], [7, 8]): + route_map = { + "r1": { + "route_maps": { + "rmap_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_{}_{}".format( + addr_type, pfx + ) + } + }, + "set": {"community": {"num": COMMUNITY[comm_id]}}, + } + ] + } + } + } + + result = create_route_maps(tgen, route_map) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify on R3 when route is added within the summary range, aggregated" + " route also has associated community value added. However if the route" + " is beyond the summary range the aggregated route would have no impact" + ) + + for addr_type in ADDR_TYPES: + input_dict = {"community": COMMUNITY[9]} + result = verify_bgp_community( + tgen, addr_type, "r3", [AGGREGATE_NW[addr_type]], input_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + for action, value in zip(["Delete", "Re-add"], [True, False]): + step("{} aggregation command from R1.".format(action)) + + for addr_type in ADDR_TYPES: + route_aggregate = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "aggregate_address": [ + { + "network": AGGREGATE_NW[addr_type], + "as_set": True, + "delete": value, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, route_aggregate) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that when as-set command is removed, we do not see community " + "attribute added to summarised route on R3. However when as-set option " + "is re-added, all the community attribute values must appear with " + "summarised route." + ) + + for addr_type in ADDR_TYPES: + input_static_agg = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + + if value: + result = verify_rib( + tgen, addr_type, "r1", input_static_agg, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Aggregated route is still present \n Error: {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, "r3", input_static_agg, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Aggregated route is still present \n Error: {}".format( + tc_name, result + ) + else: + result = verify_rib(tgen, addr_type, "r1", input_static_agg) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", input_static_agg) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict = {"community": COMMUNITY[9]} + result = verify_bgp_community( + tgen, addr_type, "r3", [AGGREGATE_NW[addr_type]], input_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_route_map/bgp_route_map_topo1.json b/tests/topotests/bgp_route_map/bgp_route_map_topo1.json new file mode 100644 index 0000000..e892639 --- /dev/null +++ b/tests/topotests/bgp_route_map/bgp_route_map_topo1.json @@ -0,0 +1,187 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base":"10.0.0.0", + "ipv4mask":30, + "ipv6base":"fd00::", + "ipv6mask":64, + "link_ip_start":{"ipv4":"10.0.0.0", "v4mask":30, "ipv6":"fd00::", "v6mask":64}, + "lo_prefix":{"ipv4":"1.0.", "v4mask":32, "ipv6":"2001:DB8:F::", "v6mask":128}, + "routers":{ + "r1":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2":{"ipv4":"auto", "ipv6":"auto"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1":{"ipv4":"auto", "ipv6":"auto"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1":{"ipv4":"auto", "ipv6":"auto"}, + "r2":{"ipv4":"auto", "ipv6":"auto"}, + "r4":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_route_map/bgp_route_map_topo2.json b/tests/topotests/bgp_route_map/bgp_route_map_topo2.json new file mode 100755 index 0000000..c22a4c3 --- /dev/null +++ b/tests/topotests/bgp_route_map/bgp_route_map_topo2.json @@ -0,0 +1,316 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + }, + "redistribute": [{ + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + }, + "redistribute": [{ + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + }, + + "static_routes": [{ + "network": "10.0.20.1/32", + "no_of_ip": 2, + "next_hop": "10.0.0.2" + }, + { + "network": "1::1/128", + "no_of_ip": 2, + "next_hop": "fd00::2" + }] + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r5": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_route_map/test_route_map_topo1.py b/tests/topotests/bgp_route_map/test_route_map_topo1.py new file mode 100644 index 0000000..e300e8d --- /dev/null +++ b/tests/topotests/bgp_route_map/test_route_map_topo1.py @@ -0,0 +1,1393 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +import sys +import time +import pytest +import os + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +# Required to instantiate the topology builder class. +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + verify_rib, + create_route_maps, + create_static_routes, + create_prefix_lists, + check_address_types, + reset_config_on_routers, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_attributes, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +################################# +# TOPOLOGY +################################# +""" + + +-------+ + +------- | R2 | + | +-------+ + | | + +-------+ | + | R1 | | + +-------+ | + | | + | +-------+ +-------+ + +---------- | R3 |----------| R4 | + +-------+ +-------+ + +""" + +################################# +# TEST SUMMARY +################################# +""" +Following tests are covered to test route-map functionality: +TC_34: + Verify if route-maps is applied in both inbound and + outbound direction to same neighbor/interface. +TC_36: + Test permit/deny statements operation in route-maps with a + permutation and combination of permit/deny in prefix-lists +TC_35: + Test multiple sequence numbers in a single route-map for different + match/set clauses. +TC_37: + Test add/remove route-maps with multiple set + clauses and without any match statement.(Set only) +TC_38: + Test add/remove route-maps with multiple match + clauses and without any set statement.(Match only) +""" + +# Global variables +bgp_convergence = False +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() + +# Global variables +bgp_convergence = False +NETWORK = {"ipv4": ["11.0.20.1/32", "20.0.20.1/32"], "ipv6": ["1::1/128", "2::1/128"]} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = {"ipv4": "10.0.0.2", "ipv6": "fd00::2"} +ADDR_TYPES = check_address_types() + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global ADDR_TYPES + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_route_map_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global bgp_convergence + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format( + bgp_convergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def test_route_map_inbound_outbound_same_neighbor_p0(request): + """ + TC_34: + Verify if route-maps is applied in both inbound and + outbound direction to same neighbor/interface. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + for adt in ADDR_TYPES: + + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK[adt][0], + "no_of_ip": 9, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "r4": { + "static_routes": [ + { + "network": NETWORK[adt][1], + "no_of_ip": 9, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_5 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + { + "seqid": 10, + "action": "permit", + "network": NETWORK["ipv4"][0], + } + ], + "pf_list_2_ipv4": [ + { + "seqid": 10, + "action": "permit", + "network": NETWORK["ipv4"][1], + } + ], + }, + "ipv6": { + "pf_list_1_ipv6": [ + { + "seqid": 100, + "action": "permit", + "network": NETWORK["ipv6"][0], + } + ], + "pf_list_2_ipv6": [ + { + "seqid": 100, + "action": "permit", + "network": NETWORK["ipv6"][1], + } + ], + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_6 = { + "r3": { + "route_maps": { + "rmap_match_tag_1_{}".format(addr_type): [ + { + "action": "deny", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + } + ], + "rmap_match_tag_2_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_2_{}".format(addr_type) + } + }, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_7 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv4", + "direction": "in", + }, + { + "name": "rmap_match_tag_1_ipv4", + "direction": "out", + }, + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv6", + "direction": "in", + }, + { + "name": "rmap_match_tag_1_ipv6", + "direction": "out", + }, + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for adt in ADDR_TYPES: + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict_2 = { + "r4": { + "static_routes": [ + { + "network": [NETWORK[adt][1]], + "no_of_ip": 9, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + + result = verify_rib( + tgen, adt, dut, input_dict_2, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes + dut = "r4" + input_dict = { + "r1": { + "static_routes": [ + { + "network": [NETWORK[adt][0]], + "no_of_ip": 9, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + result = verify_rib( + tgen, adt, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize( + "prefix_action, rmap_action", + [("permit", "permit"), ("permit", "deny"), ("deny", "permit"), ("deny", "deny")], +) +def test_route_map_with_action_values_combination_of_prefix_action_p0( + request, prefix_action, rmap_action +): + """ + TC_36: + Test permit/deny statements operation in route-maps with a permutation and + combination of permit/deny in prefix-lists + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + for adt in ADDR_TYPES: + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK[adt][0], + "no_of_ip": 9, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Permit in perfix list and route-map + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": prefix_action} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": prefix_action} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": rmap_action, + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_7 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r3" + protocol = "bgp" + input_dict_2 = { + "r1": { + "static_routes": [ + { + "network": [NETWORK[adt][0]], + "no_of_ip": 9, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + + # tgen.mininet_cli() + if "deny" in [prefix_action, rmap_action]: + result = verify_rib( + tgen, adt, dut, input_dict_2, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + else: + result = verify_rib(tgen, adt, dut, input_dict_2, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + +def test_route_map_multiple_seq_different_match_set_clause_p0(request): + """ + TC_35: + Test multiple sequence numbers in a single route-map for different + match/set clauses. + """ + + tgen = get_topogen() + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + for adt in ADDR_TYPES: + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK[adt][0], + "no_of_ip": 1, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_2_{}".format(addr_type) + } + }, + "set": {"path": {"as_num": 500}}, + }, + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_2_{}".format(addr_type) + } + }, + "set": { + "locPrf": 150, + }, + }, + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 50}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for adt in ADDR_TYPES: + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = { + "r3": { + "route_maps": { + "rmap_match_pf_list1": [ + { + "set": { + "metric": 50, + } + } + ], + } + } + } + + static_routes = [NETWORK[adt][0]] + + time.sleep(2) + result = verify_bgp_attributes( + tgen, adt, dut, static_routes, "rmap_match_pf_list1", input_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r4" + result = verify_bgp_attributes( + tgen, adt, dut, static_routes, "rmap_match_pf_list1", input_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info("Testcase " + tc_name + " :Passed \n") + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_route_map_set_only_no_match_p0(request): + """ + TC_37: + Test add/remove route-maps with multiple set + clauses and without any match statement.(Set only) + """ + + tgen = get_topogen() + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + for adt in ADDR_TYPES: + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK[adt][0], + "no_of_ip": 1, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1": [ + { + "action": "permit", + "set": {"metric": 50, "locPrf": 150, "weight": 4000}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + time.sleep(2) + for adt in ADDR_TYPES: + input_dict_4 = { + "r3": { + "route_maps": { + "rmap_match_pf_1": [ + { + "action": "permit", + "set": { + "metric": 50, + }, + } + ] + } + } + } + # Verifying RIB routes + static_routes = [NETWORK[adt][0]] + result = verify_bgp_attributes( + tgen, adt, "r3", static_routes, "rmap_match_pf_1", input_dict_3 + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_attributes( + tgen, adt, "r4", static_routes, "rmap_match_pf_1", input_dict_4 + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info("Testcase " + tc_name + " :Passed \n") + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_route_map_match_only_no_set_p0(request): + """ + TC_38: + Test add/remove route-maps with multiple match + clauses and without any set statement.(Match only) + """ + + tgen = get_topogen() + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + for adt in ADDR_TYPES: + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK[adt][0], + "no_of_ip": 1, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create ip prefix list + input_dict_2 = { + "r1": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r1": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "metric": 50, + "locPrf": 150, + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create ip prefix list + input_dict_5 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_6 = { + "r3": { + "route_maps": { + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_7 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for adt in ADDR_TYPES: + # Verifying RIB routes + static_routes = [NETWORK[adt][0]] + result = verify_bgp_attributes( + tgen, adt, "r3", static_routes, "rmap_match_pf_1", input_dict_3 + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_route_map/test_route_map_topo2.py b/tests/topotests/bgp_route_map/test_route_map_topo2.py new file mode 100644 index 0000000..95906ec --- /dev/null +++ b/tests/topotests/bgp_route_map/test_route_map_topo2.py @@ -0,0 +1,3958 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +"""Following tests are covered to test route-map functionality. +TC_57: + Create route map to match prefix-list and permit inbound + and outbound prefixes and set criteria on match +TC_52: + Test modify set/match clauses in a route-map to see + if it takes immediate effect. +TC_61: + Delete the route maps. +TC_50_1: + Test modify/remove prefix-lists referenced by a + route-map for match statement. +TC_50_2: + Remove prefix-list referencec by route-map match cluase + and verifying it reflecting as intended +TC_51: + Add and remove community-list referencec by route-map match cluase + and verifying it reflecting as intended +TC_45: + Test multiple match statements as part of a route-map"s single + sequence number. (Logical OR-ed of multiple match statements) +TC_44: + Test multiple match statements as part of a route-map"s single + sequence number. (Logical AND of multiple match statements) +TC_41: + Test add/remove route-maps to specific neighbor and see if + it takes effect as intended +TC_56: + Test clear BGP sessions and interface flaps to see if + route-map properties are intact. +TC_46: + Verify if a blank sequence number can be create(without any + match/set clause) and check if it allows all the traffic/prefixes +TC_48: + Create route map setting local preference and weight to eBGP peeer + and metric to ibgp peer and verifying it should not get advertised +TC_43: + Test multiple set statements as part of a route-map"s + single sequence number. +TC_54: + Verify route-maps continue clause functionality. +TC_55: + Verify route-maps goto clause functionality. +TC_53: + Verify route-maps call clause functionality. +TC_58: + Create route map deny inbound and outbound prefixes on + match prefix list and set criteria on match +TC_59: + Create route map to permit inbound prefixes with filter + match tag and set criteria +TC_60 + Create route map to deny outbound prefixes with filter match tag, + and set criteria + +################################# +# TOPOLOGY +################################# + + +-------+ + +--------- | R2 | + | +-------+ + |iBGP | + +-------+ | + | R1 | |iBGP + +-------+ | + | | + | iBGP +-------+ eBGP +-------+ + +---------- | R3 |----------| R4 | + +-------+ +-------+ + | + |eBGP + | + +-------+ + | R5 | + +-------+ + + +""" + +import sys +import time +import pytest +import inspect +import os +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +# Required to instantiate the topology builder class. +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + create_static_routes, + verify_rib, + delete_route_maps, + create_bgp_community_lists, + create_route_maps, + create_prefix_lists, + verify_route_maps, + check_address_types, + verify_bgp_community, + shutdown_bringup_interface, + verify_prefix_lists, + reset_config_on_routers, + verify_create_community_list, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + clear_bgp_and_verify, + verify_bgp_attributes, +) +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +# Global variables +bgp_convergence = False +NETWORK = {"ipv4": ["11.0.20.1/32", "11.0.20.2/32"], "ipv6": ["2::1/128", "2::2/128"]} + +bgp_convergence = False +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() + + +def setup_module(mod): + """setup_module. + + Set up the pytest environment + * `mod`: module name + """ + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_route_map_topo2.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global bgp_convergence + global ADDR_TYPES + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format( + bgp_convergence + ) + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """teardown_module. + + Teardown the pytest environment. + * `mod`: module name + """ + logger.info("Running teardown_module to delete topology") + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# Tests starting +##################################################### + + +def test_rmap_match_prefix_list_permit_in_and_outbound_prefixes_p0(): + """ + TC: 57 + Create route map to match prefix-list and permit inbound + and outbound prefixes and set criteria on match + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + { + "seqid": 10, + "network": "any", + "action": "permit", + } + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + { + "seqid": 10, + "network": "any", + "action": "permit", + } + ] + }, + } + } + } + + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + for addr_type in ADDR_TYPES: + # Create route map + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: {"prefix_lists": "pf_list_1_" + addr_type} + }, + "set": {"locPrf": 150, "weight": 100}, + }, + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: {"prefix_lists": "pf_list_1_" + addr_type} + }, + "set": {"metric": 50}, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + + # dual stack changes + for addr_type in ADDR_TYPES: + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + # dual stack changes + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result4 = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + # dual stack changes + for addr_type in ADDR_TYPES: + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + # Verifying BGP set attributes + dut = "r4" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + # dual stack changes + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_2_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_modify_set_match_clauses_in_rmap_p0(): + """ + TC_52: + Test modify set/match clauses in a route-map to see + if it takes immediate effect. + """ + + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + { + "seqid": 10, + "network": "any", + "action": "permit", + } + ], + "pf_list_2_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ], + }, + "ipv6": { + "pf_list_1_ipv6": [ + { + "seqid": 10, + "network": "any", + "action": "permit", + } + ], + "pf_list_2_ipv6": [ + {"seqid": 10, "network": "any", "action": "permit"} + ], + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": { + "locPrf": 150, + }, + } + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 50}, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + # dual stack changes + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result4 = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + # dual stack changes + for addr_type in ADDR_TYPES: + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + # Verifying BGP set attributes + dut = "r4" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_2_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Modify set/match clause of in-used route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": { + "locPrf": 1000, + }, + } + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 2000}, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r4" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_2_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_delete_route_maps_p1(): + """ + TC_61: + Delete the route maps. + """ + + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_tag_1_{}".format(addr_type): [ + {"action": "deny", "match": {addr_type: {"tag": "4001"}}} + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Delete route maps + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"route_maps": ["rmap_match_tag_1_{}".format(addr_type)]}} + result = delete_route_maps(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_route_maps(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_modify_prefix_list_referenced_by_rmap_p0(): + """ + TC_50_1: + Test modify/remove prefix-lists referenced by a + route-map for match statement. + """ + + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + { + "seqid": 10, + "network": "any", + "action": "permit", + } + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + { + "seqid": 100, + "network": "any", + "action": "permit", + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150, "weight": 100}, + } + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 50}, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r4" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_2_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Modify ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "deny"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "deny"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + sleep(5) + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_remove_prefix_list_referenced_by_rmap_p0(): + """ + TC_50_2: + Remove prefix-list referencec by route-map match cluase + and verifying it reflecting as intended + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": { + "locPrf": 150, + }, + } + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 50}, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r4" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_2_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Remove/Delete prefix list + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + { + "seqid": 10, + "network": "any", + "action": "permit", + "delete": True, + } + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + { + "seqid": 100, + "network": "any", + "action": "permit", + "delete": True, + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to clear bgp, so config changes would be reflected + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_add_and_remove_community_list_referenced_by_rmap_p0(): + """ + TC_51: + Add and remove community-list referencec by route-map match cluase + and verifying it reflecting as intended + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Creating configuration from JSON + # build_config_from_json(tgen, topo) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_5 = { + "r1": { + "route_maps": { + "rm_r1_out_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "large_community": {"num": "1:1:1 1:2:3 2:1:1 2:2:2"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_6 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rm_r1_out_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rm_r1_out_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + # Create standard large commumity-list + input_dict_1 = { + "r3": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "rmap_lcomm_{}".format(addr_type), + "value": "1:1:1 1:2:3 2:1:1 2:2:2", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verify BGP large community is created + result = verify_create_community_list(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + # Create route map + input_dict_2 = { + "r3": { + "route_maps": { + "rm_r3_in_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "large-community-list": { + "id": "rmap_lcomm_" + addr_type + } + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_3 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rm_r3_in_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rm_r3_in_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_3) + + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + sleep(5) + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verify large-community-list + dut = "r3" + networks = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + input_dict_4 = {"largeCommunity": "1:1:1 1:2:3 2:1:1 2:2:2"} + for addr_type in ADDR_TYPES: + result = verify_bgp_community( + tgen, addr_type, dut, networks[addr_type], input_dict_4 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_multiple_match_statement_in_route_map_logical_ORed_p0(): + """ + TC_45: + Test multiple match statements as part of a route-map"s single + sequence number. (Logical OR-ed of multiple match statements) + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict_nw1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"advertise_networks": [{"network": "10.0.30.1/32"}]} + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_nw1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to advertise networks + input_dict_nw2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"advertise_networks": [{"network": "20.0.30.1/32"}]} + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "2::1/128"}]} + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_nw2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_2_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_2_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_3_addr_type = {} + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150}, + } + ] + } + } + } + input_dict_3_addr_type[addr_type] = input_dict_3 + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 200}, + } + ] + } + } + } + input_dict_3_addr_type[addr_type] = input_dict_3 + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_6 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = {"ipv4": ["10.0.30.1/32"], "ipv6": ["1::1/128"]} + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, + addr_type, + dut, + routes[addr_type], + rmap_name, + input_dict_3_addr_type[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + routes = {"ipv4": ["20.0.30.1/32"], "ipv6": ["2::1/128"]} + for addr_type in ADDR_TYPES: + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_multiple_match_statement_in_route_map_logical_ANDed_p1(): + """ + TC_44: + Test multiple match statements as part of a route-map"s single + sequence number. (Logical AND of multiple match statements) + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_5 = { + "r1": { + "route_maps": { + "rm_r1_out_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "large_community": {"num": "1:1:1 1:2:3 2:1:1 2:2:2"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + for addr_type in ADDR_TYPES: + input_dict_6 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rm_r1_out_{}".format( + addr_type + ), + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + # Create standard large commumity-list + input_dict_1 = { + "r3": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "rmap_lcomm_{}".format(addr_type), + "value": "1:1:1 1:2:3 2:1:1 2:2:2", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verify BGP large community is created + result = verify_create_community_list(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": { + "locPrf": 150, + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + # Create route map + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "large_community_list": { + "id": "rmap_lcomm_" + addr_type + } + } + }, + "set": { + "locPrf": 150, + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + # Configure neighbor for route map + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_{}".format( + addr_type + ), + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + # sleep(10) + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_add_remove_rmap_to_specific_neighbor_p0(): + """ + TC_41: + Test add/remove route-maps to specific neighbor and see if + it takes effect as intended + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "deny"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "deny"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": { + "locPrf": 150, + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Remove applied rmap from neighbor + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_clear_bgp_and_flap_interface_to_verify_rmap_properties_p0(): + """ + TC_56: + Test clear BGP sessions and interface flaps to see if + route-map properties are intact. + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150, "weight": 100}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # clear bgp, so config changes would be reflected + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Flap interface to see if route-map properties are intact + # Shutdown interface + dut = "r3" + intf = "r3-r1-eth0" + shutdown_bringup_interface(tgen, dut, intf, False) + + sleep(5) + + # Bringup interface + dut = "r3" + intf = "r3-r1-eth0" + shutdown_bringup_interface(tgen, dut, intf, True) + + # Verify BGP convergence once interface is up + result = verify_bgp_convergence(tgen, topo) + assert result is True, "setup_module :Failed \n Error:" " {}".format(result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_rmap_without_match_and_set_clause_p0(): + """ + TC_46: + Verify if a blank sequence number can be create(without any + match/set clause) and check if it allows all the traffic/prefixes + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_no_match_set_1_{}".format(addr_type): [ + {"action": "permit", "seq_id": "5"} + ], + "rmap_no_match_set_2_{}".format(addr_type): [ + {"action": "deny", "seq_id": "5"} + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_no_match_set_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_no_match_set_2_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_no_match_set_1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_no_match_set_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_set_localpref_weight_to_ebgp_and_med_to_ibgp_peers_p0(): + """ + TC_48: + Create route map setting local preference and weight to eBGP peeer + and metric to ibgp peer and verifying it should not get advertised + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + input_dict_3_addr_type = {} + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 50}, + } + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150}, + } + ], + "rmap_match_pf_3_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"weight": 1000}, + } + ], + } + } + } + input_dict_3_addr_type[addr_type] = input_dict_3 + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv4", + "direction": "out", + } + ] + } + } + }, + "r5": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_3_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + "r5": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_3_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + rmap_name = "rmap_match_pf_1" + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r4" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + rmap_name = "rmap_match_pf_2" + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_2_{}".format(addr_type) + + result = verify_bgp_attributes( + tgen, + addr_type, + dut, + routes[addr_type], + rmap_name, + input_dict_3_addr_type[addr_type], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: BGP attributes should not be set in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes + dut = "r5" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + # Verifying BGP set attributes + dut = "r5" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + + rmap_name = "rmap_match_pf_3" + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_3_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, + addr_type, + dut, + routes[addr_type], + rmap_name, + input_dict_3_addr_type[addr_type], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: BGP attributes should not be set in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_multiple_set_on_single_sequence_in_rmap_p0(): + """ + TC_43: + Test multiple set statements as part of a route-map"s + single sequence number. + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150, "weight": 100, "metric": 50}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + + rmap_name = "rmap_match_pf_1" + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_route_maps_with_continue_clause_p0(): + """ + TC_54: + Verify route-maps continue clause functionality. + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "10", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150}, + "continue": "30", + }, + { + "action": "permit", + "seq_id": "20", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 200}, + }, + { + "action": "permit", + "seq_id": "30", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 100}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + rmap_name = "rmap_match_pf_1" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + seq_id = {"ipv4": ["10", "30"], "ipv6": ["10", "30"]} + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, + addr_type, + dut, + routes[addr_type], + rmap_name, + input_dict_3, + seq_id[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_route_maps_with_goto_clause_p0(): + """ + TC_55: + Verify route-maps goto clause functionality. + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "10", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "goto": "30", + }, + { + "action": "permit", + "seq_id": "20", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 100}, + }, + { + "action": "permit", + "seq_id": "30", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 200}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + # tgen.mininet_cli() + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + rmap_name = "rmap_match_pf_1" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + seq_id = {"ipv4": ["10", "30"], "ipv6": ["10", "30"]} + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, + addr_type, + dut, + routes[addr_type], + rmap_name, + input_dict_3, + seq_id[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_route_maps_with_call_clause_p0(): + """ + TC_53: + Verify route-maps call clause functionality. + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150}, + "call": "rmap_match_pf_2_{}".format(addr_type), + } + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 200}, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + rmap_name = "rmap_match_pf_1" + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + rmap_name = "rmap_match_pf_2" + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_2_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_create_rmap_match_prefix_list_to_deny_in_and_outbound_prefixes_p0(): + """ + TC_58: + Create route map deny inbound and outbound prefixes on + match prefix list and set criteria on match + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "deny", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": { + "locPrf": 150, + }, + } + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "deny", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 50}, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_create_rmap_to_match_tag_permit_inbound_prefixes_p0(): + """ + TC_59: + Create route map to permit inbound prefixes with filter + match tag and set criteria + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + for addr_type in ADDR_TYPES: + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": "Null0", "tag": 4001} + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + input_dict_3 = { + "r1": { + "route_maps": { + "rmap_match_tag_1_{}".format(addr_type): [ + {"action": "permit", "match": {addr_type: {"tag": "4001"}}} + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": "Null0", "tag": 4001} + ] + } + } + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_create_rmap_to_match_tag_deny_outbound_prefixes_p0(): + """ + TC_60 + Create route map to deny outbound prefixes with filter match tag, + and set criteria + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + for addr_type in ADDR_TYPES: + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": "Null0", "tag": 4001} + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + input_dict_3 = { + "r1": { + "route_maps": { + "rmap_match_tag_1_{}".format(addr_type): [ + {"action": "deny", "match": {addr_type: {"tag": "4001"}}} + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": "Null0", "tag": 4001} + ] + } + } + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_route_map_delay_timer/__init__.py b/tests/topotests/bgp_route_map_delay_timer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf b/tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf new file mode 100644 index 0000000..e5325c9 --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf @@ -0,0 +1,24 @@ +! +!debug bgp updates +!debug bgp neighbor +! +bgp route-map delay-timer 5 +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as external + address-family ipv4 unicast + network 10.10.10.1/32 + network 10.10.10.2/32 + network 10.10.10.3/32 + aggregate-address 10.10.10.0/24 summary-only + neighbor 192.168.1.2 unsuppress-map r2 + exit-address-family +! +ip prefix-list r1 seq 5 permit 10.10.10.1/32 +ip prefix-list r1 seq 10 permit 10.10.10.2/32 +! +route-map r2 permit 10 + match ip address prefix-list r1 +exit diff --git a/tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf b/tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf new file mode 100644 index 0000000..b29940f --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf @@ -0,0 +1,4 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf b/tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf new file mode 100644 index 0000000..36653e6 --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external +! diff --git a/tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf b/tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf new file mode 100644 index 0000000..cffe827 --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf @@ -0,0 +1,4 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py b/tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py new file mode 100644 index 0000000..15a077d --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" + +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_route_map_delay_timer(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge_1(): + output = json.loads( + r1.vtysh_cmd( + "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "10.10.10.0/24": {}, + "10.10.10.1/32": {}, + "10.10.10.2/32": {}, + "10.10.10.3/32": None, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge_1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "10.10.10.3/32 should not be advertised to r2" + + # Set route-map delay-timer to max value and remove 10.10.10.2/32. + # After this, r1 MUST do not announce updates immediately, and wait + # 600 seconds before withdrawing 10.10.10.2/32. + r2.vtysh_cmd( + """ + configure terminal + bgp route-map delay-timer 600 + no ip prefix-list r1 seq 10 permit 10.10.10.2/32 + """ + ) + + def _bgp_converge_2(): + output = json.loads( + r1.vtysh_cmd( + "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "10.10.10.0/24": {}, + "10.10.10.1/32": {}, + "10.10.10.2/32": None, + "10.10.10.3/32": None, + } + } + return topotest.json_cmp(output, expected) + + # We are checking `not None` here to wait count*wait time and if we have different + # results than expected, it means good - 10.10.10.2/32 wasn't withdrawn immediately. + test_func = functools.partial(_bgp_converge_2) + _, result = topotest.run_and_expect(test_func, not None, count=60, wait=0.5) + assert ( + result is not None + ), "10.10.10.2/32 advertised, but should not be advertised to r2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_route_map_match_ipv6_nexthop/__init__.py b/tests/topotests/bgp_route_map_match_ipv6_nexthop/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/bgpd.conf b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/bgpd.conf new file mode 100644 index 0000000..8b743bd --- /dev/null +++ b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/bgpd.conf @@ -0,0 +1,34 @@ +! +bgp send-extra-data zebra +! +ipv6 access-list nh1 permit 2001:db8:1::/64 +ipv6 access-list nh2 permit 2001:db8:2::/64 +ipv6 access-list nh3 permit 2001:db8:3::/64 +! +ipv6 prefix-list nh4 permit 2001:db8:5::/64 le 128 +! +router bgp 65001 + bgp router-id 10.10.10.1 + no bgp ebgp-requires-policy + neighbor 2001:db8::2 remote-as external + address-family ipv6 unicast + neighbor 2001:db8::2 activate + neighbor 2001:db8::2 route-map r2 in + exit-address-family +! +route-map r2 permit 10 + match ipv6 next-hop nh1 + set community 65002:1 +route-map r2 permit 20 + match ipv6 next-hop nh2 + set community 65002:2 +route-map r2 permit 30 + match ipv6 next-hop nh3 + set community 65002:3 +route-map r2 permit 40 + match ipv6 next-hop address 2001:db8:4::1 + set community 65002:4 +route-map r2 permit 50 + match ipv6 next-hop prefix-list nh4 + set community 65002:5 +! diff --git a/tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/zebra.conf b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/zebra.conf new file mode 100644 index 0000000..1d4374b --- /dev/null +++ b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/zebra.conf @@ -0,0 +1,4 @@ +! +int r1-eth0 + ipv6 address 2001:db8::1/64 +! diff --git a/tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/bgpd.conf b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/bgpd.conf new file mode 100644 index 0000000..61c36d3 --- /dev/null +++ b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/bgpd.conf @@ -0,0 +1,35 @@ +! +bgp send-extra-data zebra +! +router bgp 65002 + bgp router-id 10.10.10.2 + no bgp ebgp-requires-policy + neighbor 2001:db8::1 remote-as external + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8::1 activate + neighbor 2001:db8::1 route-map r1 out + exit-address-family +! +ipv6 prefix-list p1 permit 2001:db8:1::1/128 +ipv6 prefix-list p2 permit 2001:db8:2::1/128 +ipv6 prefix-list p3 permit 2001:db8:3::1/128 +ipv6 prefix-list p4 permit 2001:db8:4::1/128 +ipv6 prefix-list p5 permit 2001:db8:5::1/128 +! +route-map r1 permit 10 + match ipv6 address prefix-list p1 + set ipv6 next-hop global 2001:db8:1::1 +route-map r1 permit 20 + match ipv6 address prefix-list p2 + set ipv6 next-hop global 2001:db8:2::1 +route-map r1 permit 30 + match ipv6 address prefix-list p3 + set ipv6 next-hop global 2001:db8:3::1 +route-map r1 permit 40 + match ipv6 address prefix-list p4 + set ipv6 next-hop global 2001:db8:4::1 +route-map r1 permit 50 + match ipv6 address prefix-list p5 + set ipv6 next-hop global 2001:db8:5::1 +! diff --git a/tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/zebra.conf b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/zebra.conf new file mode 100644 index 0000000..e69345f --- /dev/null +++ b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/zebra.conf @@ -0,0 +1,11 @@ +! +int lo + ipv6 address 2001:db8:1::1/128 + ipv6 address 2001:db8:2::1/128 + ipv6 address 2001:db8:3::1/128 + ipv6 address 2001:db8:4::1/128 + ipv6 address 2001:db8:5::1/128 +! +int r2-eth0 + ip address 2001:db8::2/64 +! diff --git a/tests/topotests/bgp_route_map_match_ipv6_nexthop/test_bgp_route_map_match_ipv6_nexthop.py b/tests/topotests/bgp_route_map_match_ipv6_nexthop/test_bgp_route_map_match_ipv6_nexthop.py new file mode 100644 index 0000000..a06e3ed --- /dev/null +++ b/tests/topotests/bgp_route_map_match_ipv6_nexthop/test_bgp_route_map_match_ipv6_nexthop.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2021 by +# Donatas Abraitis +# + +""" +Test if we can match BGP prefixes by next-hop which is +specified by an IPv6 Access-list, prefix-list or just an address. +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_route_map_match_ipv6_next_hop_access_list(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ipv6 route json")) + expected = { + "2001:db8:1::1/128": [ + { + "communities": "65002:1", + } + ], + "2001:db8:2::1/128": [ + { + "communities": "65002:2", + } + ], + "2001:db8:3::1/128": [ + { + "communities": "65002:3", + } + ], + "2001:db8:4::1/128": [ + { + "communities": "65002:4", + } + ], + "2001:db8:5::1/128": [ + { + "communities": "65002:5", + } + ], + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't match routes using ipv6 next-hop access-list" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_route_map_match_source_protocol/__init__.py b/tests/topotests/bgp_route_map_match_source_protocol/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf new file mode 100644 index 0000000..7c3efee --- /dev/null +++ b/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf @@ -0,0 +1,32 @@ +! +interface lo + ip address 172.16.255.1/32 +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +interface r1-eth1 + ip address 192.168.2.1/24 +! +ip route 10.10.10.10/32 192.168.2.2 +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + address-family ipv4 + redistribute connected + redistribute static + neighbor 192.168.1.2 route-map r2 out + neighbor 192.168.2.2 route-map r3 out + exit-address-family +! +route-map r2 permit 10 + match source-protocol static +route-map r3 permit 10 + match source-protocol connected +! diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf new file mode 100644 index 0000000..7213975 --- /dev/null +++ b/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf @@ -0,0 +1,10 @@ +! +interface r2-eth0 + ip address 192.168.1.2/24 +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 +! diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf new file mode 100644 index 0000000..4a1d830 --- /dev/null +++ b/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf @@ -0,0 +1,10 @@ +! +interface r3-eth0 + ip address 192.168.2.2/24 +! +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 +! diff --git a/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py b/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py new file mode 100644 index 0000000..2828796 --- /dev/null +++ b/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if r1 can announce only static routes to r2, and only connected +routes to r3 using `match source-protocol` with route-maps. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_route_map_match_source_protocol(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_check_advertised_routes_r2(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.1.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "10.10.10.10/32": { + "valid": True, + } + }, + "totalPrefixCounter": 1, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_advertised_routes_r2) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to filter routes by source-protocol for r2" + + def _bgp_check_advertised_routes_r3(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.2.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "192.168.1.0/24": { + "valid": True, + }, + "192.168.2.0/24": { + "valid": True, + }, + "172.16.255.1/32": { + "valid": True, + }, + }, + "totalPrefixCounter": 3, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_advertised_routes_r3) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to filter routes by source-protocol for r3" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_route_map_on_match_next/__init__.py b/tests/topotests/bgp_route_map_on_match_next/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_route_map_on_match_next/r1/bgpd.conf b/tests/topotests/bgp_route_map_on_match_next/r1/bgpd.conf new file mode 100644 index 0000000..b858fff --- /dev/null +++ b/tests/topotests/bgp_route_map_on_match_next/r1/bgpd.conf @@ -0,0 +1,10 @@ +! +router bgp 65001 + no bgp network import-check + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + network 10.100.100.1/32 + exit-address-family + ! +! diff --git a/tests/topotests/bgp_route_map_on_match_next/r1/zebra.conf b/tests/topotests/bgp_route_map_on_match_next/r1/zebra.conf new file mode 100644 index 0000000..581e2e6 --- /dev/null +++ b/tests/topotests/bgp_route_map_on_match_next/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_route_map_on_match_next/r2/bgpd.conf b/tests/topotests/bgp_route_map_on_match_next/r2/bgpd.conf new file mode 100644 index 0000000..518d63d --- /dev/null +++ b/tests/topotests/bgp_route_map_on_match_next/r2/bgpd.conf @@ -0,0 +1,17 @@ +! +router bgp 65001 + neighbor 192.168.255.1 remote-as 65001 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 unicast + neighbor 192.168.255.1 route-map RM in + exit-address-family + ! +! +route-map RM permit 10 + set weight 100 +exit +! +route-map RM permit 20 + set metric 20 +exit +! diff --git a/tests/topotests/bgp_route_map_on_match_next/r2/zebra.conf b/tests/topotests/bgp_route_map_on_match_next/r2/zebra.conf new file mode 100644 index 0000000..fd45c48 --- /dev/null +++ b/tests/topotests/bgp_route_map_on_match_next/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_route_map_on_match_next/test_bgp_route_map_on_match_next.py b/tests/topotests/bgp_route_map_on_match_next/test_bgp_route_map_on_match_next.py new file mode 100644 index 0000000..8fe45a3 --- /dev/null +++ b/tests/topotests/bgp_route_map_on_match_next/test_bgp_route_map_on_match_next.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_route_map_on_match_next.py +# +# Copyright (c) 2023 Rubicon Communications, LLC. +# + +""" +Test whether `on-match next` added to an existing route-map entry takes effect. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_route_map_on_match_next(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = {"192.168.255.1": {"bgpState": "Established"}} + return topotest.json_cmp(output, expected) + + def _bgp_has_routes(router, metric, weight): + output = json.loads( + router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 routes json") + ) + expected = { + "routes": {"10.100.100.1/32": [{"metric": metric, "weight": weight}]} + } + return topotest.json_cmp(output, expected) + + # Check thst session is established + test_func = functools.partial(_bgp_converge, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed bgp convergence on r2" + + # Check that metric is 0 and weight is 100 for the received prefix + test_func = functools.partial(_bgp_has_routes, router2, 0, 100) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "r2 does not receive routes with metric 0 and weight 100" + + # Update the route-map and add "on-match next" to entry 10 + cmd = """ + configure terminal + route-map RM permit 10 + on-match next + exit + """ + router2.vtysh_cmd(cmd) + + # Check that metric is 20 and weight is 100 for the received prefix + test_func = functools.partial(_bgp_has_routes, router2, 20, 100) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "r2 does not receive routes with metric 20 and weight 100" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_route_map_vpn_import/__init__.py b/tests/topotests/bgp_route_map_vpn_import/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf b/tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf new file mode 100644 index 0000000..4aa11ec --- /dev/null +++ b/tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf @@ -0,0 +1,46 @@ +! +!debug bgp updates +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp nht +!debug route-map +! +router bgp 65001 + bgp router-id 10.10.10.10 + no bgp ebgp-requires-policy +! +router bgp 65001 vrf Customer + bgp router-id 192.168.1.2 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + rd vpn export 192.168.1.2:2 + rt vpn import 192.168.1.2:2 + rt vpn export 192.168.1.2:2 + export vpn + import vpn + exit-address-family +! +router bgp 65001 vrf Service + bgp router-id 192.168.2.2 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + rd vpn export 192.168.2.2:2 + rt vpn import 192.168.2.2:2 192.168.1.2:2 + rt vpn export 192.168.2.2:2 + route-map vpn import from-customer + export vpn + import vpn + exit-address-family +! +bgp extcommunity-list standard from-customer seq 5 permit rt 192.168.1.2:2 +! +ip prefix-list p1 seq 5 permit 192.0.2.0/24 +! +route-map from-customer permit 10 + match extcommunity from-customer + match ip address prefix-list p1 + set local-preference 123 +route-map from-customer permit 20 +! diff --git a/tests/topotests/bgp_route_map_vpn_import/r1/zebra.conf b/tests/topotests/bgp_route_map_vpn_import/r1/zebra.conf new file mode 100644 index 0000000..51966b2 --- /dev/null +++ b/tests/topotests/bgp_route_map_vpn_import/r1/zebra.conf @@ -0,0 +1,16 @@ +! +interface lo + ip address 10.10.10.10/32 +! +interface r1-eth0 vrf Customer + ip address 192.168.1.2/24 +! +interface r1-eth1 vrf Service + ip address 192.168.2.2/24 +! +interface r1-eth2 + ip address 10.0.1.1/24 +! +interface r1-eth3 vrf Customer + ip address 192.0.2.1/24 +! diff --git a/tests/topotests/bgp_route_map_vpn_import/test_bgp_route_map_vpn_import.py b/tests/topotests/bgp_route_map_vpn_import/test_bgp_route_map_vpn_import.py new file mode 100644 index 0000000..37082b4 --- /dev/null +++ b/tests/topotests/bgp_route_map_vpn_import/test_bgp_route_map_vpn_import.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Test if `route-map vpn import NAME` works by setting/matching via route-maps. +Routes from VRF Customer to VRF Service MUST be leaked and modified later +with `route-map vpn import`. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("r1") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + r1 = tgen.gears["r1"] + + r1.run("ip link add Customer type vrf table 1001") + r1.run("ip link set up dev Customer") + r1.run("ip link set r1-eth0 master Customer") + r1.run("ip link add Service type vrf table 1002") + r1.run("ip link set up dev Service") + r1.run("ip link set r1-eth1 master Service") + r1.run("ip link set r1-eth3 master Customer") + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_route_map_vpn_import(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_check_received_leaked_with_vpn_import(): + output = json.loads(r1.vtysh_cmd("show bgp vrf Service ipv4 unicast json")) + expected = { + "routes": { + "192.0.2.0/24": [ + { + "locPrf": 123, + }, + ], + "192.168.1.0/24": [ + { + "locPrf": None, + } + ], + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_received_leaked_with_vpn_import) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Failed, imported routes are not modified" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf b/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf new file mode 100644 index 0000000..1929dfa --- /dev/null +++ b/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf @@ -0,0 +1,2 @@ +router bgp 65001 + neighbor 192.168.2.1 remote-as external diff --git a/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py b/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py new file mode 100644 index 0000000..673efc2 --- /dev/null +++ b/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 03 2023, Trey Aspelund +# +# Copyright (C) 2023 NVIDIA Corporation +# +# Test if the CLI parser for RT/SoO ecoms correctly +# constrain user input to valid 4-byte ASN values. +# + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("pe1") + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + pe1 = tgen.gears["pe1"] + pe1.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "pe1/bgpd.conf")) + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_route_origin_parser(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["pe1"] + + def _invalid_soo_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + neighbor 192.168.2.1 soo 4294967296:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "soo" in run_cfg + + def _max_soo_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + neighbor 192.168.2.1 soo 4294967295:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "soo 4294967295:65" in run_cfg + + def _invalid_rt_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + rt vpn both 4294967296:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "rt vpn" in run_cfg + + def _max_rt_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + rt vpn both 4294967295:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "rt vpn both 4294967295:65" in run_cfg + + step( + "Configure invalid 4-byte value SoO (4294967296:65), this should not be accepted" + ) + test_func = functools.partial(_invalid_soo_accepted) + _, result = topotest.run_and_expect(test_func, False, count=30, wait=0.5) + assert result is False, "invalid 4-byte value of SoO accepted" + + step("Configure max 4-byte value SoO (4294967295:65), this should be accepted") + test_func = functools.partial(_max_soo_accepted) + _, result = topotest.run_and_expect(test_func, True, count=30, wait=0.5) + assert result is True, "max 4-byte value of SoO not accepted" + + step( + "Configure invalid 4-byte value RT (4294967296:65), this should not be accepted" + ) + test_func = functools.partial(_invalid_rt_accepted) + _, result = topotest.run_and_expect(test_func, False, count=30, wait=0.5) + assert result is False, "invalid 4-byte value of RT accepted" + + step("Configure max 4-byte value RT (4294967295:65), this should be accepted") + test_func = functools.partial(_max_rt_accepted) + _, result = topotest.run_and_expect(test_func, True, count=30, wait=0.5) + assert result is True, "max 4-byte value of RT not accepted" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_route_server_client/__init__.py b/tests/topotests/bgp_route_server_client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_route_server_client/r1/bgpd.conf b/tests/topotests/bgp_route_server_client/r1/bgpd.conf new file mode 100644 index 0000000..9826b67 --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r1/bgpd.conf @@ -0,0 +1,12 @@ +! +router bgp 65001 + bgp router-id 10.10.10.1 + no bgp ebgp-requires-policy + neighbor 2001:db8:1::1 remote-as external + neighbor 2001:db8:1::1 timers 3 10 + neighbor 2001:db8:1::1 timers connect 5 + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::1 activate + exit-address-family +! diff --git a/tests/topotests/bgp_route_server_client/r1/zebra.conf b/tests/topotests/bgp_route_server_client/r1/zebra.conf new file mode 100644 index 0000000..08d0be6 --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r1/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ipv6 address 2001:db8:f::1/128 +! +int r1-eth0 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/bgp_route_server_client/r2/bgpd.conf b/tests/topotests/bgp_route_server_client/r2/bgpd.conf new file mode 100644 index 0000000..3b0a24b --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r2/bgpd.conf @@ -0,0 +1,17 @@ +router bgp 65000 view RS + bgp router-id 10.10.10.2 + no bgp ebgp-requires-policy + neighbor 2001:db8:1::2 remote-as external + neighbor 2001:db8:1::2 timers 3 10 + neighbor 2001:db8:1::2 timers connect 5 + neighbor 2001:db8:3::2 remote-as external + neighbor 2001:db8:3::2 timers 3 10 + neighbor 2001:db8:3::2 timers connect 5 + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::2 activate + neighbor 2001:db8:3::2 activate + neighbor 2001:db8:1::2 route-server-client + neighbor 2001:db8:3::2 route-server-client + exit-address-family +! diff --git a/tests/topotests/bgp_route_server_client/r2/zebra.conf b/tests/topotests/bgp_route_server_client/r2/zebra.conf new file mode 100644 index 0000000..806bc4f --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r2/zebra.conf @@ -0,0 +1,7 @@ +! +int r2-eth0 + ipv6 address 2001:db8:1::1/64 +! +int r2-eth1 + ipv6 address 2001:db8:3::1/64 +! diff --git a/tests/topotests/bgp_route_server_client/r3/bgpd.conf b/tests/topotests/bgp_route_server_client/r3/bgpd.conf new file mode 100644 index 0000000..60a5ffc --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r3/bgpd.conf @@ -0,0 +1,12 @@ +! +router bgp 65003 + bgp router-id 10.10.10.3 + no bgp ebgp-requires-policy + neighbor 2001:db8:3::1 remote-as external + neighbor 2001:db8:3::1 timers 3 10 + neighbor 2001:db8:3::1 timers connect 5 + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:3::1 activate + exit-address-family +! diff --git a/tests/topotests/bgp_route_server_client/r3/zebra.conf b/tests/topotests/bgp_route_server_client/r3/zebra.conf new file mode 100644 index 0000000..b5511a4 --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r3/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ipv6 address 2001:db8:f::3/128 +! +int r3-eth0 + ipv6 address 2001:db8:3::2/64 +! diff --git a/tests/topotests/bgp_route_server_client/test_bgp_route_server_client.py b/tests/topotests/bgp_route_server_client/test_bgp_route_server_client.py new file mode 100644 index 0000000..23cf041 --- /dev/null +++ b/tests/topotests/bgp_route_server_client/test_bgp_route_server_client.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2021 by +# Donatas Abraitis +# + +""" +Test if we send ONLY GUA address for route-server-client peers. +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_route_server_client(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show bgp ipv6 unicast summary json")) + expected = {"peers": {"2001:db8:1::1": {"state": "Established", "pfxRcd": 2}}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, r1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see BGP sessions to be up" + + def _bgp_prefix_received(router): + output = json.loads(router.vtysh_cmd("show bgp 2001:db8:f::3/128 json")) + expected = { + "prefix": "2001:db8:f::3/128", + "paths": [{"nexthops": [{"ip": "2001:db8:3::2"}]}], + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_prefix_received, r1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see BGP GUA next hop from r3 in r1" + + def _bgp_single_next_hop(router): + output = json.loads(router.vtysh_cmd("show bgp 2001:db8:f::3/128 json")) + return len(output["paths"][0]["nexthops"]) + + assert ( + _bgp_single_next_hop(r1) == 1 + ), "Not ONLY one Next Hop received for 2001:db8:f::3/128" + + def _bgp_gua_lla_next_hop(router): + output = json.loads(router.vtysh_cmd("show bgp view RS 2001:db8:f::3/128 json")) + expected = { + "prefix": "2001:db8:f::3/128", + "paths": [ + { + "nexthops": [ + { + "ip": "2001:db8:3::2", + "hostname": "r3", + "afi": "ipv6", + "scope": "global", + }, + {"hostname": "r3", "afi": "ipv6", "scope": "link-local"}, + ] + } + ], + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_gua_lla_next_hop, r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see BGP LLA next hop from r3 in r2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_rr_ibgp/spine1/bgpd.conf b/tests/topotests/bgp_rr_ibgp/spine1/bgpd.conf new file mode 100644 index 0000000..cece3fc --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/spine1/bgpd.conf @@ -0,0 +1,11 @@ +hostname spine1 +router bgp 99 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as internal + neighbor 192.168.2.1 timers 3 10 + neighbor 192.168.4.2 remote-as internal + neighbor 192.168.4.2 timers 3 10 + address-family ipv4 uni + redistribute connected + neighbor 192.168.2.1 route-reflector-client + neighbor 192.168.4.2 route-reflector-client diff --git a/tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref b/tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref new file mode 100644 index 0000000..75ce1b1 --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref @@ -0,0 +1,144 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.2.1", + "afi":"ipv4", + "interfaceName":"spine1-eth0", + "active":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"spine1-eth0", + "active":true + } + ] + } + ], + "192.168.3.0\/24":[ + { + "prefix":"192.168.3.0\/24", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.4.2", + "afi":"ipv4", + "interfaceName":"spine1-eth1", + "active":true + } + ] + } + ], + "192.168.4.0\/24":[ + { + "prefix":"192.168.4.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"spine1-eth1", + "active":true + } + ] + } + ], + "192.168.5.1\/32":[ + { + "prefix":"192.168.5.1\/32", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.2.1", + "afi":"ipv4", + "interfaceName":"spine1-eth0", + "active":true + } + ] + } + ], + "192.168.6.2\/32":[ + { + "prefix":"192.168.6.2\/32", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.4.2", + "afi":"ipv4", + "interfaceName":"spine1-eth1", + "active":true + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/topotests/bgp_rr_ibgp/spine1/zebra.conf b/tests/topotests/bgp_rr_ibgp/spine1/zebra.conf new file mode 100644 index 0000000..ea25462 --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/spine1/zebra.conf @@ -0,0 +1,9 @@ +hostname spine1 +ip forwarding +ipv6 forwarding + +int spine1-eth0 + ip addr 192.168.2.3/24 + +int spine1-eth1 + ip addr 192.168.4.3/24 diff --git a/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py b/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py new file mode 100644 index 0000000..9984aba --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_rr_ibgp_topo1.py +# +# Copyright (c) 2019 by +# Cumulus Networks, Inc. +# Donald Sharp +# + +""" +test_bgp_rr_ibgp_topo1.py: Testing IBGP with RR and no IGP + +Ensure that a basic rr topology comes up and correctly passes +routes around + +""" + +import os +import sys +import pytest +import json + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + tgen.add_router("tor1") + tgen.add_router("tor2") + tgen.add_router("spine1") + + # First switch is for a dummy interface (for local network) + # on tor1 + # 192.168.1.0/24 + switch = tgen.add_switch("sw1") + switch.add_link(tgen.gears["tor1"]) + + # 192.168.2.0/24 - tor1 <-> spine1 connection + switch = tgen.add_switch("sw2") + switch.add_link(tgen.gears["tor1"]) + switch.add_link(tgen.gears["spine1"]) + + # 3rd switch is for a dummy interface (for local netwokr) + # 192.168.3.0/24 - tor2 + switch = tgen.add_switch("sw3") + switch.add_link(tgen.gears["tor2"]) + + # 192.168.4.0/24 - tor2 <-> spine1 connection + switch = tgen.add_switch("sw4") + switch.add_link(tgen.gears["tor2"]) + switch.add_link(tgen.gears["spine1"]) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + "Setup topology" + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + # tgen.mininet_cli() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_converge_protocols(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + topotest.sleep(5, "Waiting for BGP_RR_IBGP convergence") + + +def test_bgp_rr_ibgp_routes(): + "Test Route Reflection" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Verify BGP_RR_IBGP Status + logger.info("Verifying BGP_RR_IBGP routes") + + +def test_zebra_ipv4_routingTable(): + "Test 'show ip route'" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + failures = 0 + router_list = tgen.routers().values() + for router in router_list: + output = router.vtysh_cmd("show ip route json", isjson=True) + refTableFile = "{}/{}/show_ip_route.json_ref".format(CWD, router.name) + expected = json.loads(open(refTableFile).read()) + + assertmsg = "Zebra IPv4 Routing Table verification failed for router {}".format( + router.name + ) + assert topotest.json_cmp(output, expected) is None, assertmsg + + +def test_shutdown_check_stderr(): + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + pytest.skip("Skipping test for Stderr output and memory leaks") + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Verifying unexpected STDERR output from daemons") + + router_list = tgen.routers().values() + for router in router_list: + router.stop() + + log = tgen.net[router.name].getStdErr("bgpd") + if log: + logger.error("BGPd StdErr Log:" + log) + log = tgen.net[router.name].getStdErr("zebra") + if log: + logger.error("Zebra StdErr Log:" + log) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) + +# +# Auxiliary Functions +# diff --git a/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf b/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf new file mode 100644 index 0000000..1b9f150 --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf @@ -0,0 +1,6 @@ +hostname tor1 +router bgp 99 + no bgp ebgp-requires-policy + neighbor 192.168.2.3 remote-as internal + neighbor 192.168.2.3 timers 3 10 + redistribute connected diff --git a/tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref b/tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref new file mode 100644 index 0000000..6cfa024 --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref @@ -0,0 +1,157 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"tor1-eth0", + "active":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"tor1-eth1", + "active":true + } + ] + } + ], + "192.168.3.0\/24":[ + { + "prefix":"192.168.3.0\/24", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":5, + "ip":"192.168.4.2", + "afi":"ipv4", + "active":true, + "recursive":true + }, + { + "flags":3, + "fib":true, + "ip":"192.168.2.3", + "afi":"ipv4", + "interfaceName":"tor1-eth1", + "active":true + } + ] + } + ], + "192.168.4.0\/24":[ + { + "prefix":"192.168.4.0\/24", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.2.3", + "afi":"ipv4", + "interfaceName":"tor1-eth1", + "active":true + } + ] + } + ], + "192.168.5.1\/32":[ + { + "prefix":"192.168.5.1\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "192.168.6.2\/32":[ + { + "prefix":"192.168.6.2\/32", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":5, + "ip":"192.168.4.2", + "afi":"ipv4", + "active":true, + "recursive":true + }, + { + "flags":3, + "fib":true, + "ip":"192.168.2.3", + "afi":"ipv4", + "interfaceName":"tor1-eth1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_rr_ibgp/tor1/zebra.conf b/tests/topotests/bgp_rr_ibgp/tor1/zebra.conf new file mode 100644 index 0000000..25b4fcf --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/tor1/zebra.conf @@ -0,0 +1,12 @@ +hostname tor1 +ip forwarding +ipv6 forwarding + +int tor1-eth0 + ip addr 192.168.1.1/24 + +int tor1-eth1 + ip addr 192.168.2.1/24 + +int lo + ip addr 192.168.5.1/32 diff --git a/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf b/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf new file mode 100644 index 0000000..3bdb359 --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf @@ -0,0 +1,6 @@ +hostname tor2 +router bgp 99 + no bgp ebgp-requires-policy + neighbor 192.168.4.3 remote-as internal + neighbor 192.168.4.3 timers 3 10 + redistribute connected diff --git a/tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref b/tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref new file mode 100644 index 0000000..d9e9290 --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref @@ -0,0 +1,157 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":5, + "ip":"192.168.2.1", + "afi":"ipv4", + "active":true, + "recursive":true + }, + { + "flags":3, + "fib":true, + "ip":"192.168.4.3", + "afi":"ipv4", + "interfaceName":"tor2-eth1", + "active":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.4.3", + "afi":"ipv4", + "interfaceName":"tor2-eth1", + "active":true + } + ] + } + ], + "192.168.3.0\/24":[ + { + "prefix":"192.168.3.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"tor2-eth0", + "active":true + } + ] + } + ], + "192.168.4.0\/24":[ + { + "prefix":"192.168.4.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"tor2-eth1", + "active":true + } + ] + } + ], + "192.168.5.1\/32":[ + { + "prefix":"192.168.5.1\/32", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":5, + "ip":"192.168.2.1", + "afi":"ipv4", + "active":true, + "recursive":true + }, + { + "flags":3, + "fib":true, + "ip":"192.168.4.3", + "afi":"ipv4", + "interfaceName":"tor2-eth1", + "active":true + } + ] + } + ], + "192.168.6.2\/32":[ + { + "prefix":"192.168.6.2\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_rr_ibgp/tor2/zebra.conf b/tests/topotests/bgp_rr_ibgp/tor2/zebra.conf new file mode 100644 index 0000000..e1a06b1 --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/tor2/zebra.conf @@ -0,0 +1,13 @@ +hostname tor2 +ip forwarding +ipv6 forwarding + +int tor2-eth0 + ip addr 192.168.3.2/24 + +int tor2-eth1 + ip addr 192.168.4.2/24 + + +int lo + ip addr 192.168.6.2/32 diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/__init__.py b/tests/topotests/bgp_sender_as_path_loop_detection/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r1/bgpd.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r1/bgpd.conf new file mode 100644 index 0000000..409be74 --- /dev/null +++ b/tests/topotests/bgp_sender_as_path_loop_detection/r1/bgpd.conf @@ -0,0 +1,19 @@ +! exit1 +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65002 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 unicast + neighbor 192.168.255.1 route-map prepend out + redistribute connected + exit-address-family + ! +! +ip prefix-list p1 seq 5 permit 172.16.255.253/32 +! +route-map prepend permit 10 + match ip address prefix-list p1 + set as-path prepend 65003 +! +route-map prepend permit 20 +! diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf new file mode 100644 index 0000000..74489a0 --- /dev/null +++ b/tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf @@ -0,0 +1,10 @@ +! exit1 +interface lo + ip address 172.16.255.253/32 + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf new file mode 100644 index 0000000..dcb52a2 --- /dev/null +++ b/tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf @@ -0,0 +1,10 @@ +! spine +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 sender-as-path-loop-detection + neighbor 192.168.254.2 remote-as 65003 + neighbor 192.168.254.2 timers 3 10 + neighbor 192.168.254.2 sender-as-path-loop-detection +! diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r2/zebra.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r2/zebra.conf new file mode 100644 index 0000000..f0d357c --- /dev/null +++ b/tests/topotests/bgp_sender_as_path_loop_detection/r2/zebra.conf @@ -0,0 +1,9 @@ +! spine +interface r2-eth0 + ip address 192.168.255.1/30 +! +interface r2-eth1 + ip address 192.168.254.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r3/bgpd.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r3/bgpd.conf new file mode 100644 index 0000000..519273d --- /dev/null +++ b/tests/topotests/bgp_sender_as_path_loop_detection/r3/bgpd.conf @@ -0,0 +1,6 @@ +! exit2 +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.254.1 remote-as 65002 + neighbor 192.168.254.1 timers 3 10 +! diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r3/zebra.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r3/zebra.conf new file mode 100644 index 0000000..a10fe3a --- /dev/null +++ b/tests/topotests/bgp_sender_as_path_loop_detection/r3/zebra.conf @@ -0,0 +1,6 @@ +! exit2 +interface r3-eth0 + ip address 192.168.254.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py b/tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py new file mode 100644 index 0000000..3886bc1 --- /dev/null +++ b/tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_sender-as-path-loop-detection.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Donatas Abraitis +# + +""" +Test if neighbor sender-as-path-loop-detection +command works as expeced. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_sender_as_path_loop_detection(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 3}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_has_route_from_r1(): + output = json.loads(r2.vtysh_cmd("show ip bgp 172.16.255.253/32 json")) + expected = { + "paths": [ + { + "aspath": { + "segments": [{"type": "as-sequence", "list": [65001, 65003]}], + "length": 2, + } + } + ] + } + return topotest.json_cmp(output, expected) + + def _bgp_suppress_route_to_r1(): + output = json.loads( + r2.vtysh_cmd("show ip bgp neighbor 192.168.255.2 advertised-routes json") + ) + expected = {"totalPrefixCounter": 0} + return topotest.json_cmp(output, expected) + + def _bgp_suppress_route_to_r3(): + output = json.loads( + r2.vtysh_cmd("show ip bgp neighbor 192.168.254.2 advertised-routes json") + ) + expected = {"totalPrefixCounter": 2} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed bgp to convergence" + + test_func = functools.partial(_bgp_has_route_from_r1) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed to see a route from r1" + + test_func = functools.partial(_bgp_suppress_route_to_r3) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Route 172.16.255.253/32 should not be sent to r3 from r2" + + test_func = functools.partial(_bgp_suppress_route_to_r1) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Routes should not be sent to r1 from r2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_set_aspath_exclude/__init__.py b/tests/topotests/bgp_set_aspath_exclude/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf b/tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf new file mode 100644 index 0000000..9bef24f --- /dev/null +++ b/tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf @@ -0,0 +1,17 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 3 10 + address-family ipv4 unicast + neighbor 192.168.1.2 route-map r2 in + exit-address-family +! +ip prefix-list p1 seq 5 permit 172.16.255.31/32 +! +route-map r2 permit 10 + match ip address prefix-list p1 + set as-path exclude 65003 +route-map r2 permit 20 + set as-path exclude all +! diff --git a/tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf b/tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf new file mode 100644 index 0000000..acf120b --- /dev/null +++ b/tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_set_aspath_exclude/r2/bgpd.conf b/tests/topotests/bgp_set_aspath_exclude/r2/bgpd.conf new file mode 100644 index 0000000..23367f9 --- /dev/null +++ b/tests/topotests/bgp_set_aspath_exclude/r2/bgpd.conf @@ -0,0 +1,8 @@ +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 3 10 + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 3 10 +! diff --git a/tests/topotests/bgp_set_aspath_exclude/r2/zebra.conf b/tests/topotests/bgp_set_aspath_exclude/r2/zebra.conf new file mode 100644 index 0000000..f229954 --- /dev/null +++ b/tests/topotests/bgp_set_aspath_exclude/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface r2-eth0 + ip address 192.168.1.2/24 +! +interface r2-eth1 + ip address 192.168.2.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_set_aspath_exclude/r3/bgpd.conf b/tests/topotests/bgp_set_aspath_exclude/r3/bgpd.conf new file mode 100644 index 0000000..b7a7ced --- /dev/null +++ b/tests/topotests/bgp_set_aspath_exclude/r3/bgpd.conf @@ -0,0 +1,9 @@ +! +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_set_aspath_exclude/r3/zebra.conf b/tests/topotests/bgp_set_aspath_exclude/r3/zebra.conf new file mode 100644 index 0000000..3fa6c64 --- /dev/null +++ b/tests/topotests/bgp_set_aspath_exclude/r3/zebra.conf @@ -0,0 +1,10 @@ +! +int lo + ip address 172.16.255.31/32 + ip address 172.16.255.32/32 +! +interface r3-eth0 + ip address 192.168.2.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py b/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py new file mode 100644 index 0000000..a0cd89f --- /dev/null +++ b/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# test_bgp_set_aspath_exclude.py +# +# Copyright 2023 by 6WIND S.A. +# + +""" +Test if `set as-path exclude` is working correctly for route-maps. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_set_aspath_exclude(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "172.16.255.31/32": [{"path": "65002"}], + "172.16.255.32/32": [{"path": ""}], + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, tgen.gears["r1"]) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, "Failed overriding incoming AS-PATH with route-map" + + +def test_bgp_set_aspath_exclude_access_list(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + rname = "r1" + r1 = tgen.gears[rname] + + r1.vtysh_cmd( + """ +conf + bgp as-path access-list FIRST permit ^65 + route-map r2 permit 6 + set as-path exclude as-path-access-list FIRST + """ + ) + + expected = { + "routes": { + "172.16.255.31/32": [{"path": ""}], + "172.16.255.32/32": [{"path": ""}], + } + } + + def _bgp_regexp_1(router): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_regexp_1, tgen.gears["r1"]) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, "Failed overriding incoming AS-PATH with regex 1 route-map" + r1.vtysh_cmd( + """ +conf + bgp as-path access-list SECOND permit 2 + route-map r2 permit 6 + set as-path exclude as-path-access-list SECOND + """ + ) + + expected = { + "routes": { + "172.16.255.31/32": [{"path": "65003"}], + "172.16.255.32/32": [{"path": "65003"}], + } + } + + test_func = functools.partial(_bgp_regexp_1, tgen.gears["r1"]) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, "Failed overriding incoming AS-PATH with regex 2 route-map" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_set_aspath_replace/__init__.py b/tests/topotests/bgp_set_aspath_replace/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_set_aspath_replace/r1/bgpd.conf b/tests/topotests/bgp_set_aspath_replace/r1/bgpd.conf new file mode 100644 index 0000000..f586c1f --- /dev/null +++ b/tests/topotests/bgp_set_aspath_replace/r1/bgpd.conf @@ -0,0 +1,18 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 3 10 + address-family ipv4 unicast + neighbor 192.168.1.2 route-map r2 in + exit-address-family +! +ip prefix-list p1 seq 5 permit 172.16.255.31/32 +! +bgp route-map delay-timer 1 +route-map r2 permit 10 + match ip address prefix-list p1 + set as-path replace 65003 +route-map r2 permit 20 + set as-path replace any +! diff --git a/tests/topotests/bgp_set_aspath_replace/r1/zebra.conf b/tests/topotests/bgp_set_aspath_replace/r1/zebra.conf new file mode 100644 index 0000000..acf120b --- /dev/null +++ b/tests/topotests/bgp_set_aspath_replace/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_set_aspath_replace/r2/bgpd.conf b/tests/topotests/bgp_set_aspath_replace/r2/bgpd.conf new file mode 100644 index 0000000..23367f9 --- /dev/null +++ b/tests/topotests/bgp_set_aspath_replace/r2/bgpd.conf @@ -0,0 +1,8 @@ +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 3 10 + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 3 10 +! diff --git a/tests/topotests/bgp_set_aspath_replace/r2/zebra.conf b/tests/topotests/bgp_set_aspath_replace/r2/zebra.conf new file mode 100644 index 0000000..f229954 --- /dev/null +++ b/tests/topotests/bgp_set_aspath_replace/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface r2-eth0 + ip address 192.168.1.2/24 +! +interface r2-eth1 + ip address 192.168.2.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_set_aspath_replace/r3/bgpd.conf b/tests/topotests/bgp_set_aspath_replace/r3/bgpd.conf new file mode 100644 index 0000000..b7a7ced --- /dev/null +++ b/tests/topotests/bgp_set_aspath_replace/r3/bgpd.conf @@ -0,0 +1,9 @@ +! +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_set_aspath_replace/r3/zebra.conf b/tests/topotests/bgp_set_aspath_replace/r3/zebra.conf new file mode 100644 index 0000000..3fa6c64 --- /dev/null +++ b/tests/topotests/bgp_set_aspath_replace/r3/zebra.conf @@ -0,0 +1,10 @@ +! +int lo + ip address 172.16.255.31/32 + ip address 172.16.255.32/32 +! +interface r3-eth0 + ip address 192.168.2.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_set_aspath_replace/test_bgp_set_aspath_replace.py b/tests/topotests/bgp_set_aspath_replace/test_bgp_set_aspath_replace.py new file mode 100644 index 0000000..c0e19fa --- /dev/null +++ b/tests/topotests/bgp_set_aspath_replace/test_bgp_set_aspath_replace.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_set_aspath_replace.py +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Test if `set as-path replace` is working correctly for route-maps. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_set_aspath_replace_test1(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "172.16.255.31/32": [{"path": "65002 65001"}], + "172.16.255.32/32": [{"path": "65001 65001"}], + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, tgen.gears["r1"]) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, "Failed overriding incoming AS-PATH with route-map" + + +def test_bgp_set_aspath_replace_test2(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("Configuring r1 to replace the matching AS with a configured ASN") + router = tgen.gears["r1"] + router.vtysh_cmd( + "configure terminal\nroute-map r2 permit 10\nset as-path replace 65003 65500\n", + isjson=False, + ) + router.vtysh_cmd( + "configure terminal\nroute-map r2 permit 20\nset as-path replace any 65501\n", + isjson=False, + ) + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "172.16.255.31/32": [{"path": "65002 65500"}], + "172.16.255.32/32": [{"path": "65501 65501"}], + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert ( + result is None + ), "Failed overriding incoming AS-PATH with route-map replace with configured ASN" + + +def test_bgp_set_aspath_replace_access_list(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + rname = "r1" + r1 = tgen.gears[rname] + + r1.vtysh_cmd( + """ +conf + bgp as-path access-list FIRST permit ^65 + route-map r2 permit 20 + set as-path replace as-path-access-list FIRST 65002 + """ + ) + + expected = { + "routes": { + "172.16.255.31/32": [{"path": "65002 65500"}], + "172.16.255.32/32": [{"path": "65002 65002"}], + } + } + + def _bgp_regexp_1(router): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_regexp_1, tgen.gears["r1"]) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, "Failed overriding incoming AS-PATH with regex 1 route-map" + r1.vtysh_cmd( + """ +conf + bgp as-path access-list SECOND permit 2 + route-map r2 permit 10 + set as-path replace as-path-access-list SECOND 65001 + """ + ) + + expected = { + "routes": { + "172.16.255.31/32": [{"path": "65001 65003"}], + "172.16.255.32/32": [{"path": "65002 65002"}], + } + } + + test_func = functools.partial(_bgp_regexp_1, tgen.gears["r1"]) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, "Failed overriding incoming AS-PATH with regex 2 route-map" + + r1.vtysh_cmd( + """ +conf + bgp as-path access-list TER permit 3 + route-map r2 permit 10 + set as-path replace as-path-access-list TER + """ + ) + expected = { + "routes": { + "172.16.255.31/32": [{"path": "65002 65001"}], + "172.16.255.32/32": [{"path": "65002 65002"}], + } + } + + test_func = functools.partial(_bgp_regexp_1, tgen.gears["r1"]) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, "Failed overriding incoming AS-PATH with regex 3 route-map" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/__init__.py b/tests/topotests/bgp_set_local_preference_add_subtract/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/r1/bgpd.conf b/tests/topotests/bgp_set_local_preference_add_subtract/r1/bgpd.conf new file mode 100644 index 0000000..57e2f58 --- /dev/null +++ b/tests/topotests/bgp_set_local_preference_add_subtract/r1/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65000 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.3 remote-as 65000 + neighbor 192.168.255.3 timers 3 10 + exit-address-family +! diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/r1/zebra.conf b/tests/topotests/bgp_set_local_preference_add_subtract/r1/zebra.conf new file mode 100644 index 0000000..6e9b0b4 --- /dev/null +++ b/tests/topotests/bgp_set_local_preference_add_subtract/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/r2/bgpd.conf b/tests/topotests/bgp_set_local_preference_add_subtract/r2/bgpd.conf new file mode 100644 index 0000000..1f85a35 --- /dev/null +++ b/tests/topotests/bgp_set_local_preference_add_subtract/r2/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 65000 + no bgp ebgp-requires-policy + no bgp network import-check + network 10.10.10.2/32 route-map l2 + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 + redistribute connected + neighbor 192.168.255.1 route-map r1-out out + exit-address-family +! +route-map r1-out permit 10 + set local-preference +50 +route-map l2 permit 10 + set local-preference +10 diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/r2/zebra.conf b/tests/topotests/bgp_set_local_preference_add_subtract/r2/zebra.conf new file mode 100644 index 0000000..93e3590 --- /dev/null +++ b/tests/topotests/bgp_set_local_preference_add_subtract/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/r3/bgpd.conf b/tests/topotests/bgp_set_local_preference_add_subtract/r3/bgpd.conf new file mode 100644 index 0000000..49b8f1c --- /dev/null +++ b/tests/topotests/bgp_set_local_preference_add_subtract/r3/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 65000 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + network 10.10.10.3/32 route-map l3 + address-family ipv4 + redistribute connected + neighbor 192.168.255.1 route-map r1-out out + exit-address-family +! +route-map r1-out permit 10 + set local-preference -50 +route-map l3 permit 10 + set local-preference -10 diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/r3/zebra.conf b/tests/topotests/bgp_set_local_preference_add_subtract/r3/zebra.conf new file mode 100644 index 0000000..b5e060c --- /dev/null +++ b/tests/topotests/bgp_set_local_preference_add_subtract/r3/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r3-eth0 + ip address 192.168.255.3/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/test_bgp_set_local-preference_add_subtract.py b/tests/topotests/bgp_set_local_preference_add_subtract/test_bgp_set_local-preference_add_subtract.py new file mode 100644 index 0000000..292cf70 --- /dev/null +++ b/tests/topotests/bgp_set_local_preference_add_subtract/test_bgp_set_local-preference_add_subtract.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# bgp_set_local-preference_add_subtract.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Donatas Abraitis +# + +""" +bgp_set_local-preference_add_subtract.py: +Test if we can add/subtract the value to/from an existing +LOCAL_PREF in route-maps. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_set_local_preference(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 3}}, + }, + "192.168.255.3": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 3}}, + }, + } + return topotest.json_cmp(output, expected) + + def _bgp_check_local_preference(router): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "10.10.10.2/32": [{"locPrf": 160}], + "10.10.10.3/32": [{"locPrf": 40}], + "172.16.255.254/32": [ + {"locPrf": 50, "nexthops": [{"ip": "192.168.255.3"}]}, + {"locPrf": 150, "nexthops": [{"ip": "192.168.255.2"}]}, + ], + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + + assert result is None, 'Failed to see BGP convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_check_local_preference, router) + success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + + assert result is None, 'Failed to see applied BGP local-preference in "{}"'.format( + router + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf new file mode 100644 index 0000000..d82a21e --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf @@ -0,0 +1,31 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + no bgp default ipv4-unicast + neighbor 192.168.12.2 remote-as external + neighbor 192.168.12.2 timers 1 3 + neighbor 192.168.12.2 timers connect 1 + neighbor 2001:db8::12:2 remote-as external + neighbor 2001:db8::12:2 timers 1 3 + neighbor 2001:db8::12:2 timers connect 1 + ! + address-family ipv4 unicast + network 10.0.0.0/31 route-map p1 + network 10.0.0.2/32 route-map p2 + neighbor 192.168.12.2 activate + exit-address-family + address-family ipv6 unicast + network 2001:db8::1/128 route-map p1 + network 2001:db8:1::/56 route-map p2 + neighbor 2001:db8::12:2 activate + exit-address-family +! +route-map p1 permit 10 + set metric 1 +exit +route-map p2 permit 10 + set metric 2 + set origin incomplete +exit +! diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r1/zebra.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r1/zebra.conf new file mode 100644 index 0000000..0807e89 --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r1/zebra.conf @@ -0,0 +1,5 @@ +! +interface r1-eth0 + ip address 192.168.12.1/24 + ipv6 address 2001:db8::12:1/64 +! diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf new file mode 100644 index 0000000..cf0013e --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf @@ -0,0 +1,23 @@ +! +!debug bgp updates +! +router bgp 65002 + no bgp ebgp-requires-policy + no bgp network import-check + no bgp default ipv4-unicast + neighbor 192.168.12.1 remote-as external + neighbor 192.168.12.1 timers 1 3 + neighbor 192.168.12.1 timers connect 1 + neighbor 2001:db8::12:1 remote-as external + neighbor 2001:db8::12:1 timers 1 3 + neighbor 2001:db8::12:1 timers connect 1 + ! + address-family ipv4 unicast + neighbor 192.168.12.1 activate + exit-address-family + address-family ipv6 unicast + neighbor 2001:db8::12:1 activate + exit-address-family +! +agentx +! diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf new file mode 100644 index 0000000..032b93b --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf @@ -0,0 +1,17 @@ +agentAddress 127.0.0.1,[::1] + +group public_group v1 public +group public_group v2c public +access public_group "" any noauth prefix all all none + +rocommunity public default + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx + +agentXSocket /etc/frr/agentx +agentXPerms 777 755 root frr diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r2/zebra.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r2/zebra.conf new file mode 100644 index 0000000..db6d700 --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r2/zebra.conf @@ -0,0 +1,5 @@ +! +interface r2-eth0 + ip address 192.168.12.2/24 + ipv6 address 2001:db8::12:2/64 +! diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py b/tests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py new file mode 100755 index 0000000..a253d84 --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 Donatas Abraitis +# + +""" +Test some of the BGP4V2-MIB entries. +""" + +import os +import sys +import json +from time import sleep +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.snmptest import SnmpTester +from lib import topotest + +pytestmark = [pytest.mark.bgpd, pytest.mark.snmp] + + +def build_topo(tgen): + tgen.add_router("r1") + tgen.add_router("r2") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + snmpd = os.system("which snmpd") + if snmpd: + error_msg = "SNMP not installed - skipping" + pytest.skip(error_msg) + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + for rname, router in tgen.routers().items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, "{}/bgpd.conf".format(rname)), + "-M snmp", + ) + router.load_config( + TopoRouter.RD_SNMP, + os.path.join(CWD, "{}/snmpd.conf".format(rname)), + "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX", + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_snmp_bgp4v2(): + tgen = get_topogen() + + r2 = tgen.gears["r2"] + + def _bgp_converge_summary(): + output = json.loads(r2.vtysh_cmd("show bgp summary json")) + expected = { + "ipv4Unicast": { + "peers": { + "192.168.12.1": { + "state": "Established", + "pfxRcd": 2, + } + } + }, + "ipv6Unicast": { + "peers": { + "2001:db8::12:1": { + "state": "Established", + "pfxRcd": 2, + } + } + }, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge_summary) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't see connections established" + + def _bgp_converge_prefixes(): + output = json.loads(r2.vtysh_cmd("show bgp all json")) + expected = { + "ipv4Unicast": { + "routes": { + "10.0.0.0/31": [ + { + "metric": 1, + "origin": "IGP", + } + ], + "10.0.0.2/32": [ + { + "metric": 2, + "origin": "incomplete", + } + ], + } + }, + "ipv6Unicast": { + "routes": { + "2001:db8::1/128": [ + { + "metric": 1, + "origin": "IGP", + } + ], + "2001:db8:1::/56": [ + { + "metric": 2, + "origin": "incomplete", + } + ], + } + }, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge_prefixes) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't see prefixes from R1" + + snmp = SnmpTester(r2, "localhost", "public", "2c", "-Ln -On") + + def _snmpwalk_remote_addr(): + expected = { + "1.3.6.1.3.5.1.1.2.1.5.1.1.192.168.12.1": "C0 A8 0C 01", + "1.3.6.1.3.5.1.1.2.1.5.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1": "20 01 0D B8 00 00 00 00 00 00 00 00 00 12 00 01", + } + + # bgp4V2PeerRemoteAddr + output, _ = snmp.walk(".1.3.6.1.3.5.1.1.2.1.5") + return output == expected + + _, result = topotest.run_and_expect(_snmpwalk_remote_addr, True, count=10, wait=1) + assertmsg = "Can't fetch SNMP for bgp4V2PeerRemoteAddr" + assert result, assertmsg + + def _snmpwalk_peer_state(): + expected = { + "1.3.6.1.3.5.1.1.2.1.13.1.1.192.168.12.1": "6", + "1.3.6.1.3.5.1.1.2.1.13.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1": "6", + } + + # bgp4V2PeerState + output, _ = snmp.walk(".1.3.6.1.3.5.1.1.2.1.13") + return output == expected + + _, result = topotest.run_and_expect(_snmpwalk_peer_state, True, count=10, wait=1) + assertmsg = "Can't fetch SNMP for bgp4V2PeerState" + assert result, assertmsg + + def _snmpwalk_peer_last_error_code_received(): + expected = { + "1.3.6.1.3.5.1.1.3.1.1.1.1.192.168.12.1": "0", + "1.3.6.1.3.5.1.1.3.1.1.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1": "0", + } + + # bgp4V2PeerLastErrorCodeReceived + output, _ = snmp.walk(".1.3.6.1.3.5.1.1.3.1.1") + return output == expected + + _, result = topotest.run_and_expect( + _snmpwalk_peer_last_error_code_received, True, count=10, wait=1 + ) + assertmsg = "Can't fetch SNMP for bgp4V2PeerLastErrorCodeReceived" + assert result, assertmsg + + def _snmpwalk_origin(): + expected = { + "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.0.31.1.192.168.12.1.1": "1", + "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.2.32.1.192.168.12.1.1": "3", + "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "1", + "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "3", + } + + # bgp4V2NlriOrigin + output, _ = snmp.walk(".1.3.6.1.3.5.1.1.9.1.9") + return output == expected + + _, result = topotest.run_and_expect(_snmpwalk_origin, True, count=10, wait=1) + assertmsg = "Can't fetch SNMP for bgp4V2NlriOrigin" + assert result, assertmsg + + def _snmpwalk_med(): + expected = { + "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.0.31.1.192.168.12.1.1": "1", + "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.2.32.1.192.168.12.1.1": "2", + "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "1", + "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "2", + } + + # bgp4V2NlriMed + output, _ = snmp.walk(".1.3.6.1.3.5.1.1.9.1.17") + # tgen.mininet_cli() + return output == expected + + _, result = topotest.run_and_expect(_snmpwalk_med, True, count=10, wait=1) + assertmsg = "Can't fetch SNMP for bgp4V2NlriMed" + assert result, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce1/bgpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce1/bgpd.conf new file mode 100644 index 0000000..b598666 --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce1/bgpd.conf @@ -0,0 +1,12 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65001 + timers bgp 3 9 + bgp router-id 192.168.100.10 + neighbor 192.168.100.20 remote-as 65001 + neighbor 192.168.100.20 update-source 192.168.100.10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce1/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce1/snmpd.conf new file mode 100644 index 0000000..c8d0bab --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce1/snmpd.conf @@ -0,0 +1,18 @@ +agentAddress udp:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx + +agentXSocket /etc/frr/agentx +agentXPerms 777 755 root frr diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce1/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce1/zebra.conf new file mode 100644 index 0000000..4a85798 --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce1/zebra.conf @@ -0,0 +1,19 @@ +log file /tmp/zebra.log +log stdout +! +! debug zebra events +! debug zebra dplane +! +! +interface ce1-eth0 + ip address 192.168.100.10/24 + ipv6 address 2000:1:1:100::10/64 +! +! +interface lo + ip address 10.5.5.5/32 + ipv6 address 2000:5:5:5::5/128 +! +! +! +line vty diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce2/bgpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce2/bgpd.conf new file mode 100644 index 0000000..e388ccb --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce2/bgpd.conf @@ -0,0 +1,12 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65001 + bgp router-id 192.168.200.10 + timers bgp 3 9 + neighbor 192.168.200.20 remote-as 65001 + neighbor 192.168.200.20 update-source 192.168.200.10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce2/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce2/snmpd.conf new file mode 100644 index 0000000..c8d0bab --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce2/snmpd.conf @@ -0,0 +1,18 @@ +agentAddress udp:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx + +agentXSocket /etc/frr/agentx +agentXPerms 777 755 root frr diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce2/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce2/zebra.conf new file mode 100644 index 0000000..5e0aa5d --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce2/zebra.conf @@ -0,0 +1,19 @@ +log file /tmp/zebra.log +log stdout +! +! debug zebra events +! debug zebra dplane +! +! +interface ce2-eth0 + ip address 192.168.200.10/24 + ipv6 address 2000:1:1:200::10/64 +! +! +interface lo + ip address 10.6.6.6/32 + ipv6 address 2000:6:6:6::6/128 +! +! +! +line vty diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce3/bgpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce3/bgpd.conf new file mode 100644 index 0000000..e388ccb --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce3/bgpd.conf @@ -0,0 +1,12 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65001 + bgp router-id 192.168.200.10 + timers bgp 3 9 + neighbor 192.168.200.20 remote-as 65001 + neighbor 192.168.200.20 update-source 192.168.200.10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce3/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce3/snmpd.conf new file mode 100644 index 0000000..c8d0bab --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce3/snmpd.conf @@ -0,0 +1,18 @@ +agentAddress udp:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx + +agentXSocket /etc/frr/agentx +agentXPerms 777 755 root frr diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce3/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce3/zebra.conf new file mode 100644 index 0000000..fabc11e --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce3/zebra.conf @@ -0,0 +1,19 @@ +log file /tmp/zebra.log +log stdout +! +! debug zebra events +! debug zebra dplane +! +! +interface ce3-eth0 + ip address 192.168.200.10/24 + ipv6 address 2000:1:1:200::10/64 +! +! +interface lo + ip address 10.7.7.7/32 + ipv6 address 2000:7:7:7::7/128 +! +! +! +line vty diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce4/bgpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce4/bgpd.conf new file mode 100644 index 0000000..e388ccb --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce4/bgpd.conf @@ -0,0 +1,12 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65001 + bgp router-id 192.168.200.10 + timers bgp 3 9 + neighbor 192.168.200.20 remote-as 65001 + neighbor 192.168.200.20 update-source 192.168.200.10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce4/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce4/snmpd.conf new file mode 100644 index 0000000..c8d0bab --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce4/snmpd.conf @@ -0,0 +1,18 @@ +agentAddress udp:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx + +agentXSocket /etc/frr/agentx +agentXPerms 777 755 root frr diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce4/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce4/zebra.conf new file mode 100644 index 0000000..e369f41 --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce4/zebra.conf @@ -0,0 +1,19 @@ +log file /tmp/zebra.log +log stdout +! +! debug zebra events +! debug zebra dplane +! +! +interface ce4-eth0 + ip address 192.168.34.10/24 + ipv6 address 2000:1:1:300::10/64 +! +! +interface lo + ip address 10.8.8.8/32 + ipv6 address 2000:8:8:8::8/128 +! +! +! +line vty diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r1/bgpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r1/bgpd.conf new file mode 100644 index 0000000..098e55d --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/r1/bgpd.conf @@ -0,0 +1,48 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65000 + bgp router-id 10.1.1.1 + neighbor 10.4.4.4 remote-as 65000 + neighbor 10.4.4.4 update-source 10.1.1.1 + neighbor 10.4.4.4 timers connect 10 + ! + address-family ipv4 vpn + neighbor 10.4.4.4 activate + exit-address-family + +! +router bgp 65001 vrf VRF-a + bgp router-id 192.168.100.20 + timers bgp 3 9 + neighbor 192.168.100.10 remote-as 65001 + neighbor 192.168.100.10 update-source 192.168.100.20 + neighbor 192.168.200.10 remote-as 65001 + neighbor 192.168.200.10 update-source 192.168.200.20 + ! + address-family ipv4 unicast + redistribute connected + redistribute isis + label vpn export 1111 + rd vpn export 10:1 + rt vpn both 1:1 + export vpn + import vpn + exit-address-family + +router bgp 65002 vrf VRF-b + bgp router-id 192.168.10.20 + timers bgp 3 9 + neighbor 192.168.10.10 remote-as 65003 + neighbor 192.168.10.10 update-source 192.168.10.20 +! + address-family ipv4 unicast + redistribute connected + redistribute isis + label vpn export 6666 + rd vpn export 10:2 + rt vpn both 1:2 + export vpn + import vpn + exit-address-family + +agentx diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r1/isisd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r1/isisd.conf new file mode 100644 index 0000000..435abde --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/r1/isisd.conf @@ -0,0 +1,46 @@ +log stdout debugging +! +! debug isis route-events +! debug isis events +! +interface r1-eth0 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 10 + isis network point-to-point +! +interface r1-eth1 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 10 + isis network point-to-point +! +interface r1-eth2 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 10 + isis network point-to-point +! +interface lo + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + isis passive + no isis hello padding +! +router isis ISIS1 + net 01.1111.0000.0000.0001.00 + is-type level-1 + topology ipv6-unicast +! +line vty +! diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r1/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r1/snmpd.conf new file mode 100644 index 0000000..d7886e5 --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/r1/snmpd.conf @@ -0,0 +1,20 @@ +agentAddress udp:161 + +com2sec public 10.1.1.1 public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx + +noRangeCheck yes + +agentXSocket /etc/frr/agentx +agentXPerms 777 755 root frr diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r1/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r1/zebra.conf new file mode 100644 index 0000000..7228ae6 --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/r1/zebra.conf @@ -0,0 +1,33 @@ +log file zebra.log +! +interface r1-eth0 + ip address 192.168.12.12/24 + ipv6 address 2000:1:1:12::12/64 +! +interface r1-eth1 + ip address 192.168.13.13/24 + ipv6 address 2000:1:1:13::13/64 +! +interface r1-eth2 + ip address 192.168.14.14/24 + ipv6 address 2000:1:1:14::14/64 +! +interface r1-eth3 vrf VRF-a + ip address 192.168.100.20/24 + ipv6 address 2000:1:1:100::20/64 +! +interface r1-eth4 vrf VRF-a + ip address 192.168.200.20/24 + ipv6 address 2000:1:1:200::20/64 +! +interface r1-eth5 vrf VRF-b + ip address 192.168.300.20/24 + ipv6 address 2000:1:1:300::20/64 + +interface lo + ip address 10.1.1.1/32 + ipv6 address 2000:1:1:1::1/128 +! +! +! +line vty diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r2/isisd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r2/isisd.conf new file mode 100644 index 0000000..be5e7f5 --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/r2/isisd.conf @@ -0,0 +1,37 @@ +log stdout debugging +! +! debug isis route-events +! debug isis events +! +interface r2-eth0 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 10 + isis network point-to-point +! +interface r2-eth1 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 10 + isis network point-to-point +! +interface lo + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + isis passive + no isis hello padding +! +router isis ISIS1 + net 01.1111.0000.0000.0002.00 + is-type level-1 + topology ipv6-unicast +! +line vty +! diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r2/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r2/snmpd.conf new file mode 100644 index 0000000..c8d0bab --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/r2/snmpd.conf @@ -0,0 +1,18 @@ +agentAddress udp:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx + +agentXSocket /etc/frr/agentx +agentXPerms 777 755 root frr diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r2/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r2/zebra.conf new file mode 100644 index 0000000..4fec8af --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/r2/zebra.conf @@ -0,0 +1,24 @@ +log file /tmp/zebra.log +log stdout +! +! debug zebra events +! debug zebra dplane +! +! +interface r2-eth0 + ip address 192.168.12.21/24 + ipv6 address 2000:1:1:12::21/64 +! +interface r2-eth1 + ip address 192.168.23.23/24 + ipv6 address 2000:1:1:23::23/64 +! +! +interface lo + ip address 10.2.2.2/32 + ipv6 address 2000:2:2:2::2/128 +! +! +! +line vty +! diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r3/isisd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r3/isisd.conf new file mode 100644 index 0000000..6f3d8ec --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/r3/isisd.conf @@ -0,0 +1,45 @@ +log stdout debugging +! +! debug isis route-events +! debug isis events +! +interface r3-eth0 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 10 + isis network point-to-point +! +interface r3-eth1 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 10 + isis network point-to-point +! +interface r3-eth2 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 10 + isis network point-to-point +! +interface lo + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + isis passive + no isis hello padding +! +router isis ISIS1 + net 01.1111.0000.0000.0003.00 + is-type level-1 + topology ipv6-unicast +! +line vty diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r3/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r3/snmpd.conf new file mode 100644 index 0000000..c8d0bab --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/r3/snmpd.conf @@ -0,0 +1,18 @@ +agentAddress udp:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx + +agentXSocket /etc/frr/agentx +agentXPerms 777 755 root frr diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r3/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r3/zebra.conf new file mode 100644 index 0000000..e433995 --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/r3/zebra.conf @@ -0,0 +1,27 @@ +log file /tmp/zebra.log +log stdout +! +! debug zebra events +! debug zebra dplane +! +! +interface r3-eth0 + ip address 192.168.13.31/24 + ipv6 address 2000:1:1:13::31/64 +! +interface r3-eth1 + ip address 192.168.23.32/24 + ipv6 address 2000:1:1:23::32/64 +! +interface r3-eth2 + ip address 192.168.34.34/24 + ipv6 address 2000:1:1:34::34/64 +! +! +interface lo + ip address 10.3.3.3/32 + ipv6 address 2000:3:3:3::3/128 +! +! +! +line vty diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r4/bgpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r4/bgpd.conf new file mode 100644 index 0000000..2a834c7 --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/r4/bgpd.conf @@ -0,0 +1,43 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65000 + bgp router-id 10.4.4.4 + timers bgp 3 9 + neighbor 10.1.1.1 remote-as 65000 + neighbor 10.1.1.1 update-source 10.4.4.4 + neighbor 10.1.1.1 timers connect 10 + ! + address-family ipv4 vpn + neighbor 10.1.1.1 activate + exit-address-family +! + + address-family ipv6 vpn + neighbor 10.1.1.1 activate + exit-address-family +! +router bgp 65001 vrf VRF-a + bgp router-id 192.168.200.20 + timers bgp 3 9 + neighbor 192.168.200.10 remote-as 65001 + neighbor 192.168.200.10 update-source 192.168.200.20 + ! + address-family ipv4 unicast + redistribute connected + redistribute isis + label vpn export 1111 + rd vpn export 10:3 + rt vpn both 1:1 + export vpn + import vpn + exit-address-family + + address-family ipv6 unicast + redistribute connected + redistribute isis + label vpn export 1111 + rd vpn export 10:3 + rt vpn both 1:2 + export vpn + import vpn + exit-address-family diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r4/isisd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r4/isisd.conf new file mode 100644 index 0000000..7d923b6 --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/r4/isisd.conf @@ -0,0 +1,36 @@ +log stdout debugging +! +! debug isis route-events +! debug isis events +! +interface r4-eth0 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 10 + isis network point-to-point +! +interface r4-eth1 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 10 + isis network point-to-point +! +interface lo + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + isis passive + no isis hello padding +! +router isis ISIS1 + net 01.1111.0000.0000.0004.00 + is-type level-1 + topology ipv6-unicast +! +line vty diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r4/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r4/snmpd.conf new file mode 100644 index 0000000..c8d0bab --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/r4/snmpd.conf @@ -0,0 +1,18 @@ +agentAddress udp:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx + +agentXSocket /etc/frr/agentx +agentXPerms 777 755 root frr diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r4/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r4/zebra.conf new file mode 100644 index 0000000..14580e5 --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/r4/zebra.conf @@ -0,0 +1,27 @@ +log file /tmp/zebra.log +log stdout +! +! debug zebra events +! debug zebra dplane +! +! +interface r4-eth0 + ip address 192.168.14.41/24 + ipv6 address 2000:1:1:14::41/64 +! +interface r4-eth1 + ip address 192.168.34.43/24 + ipv6 address 2000:1:1:34::43/64 +! +interface r4-eth2 vrf aaa + ip address 192.168.200.20/24 + ipv6 address 2000:1:1:200::20/64 +! +! +interface lo + ip address 10.4.4.4/32 + ipv6 address 2000:4:4:4::4/128 +! +! +line vty +! diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py b/tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py new file mode 100755 index 0000000..0131e12 --- /dev/null +++ b/tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py @@ -0,0 +1,738 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_snmp_mplsl3vpn.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# + +""" +test_bgp_snmp_mplsl3vpn.py: Test mplsL3Vpn MIB [RFC4382]. +""" + +import os +import sys +from time import sleep +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.snmptest import SnmpTester +from lib import topotest + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.bgpd, pytest.mark.isisd, pytest.mark.snmp] + + +def build_topo(tgen): + "Build function" + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # + # Create routers + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r3") + tgen.add_router("r4") + tgen.add_router("ce1") + tgen.add_router("ce2") + tgen.add_router("ce3") + tgen.add_router("ce4") + + # r1-r2 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # r1-r3 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + # r1-r4 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r4"]) + + # r1-ce1 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["ce1"]) + + # r1-ce3 + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["ce3"]) + + # r1-ce4 + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["ce4"]) + + # r1-dangling + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["r1"]) + + # r2-r3 + switch = tgen.add_switch("s8") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + # r3-r4 + switch = tgen.add_switch("s9") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + # r4-ce2 + switch = tgen.add_switch("s10") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["ce2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + + # skip tests is SNMP not installed + snmpd = os.system("which snmpd") + if snmpd: + error_msg = "SNMP not installed - skipping" + pytest.skip(error_msg) + + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + r4 = tgen.gears["r4"] + + # setup VRF-a in r1 + r1.run("ip link add VRF-a type vrf table 1001") + r1.run("ip link set up dev VRF-a") + r1.run("ip link add VRF-b type vrf table 1002") + r1.run("ip link set up dev VRF-b") + r4.run("ip link add VRF-a type vrf table 1001") + r4.run("ip link set up dev VRF-a") + + # enslave vrf interfaces + r1.run("ip link set r1-eth3 master VRF-a") + r1.run("ip link set r1-eth4 master VRF-a") + r1.run("ip link set r1-eth5 master VRF-b") + r4.run("ip link set r4-eth1 master VRF-a") + + r1.run("sysctl -w net.ipv4.ip_forward=1") + r2.run("sysctl -w net.ipv4.ip_forward=1") + r3.run("sysctl -w net.ipv4.ip_forward=1") + r4.run("sysctl -w net.ipv4.ip_forward=1") + r1.run("sysctl -w net.mpls.conf.r1-eth0.input=1") + r1.run("sysctl -w net.mpls.conf.r1-eth1.input=1") + r1.run("sysctl -w net.mpls.conf.r1-eth2.input=1") + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, "{}/bgpd.conf".format(rname)), + "-M snmp", + ) + router.load_config( + TopoRouter.RD_SNMP, + os.path.join(CWD, "{}/snmpd.conf".format(rname)), + "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX,trap", + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +# SNMP utilities - maybe move to lib +def snmp_uint32_to_oid(val): + oid1 = int(val / 16777216) % 256 + oid2 = int(val / 65536) % 256 + oid3 = int(val / 256) % 256 + oid4 = int(val) % 256 + return "%(oid1)s.%(oid2)s.%(oid3)s.%(oid4)s" % locals() + + +def snmp_oid_to_uint32(oid): + values = oid.split(".") + return ( + (int(values[0]) * 16777216) + + (int(values[1]) * 65536) + + (int(values[2]) * 256) + + int(values[3]) + ) + + +def snmp_str_to_oid(str): + out_oid = "" + for char in str: + out_oid += "{}.".format(ord(char)) + return out_oid.rstrip(".") + + +def snmp_oid_to_str(oid): + out_str = "" + oids = oid.split(".") + for char in oids: + out_str += "{}".format(chr(int(char))) + return out_str + + +def snmp_rte_oid(vrf, dtype, dest, plen, policy, ntype, nhop=0): + oid_1 = snmp_str_to_oid(vrf) + oid_2 = dtype + oid_3 = dest + oid_4 = plen + oid_5 = "0.{}".format(policy) + oid_6 = ntype + if ntype == 0: + oid_7 = "" + else: + oid_7 = ".{}".format(nhop) + + return "{}.{}.{}.{}.{}.{}{}".format(oid_1, oid_2, oid_3, oid_4, oid_5, oid_6, oid_7) + + +def test_pe1_converge_evpn(): + "Wait for protocol convergence" + tgen = get_topogen() + + r1 = tgen.gears["r1"] + + def _convergence(): + r1 = tgen.gears["r1"] + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + return r1_snmp.test_oid("bgpVersion", "10") + + _, result = topotest.run_and_expect(_convergence, True, count=20, wait=1) + assertmsg = "BGP SNMP does not seem to be running" + assert result, assertmsg + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + count = 0 + passed = False + while count < 125: + if r1_snmp.test_oid_walk("bgpPeerLocalAddr.10.4.4.4", ["10.1.1.1"]): + passed = True + break + count += 1 + sleep(1) + # tgen.mininet_cli() + assertmsg = "BGP Peer 10.4.4.4 did not connect" + assert passed, assertmsg + + +interfaces_up_test = { + "mplsL3VpnConfiguredVrfs": "2", + "mplsL3VpnActiveVrfs": "2", + "mplsL3VpnConnectedInterfaces": "3", + "mplsL3VpnNotificationEnable": "true(1)", + "mplsL3VpnVrfConfMaxPossRts": "0", + "mplsL3VpnVrfConfRteMxThrshTime": "0 seconds", + "mplsL3VpnIlllblRcvThrsh": "0", +} + +interfaces_down_test = { + "mplsL3VpnConfiguredVrfs": "2", + "mplsL3VpnActiveVrfs": "1", + "mplsL3VpnConnectedInterfaces": "3", + "mplsL3VpnNotificationEnable": "true(1)", + "mplsL3VpnVrfConfMaxPossRts": "0", + "mplsL3VpnVrfConfRteMxThrshTime": "0 seconds", + "mplsL3VpnIlllblRcvThrsh": "0", +} + + +def test_r1_mplsvpn_scalars(): + "check scalar values" + tgen = get_topogen() + r1 = tgen.gears["r1"] + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + for item in interfaces_up_test.keys(): + assertmsg = "{} should be {}: value {}".format( + item, interfaces_up_test[item], r1_snmp.get_next(item) + ) + assert r1_snmp.test_oid(item, interfaces_up_test[item]), assertmsg + + +def test_r1_mplsvpn_scalars_interface(): + "check scalar interface changing values" + tgen = get_topogen() + r1 = tgen.gears["r1"] + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + r1.vtysh_cmd("conf t\ninterface r1-eth3\nshutdown") + r1.vtysh_cmd("conf t\ninterface r1-eth4\nshutdown") + + for item in interfaces_up_test.keys(): + assertmsg = "{} should be {}: value {}".format( + item, interfaces_down_test[item], r1_snmp.get_next(item) + ) + assert r1_snmp.test_oid(item, interfaces_down_test[item]), assertmsg + + r1.vtysh_cmd("conf t\ninterface r1-eth3\nno shutdown") + r1.vtysh_cmd("conf t\ninterface r1-eth4\nno shutdown") + + for item in interfaces_up_test.keys(): + assertmsg = "{} should be {}: value {}".format( + item, interfaces_up_test[item], r1_snmp.get_next(item) + ) + assert r1_snmp.test_oid(item, interfaces_up_test[item]), assertmsg + + +def router_interface_get_ifindex(router, interface): + ifindex = 0 + r_int_output = router.vtysh_cmd( + "show interface {}-{}".format(router.name, interface) + ) + int_lines = r_int_output.splitlines() + for line in int_lines: + line_items = line.lstrip().split(" ") + if "index" in line_items[0]: + ifindex = line_items[1] + return ifindex + + +def generate_vrf_ifindex_oid(vrf, ifindex): + + intoid = snmp_uint32_to_oid(int(ifindex)) + vrfoid = snmp_str_to_oid(vrf) + oid = "{}.{}".format(vrfoid, intoid) + + return oid + + +def generate_vrf_index_type_oid(vrf, index, type): + vrfoid = snmp_str_to_oid(vrf) + intoid = snmp_uint32_to_oid(int(index)) + oid = "{}.{}.{}".format(vrfoid, intoid, type) + + return oid + + +iftable_up_test = { + "mplsL3VpnIfVpnClassification": ["enterprise(2)", "enterprise(2)", "enterprise(2)"], + "mplsL3VpnIfConfStorageType": ["volatile(2)", "volatile(2)", "volatile(2)"], + "mplsL3VpnIfConfRowStatus": ["active(1)", "active(1)", "active(1)"], +} + + +def get_timetick_val(time): + return int(time.split(" ")[0].lstrip("(").rstrip(")")) + + +def test_r1_mplsvpn_IfTable(): + "mplsL3VpnIf table values" + + tgen = get_topogen() + r1 = tgen.gears["r1"] + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + # tgen.mininet_cli() + eth3_ifindex = router_interface_get_ifindex(r1, "eth3") + eth4_ifindex = router_interface_get_ifindex(r1, "eth4") + eth5_ifindex = router_interface_get_ifindex(r1, "eth5") + + # get ifindex and make sure the oid is correct + + oids = [] + # generate oid + oids.append(generate_vrf_ifindex_oid("VRF-a", eth3_ifindex)) + oids.append(generate_vrf_ifindex_oid("VRF-a", eth4_ifindex)) + oids.append(generate_vrf_ifindex_oid("VRF-b", eth5_ifindex)) + + for item in iftable_up_test.keys(): + assertmsg = "{} should be {} oids {} full dict {}:".format( + item, iftable_up_test[item], oids, r1_snmp.walk(item) + ) + assert r1_snmp.test_oid_walk(item, iftable_up_test[item], oids), assertmsg + + # an inactive vrf should not affect these values + r1.cmd("ip link set r1-eth5 down") + + for item in iftable_up_test.keys(): + assertmsg = "{} should be {} oids {} full dict {}:".format( + item, iftable_up_test[item], oids, r1_snmp.walk(item) + ) + assert r1_snmp.test_oid_walk(item, iftable_up_test[item], oids), assertmsg + + r1.cmd("ip link set r1-eth5 up") + + +vrftable_test = { + "mplsL3VpnVrfDescription": ["VRF-a", "VRF-b"], + "mplsL3VpnVrfRD": ['"10:1"', '"10:2"'], + "mplsL3VpnVrfOperStatus": ["up(1)", "up(1)"], + "mplsL3VpnVrfActiveInterfaces": ["2", "1"], + "mplsL3VpnVrfAssociatedInterfaces": ["2", "1"], + "mplsL3VpnVrfConfMidRteThresh": ["0", "0"], + "mplsL3VpnVrfConfHighRteThresh": ["0", "0"], + "mplsL3VpnVrfConfMaxRoutes": ["0", "0"], + "mplsL3VpnVrfConfRowStatus": ["active(1)", "active(1)"], + "mplsL3VpnVrfConfAdminStatus": ["up(1)", "up(1)"], + "mplsL3VpnVrfConfStorageType": ["volatile(2)", "volatile(2)"], +} + + +def test_r1_mplsvpn_VrfTable(): + tgen = get_topogen() + + r1 = tgen.gears["r1"] + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + # tgen.mininet_cli() + + oids = [] + + oids.append(snmp_str_to_oid("VRF-a")) + oids.append(snmp_str_to_oid("VRF-b")) + + # check items + for item in vrftable_test.keys(): + assertmsg = "{} should be {} oids {} full dict {}:".format( + item, vrftable_test[item], oids, r1_snmp.walk(item) + ) + assert r1_snmp.test_oid_walk(item, vrftable_test[item], oids), assertmsg + + # check timetick set and stable + ts_a = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-a"))) + ts_b = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-b"))) + ts_val_a1 = get_timetick_val(ts_a) + ts_val_b1 = get_timetick_val(ts_b) + ts_a = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-a"))) + ts_b = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-b"))) + ts_val_a2 = get_timetick_val(ts_a) + ts_val_b2 = get_timetick_val(ts_b) + + assertmsg = "timestamp values for VRF-a do not match {} {}".format( + ts_val_a1, ts_val_a2 + ) + assert ts_val_a1 == ts_val_a2, assertmsg + assertmsg = "timestamp values for VRF-b do not match {} {}".format( + ts_val_b1, ts_val_b2 + ) + assert ts_val_b1 == ts_val_b2, assertmsg + + # take Last changed time, fiddle with active interfaces, ensure + # time changes and active interfaces change + ts_last = r1_snmp.get( + "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a")) + ) + ts_val_last_1 = get_timetick_val(ts_last) + r1.vtysh_cmd("conf t\ninterface r1-eth3\nshutdown") + active_int = r1_snmp.get( + "mplsL3VpnVrfActiveInterfaces.{}".format(snmp_str_to_oid("VRF-a")) + ) + assertmsg = "mplsL3VpnVrfActiveInterfaces incorrect should be 1 value {}".format( + active_int + ) + assert active_int == "1", assertmsg + + ts_last = r1_snmp.get( + "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a")) + ) + ts_val_last_2 = get_timetick_val(ts_last) + assertmsg = "mplsL3VpnVrfConfLastChanged does not update on interface change" + assert ts_val_last_2 > ts_val_last_1, assertmsg + r1.vtysh_cmd("conf t\ninterface r1-eth3\nno shutdown") + + # take Last changed time, fiddle with associated interfaces, ensure + # time changes and active interfaces change + ts_last = r1_snmp.get( + "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a")) + ) + ts_val_last_1 = get_timetick_val(ts_last) + r1.cmd("ip link set r1-eth6 master VRF-a") + r1.cmd("ip link set r1-eth6 up") + + associated_int = r1_snmp.get( + "mplsL3VpnVrfAssociatedInterfaces.{}".format(snmp_str_to_oid("VRF-a")) + ) + assertmsg = ( + "mplsL3VpnVrfAssociatedInterfaces incorrect should be 3 value {}".format( + associated_int + ) + ) + + assert associated_int == "3", assertmsg + ts_last = r1_snmp.get( + "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a")) + ) + ts_val_last_2 = get_timetick_val(ts_last) + assertmsg = "mplsL3VpnVrfConfLastChanged does not update on interface change" + assert ts_val_last_2 > ts_val_last_1, assertmsg + r1.cmd("ip link del r1-eth6 master VRF-a") + r1.cmd("ip link set r1-eth6 down") + + +rt_table_test = { + "mplsL3VpnVrfRT": ['"1:1"', '"1:2"'], + "mplsL3VpnVrfRTDescr": ["RT both for VRF VRF-a", "RT both for VRF VRF-b"], + "mplsL3VpnVrfRTRowStatus": ["active(1)", "active(1)"], + "mplsL3VpnVrfRTStorageType": ["volatile(2)", "volatile(2)"], +} + + +def test_r1_mplsvpn_VrfRT_table(): + tgen = get_topogen() + + r1 = tgen.gears["r1"] + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + oids = [] + oids.append(generate_vrf_index_type_oid("VRF-a", 1, 3)) + oids.append(generate_vrf_index_type_oid("VRF-b", 1, 3)) + + # check items + for item in rt_table_test.keys(): + print(item) + assertmsg = "{} should be {} oids {} full dict {}:".format( + item, rt_table_test[item], oids, r1_snmp.walk(item) + ) + assert r1_snmp.test_oid_walk(item, rt_table_test[item], oids), assertmsg + + +def test_r1_mplsvpn_perf_table(): + tgen = get_topogen() + + r1 = tgen.gears["r1"] + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + # tgen.mininet_cli() + oid_a = snmp_str_to_oid("VRF-a") + oid_b = snmp_str_to_oid("VRF-b") + + # poll for 10 seconds for routes to appear + count = 0 + passed = False + while count < 60: + if r1_snmp.test_oid_walk( + "mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_a), ["7"] + ): + passed = True + break + count += 1 + sleep(1) + # tgen.mininet_cli() + assertmsg = "mplsL3VpnVrfPerfCurrNumRoutes shouold be 7 got {}".format( + r1_snmp.get("mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_a)) + ) + assert passed, assertmsg + curr_a = int(r1_snmp.get("mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_a))) + del_a = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesDeleted.{}".format(oid_a))) + add_a = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesAdded.{}".format(oid_a))) + + assertmsg = "FAIL curr{} does not equal added{} - deleted {}".format( + curr_a, add_a, del_a + ) + assert curr_a == (add_a - del_a), assertmsg + curr_b = int(r1_snmp.get("mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_b))) + del_b = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesDeleted.{}".format(oid_b))) + add_b = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesAdded.{}".format(oid_b))) + assertmsg = "FAIL curr{} does not equal added{} - deleted {}".format( + curr_b, add_b, del_b + ) + assert curr_b == (add_b - del_b), assertmsg + + +rte_table_test = { + "mplsL3VpnVrfRteInetCidrDestType": [ + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + ], + "mplsL3VpnVrfRteInetCidrDest": [ + "0A 05 05 05", + "0A 07 07 07", + "C0 A8 22 00", + "C0 A8 64 00", + "C0 A8 64 00", + "C0 A8 C8 00", + "C0 A8 C8 00", + ], + "mplsL3VpnVrfRteInetCidrPfxLen": ["32", "32", "24", "24", "24", "24", "24"], + "mplsL3VpnVrfRteInetCidrNHopType": [ + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "unknown(0)", + "ipv4(1)", + "unknown(0)", + ], + "mplsL3VpnVrfRteInetCidrNextHop": [ + "C0 A8 64 0A", + "C0 A8 C8 0A", + "0A 04 04 04", + "C0 A8 64 0A", + '""', + "C0 A8 C8 0A", + '""', + ], + "mplsL3VpnVrfRteInetCidrType": [ + "local(3)", + "local(3)", + "remote(4)", + "local(3)", + "other(1)", + "local(3)", + "other(1)", + ], + "mplsL3VpnVrfRteInetCidrProto": [ + "bgp(14)", + "bgp(14)", + "bgp(14)", + "bgp(14)", + "local(2)", + "bgp(14)", + "local(2)", + ], + "mplsL3VpnVrfRteInetCidrNextHopAS": [ + "65001", + "65001", + "0", + "65001", + "0", + "65001", + "0", + ], + "mplsL3VpnVrfRteInetCidrMetric1": ["0", "0", "20", "0", "0", "0", "0"], + "mplsL3VpnVrfRteInetCidrMetric2": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"], + "mplsL3VpnVrfRteInetCidrMetric3": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"], + "mplsL3VpnVrfRteInetCidrMetric4": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"], + "mplsL3VpnVrfRteInetCidrMetric5": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"], + "mplsL3VpnVrfRteXCPointer": ["00", "00", "00", "00", "00", "00", "00"], + "mplsL3VpnVrfRteInetCidrStatus": [ + "active(1)", + "active(1)", + "active(1)", + "active(1)", + "active(1)", + "active(1)", + "active(1)", + ], +} + + +def test_r1_mplsvpn_rte_table(): + tgen = get_topogen() + + r1 = tgen.gears["r1"] + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + # tgen.mininet_cli() + oid_1 = snmp_rte_oid("VRF-a", 1, "10.5.5.5", 32, 0, 1, "192.168.100.10") + oid_2 = snmp_rte_oid("VRF-a", 1, "10.7.7.7", 32, 0, 1, "192.168.200.10") + oid_3 = snmp_rte_oid("VRF-a", 1, "192.168.34.0", 24, 0, 1, "10.4.4.4") + oid_4 = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 1, 1, "192.168.100.10") + oid_4_a = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 0, 1, "192.168.100.10") + oid_5 = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 0, 0) + oid_5_a = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 1, 0) + oid_6 = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 1, 1, "192.168.200.10") + oid_6_a = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 0, 1, "192.168.200.10") + oid_7 = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 0, 0) + oid_7_a = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 1, 0) + + oid_lists = [ + [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6, oid_7], + [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6, oid_7], + [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6_a, oid_7_a], + [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6_a, oid_7_a], + [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6, oid_7], + [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6, oid_7], + [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6_a, oid_7_a], + [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6_a, oid_7_a], + ] + + # check items + + passed = False + for oid_list in oid_lists: + passed = True + for item in rte_table_test.keys(): + print(item) + assertmsg = "{} should be {} oids {} full dict {}:".format( + item, rte_table_test[item], oid_list, r1_snmp.walk(item) + ) + if not r1_snmp.test_oid_walk(item, rte_table_test[item], oid_list): + passed = False + break + print( + "{} should be {} oids {} full dict {}:".format( + item, rte_table_test[item], oid_list, r1_snmp.walk(item) + ) + ) + if passed: + break + # generate ifindex row grabbing ifindices from vtysh + if passed: + ifindex_row = [ + router_interface_get_ifindex(r1, "eth3"), + router_interface_get_ifindex(r1, "eth4"), + router_interface_get_ifindex(r1, "eth2"), + router_interface_get_ifindex(r1, "eth3"), + "0", + router_interface_get_ifindex(r1, "eth4"), + "0", + ] + if not r1_snmp.test_oid_walk( + "mplsL3VpnVrfRteInetCidrIfIndex", ifindex_row, oid_list + ): + passed = False + + print("passed {}".format(passed)) + assert passed, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_software_version/__init__.py b/tests/topotests/bgp_software_version/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_software_version/r1/bgpd.conf b/tests/topotests/bgp_software_version/r1/bgpd.conf new file mode 100644 index 0000000..42d15f7 --- /dev/null +++ b/tests/topotests/bgp_software_version/r1/bgpd.conf @@ -0,0 +1,8 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + bgp default software-version-capability + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 +! diff --git a/tests/topotests/bgp_software_version/r1/zebra.conf b/tests/topotests/bgp_software_version/r1/zebra.conf new file mode 100644 index 0000000..b29940f --- /dev/null +++ b/tests/topotests/bgp_software_version/r1/zebra.conf @@ -0,0 +1,4 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_software_version/r2/bgpd.conf b/tests/topotests/bgp_software_version/r2/bgpd.conf new file mode 100644 index 0000000..c8ab017 --- /dev/null +++ b/tests/topotests/bgp_software_version/r2/bgpd.conf @@ -0,0 +1,7 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + neighbor 192.168.1.1 capability software-version +! diff --git a/tests/topotests/bgp_software_version/r2/zebra.conf b/tests/topotests/bgp_software_version/r2/zebra.conf new file mode 100644 index 0000000..cffe827 --- /dev/null +++ b/tests/topotests/bgp_software_version/r2/zebra.conf @@ -0,0 +1,4 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_software_version/test_bgp_software_version.py b/tests/topotests/bgp_software_version/test_bgp_software_version.py new file mode 100644 index 0000000..25e646c --- /dev/null +++ b/tests/topotests/bgp_software_version/test_bgp_software_version.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Test if Software Version capability works if forced with a knob. +Reference: https://datatracker.ietf.org/doc/html/draft-abraitis-bgp-version-capability +""" + +import os +import re +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_software_version(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp summary json")) + expected = {"ipv4Unicast": {"peers": {"192.168.1.2": {"state": "Established"}}}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge" + + def _bgp_check_software_version(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor 192.168.1.2 json")) + + try: + versions = output["192.168.1.2"]["neighborCapabilities"]["softwareVersion"] + adv = versions["advertisedSoftwareVersion"] + rcv = versions["receivedSoftwareVersion"] + + if not adv and not rcv: + return False + + pattern = "^FRRouting/\\d.+" + if re.search(pattern, adv) and re.search(pattern, rcv): + return True + except: + return False + + return False + + assert _bgp_check_software_version(), "Neighbor's software version is n/a" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_soo/__init__.py b/tests/topotests/bgp_soo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_soo/cpe1/bgpd.conf b/tests/topotests/bgp_soo/cpe1/bgpd.conf new file mode 100644 index 0000000..a8984d4 --- /dev/null +++ b/tests/topotests/bgp_soo/cpe1/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 10.0.0.2 remote-as internal + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_soo/cpe1/zebra.conf b/tests/topotests/bgp_soo/cpe1/zebra.conf new file mode 100644 index 0000000..669cb91 --- /dev/null +++ b/tests/topotests/bgp_soo/cpe1/zebra.conf @@ -0,0 +1,12 @@ +! +interface lo + ip address 172.16.255.1/32 +! +interface cpe1-eth0 + ip address 192.168.1.1/24 +! +interface cpe1-eth1 + ip address 10.0.0.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_soo/cpe2/bgpd.conf b/tests/topotests/bgp_soo/cpe2/bgpd.conf new file mode 100644 index 0000000..19f7a24 --- /dev/null +++ b/tests/topotests/bgp_soo/cpe2/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + neighbor 10.0.0.1 remote-as internal + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_soo/cpe2/zebra.conf b/tests/topotests/bgp_soo/cpe2/zebra.conf new file mode 100644 index 0000000..52f36c0 --- /dev/null +++ b/tests/topotests/bgp_soo/cpe2/zebra.conf @@ -0,0 +1,9 @@ +! +interface cpe2-eth0 + ip address 192.168.2.1/24 +! +interface cpe2-eth1 + ip address 10.0.0.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_soo/pe1/bgpd.conf b/tests/topotests/bgp_soo/pe1/bgpd.conf new file mode 100644 index 0000000..04a6857 --- /dev/null +++ b/tests/topotests/bgp_soo/pe1/bgpd.conf @@ -0,0 +1,27 @@ +router bgp 65001 + bgp router-id 10.10.10.10 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 10.10.10.20 remote-as internal + neighbor 10.10.10.20 update-source 10.10.10.10 + address-family ipv4 vpn + neighbor 10.10.10.20 activate + exit-address-family +! +router bgp 65001 vrf RED + bgp router-id 192.168.1.2 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + address-family ipv4 unicast + neighbor 192.168.1.1 as-override + neighbor 192.168.1.1 soo 65000:1 + label vpn export 1111 + rd vpn export 192.168.1.2:2 + rt vpn import 192.168.2.2:2 192.168.1.2:2 + rt vpn export 192.168.1.2:2 + export vpn + import vpn + exit-address-family +! diff --git a/tests/topotests/bgp_soo/pe1/ldpd.conf b/tests/topotests/bgp_soo/pe1/ldpd.conf new file mode 100644 index 0000000..fb40f06 --- /dev/null +++ b/tests/topotests/bgp_soo/pe1/ldpd.conf @@ -0,0 +1,10 @@ +mpls ldp + router-id 10.10.10.10 + ! + address-family ipv4 + discovery transport-address 10.10.10.10 + ! + interface pe1-eth1 + ! + ! +! diff --git a/tests/topotests/bgp_soo/pe1/ospfd.conf b/tests/topotests/bgp_soo/pe1/ospfd.conf new file mode 100644 index 0000000..34f0899 --- /dev/null +++ b/tests/topotests/bgp_soo/pe1/ospfd.conf @@ -0,0 +1,7 @@ +interface pe1-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +! +router ospf + router-id 10.10.10.10 + network 0.0.0.0/0 area 0 diff --git a/tests/topotests/bgp_soo/pe1/zebra.conf b/tests/topotests/bgp_soo/pe1/zebra.conf new file mode 100644 index 0000000..cc8ff19 --- /dev/null +++ b/tests/topotests/bgp_soo/pe1/zebra.conf @@ -0,0 +1,12 @@ +! +interface lo + ip address 10.10.10.10/32 +! +interface pe1-eth0 vrf RED + ip address 192.168.1.2/24 +! +interface pe1-eth1 + ip address 10.0.1.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_soo/pe2/bgpd.conf b/tests/topotests/bgp_soo/pe2/bgpd.conf new file mode 100644 index 0000000..efebc02 --- /dev/null +++ b/tests/topotests/bgp_soo/pe2/bgpd.conf @@ -0,0 +1,31 @@ +router bgp 65001 + bgp router-id 10.10.10.20 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 10.10.10.10 remote-as internal + neighbor 10.10.10.10 update-source 10.10.10.20 + address-family ipv4 vpn + neighbor 10.10.10.10 activate + exit-address-family +! +router bgp 65001 vrf RED + bgp router-id 192.168.2.2 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 + address-family ipv4 unicast + neighbor 192.168.2.1 as-override + neighbor 192.168.2.1 route-map cpe2-in in + label vpn export 2222 + rd vpn export 192.168.2.2:2 + rt vpn import 192.168.2.2:2 192.168.1.2:2 + rt vpn export 192.168.2.2:2 + export vpn + import vpn + exit-address-family +! +! To prefer internal MPLS route over eBGP +route-map cpe2-in permit 10 + set local-preference 50 +exit diff --git a/tests/topotests/bgp_soo/pe2/ldpd.conf b/tests/topotests/bgp_soo/pe2/ldpd.conf new file mode 100644 index 0000000..e2b5359 --- /dev/null +++ b/tests/topotests/bgp_soo/pe2/ldpd.conf @@ -0,0 +1,10 @@ +mpls ldp + router-id 10.10.10.20 + ! + address-family ipv4 + discovery transport-address 10.10.10.20 + ! + interface pe2-eth0 + ! + ! +! diff --git a/tests/topotests/bgp_soo/pe2/ospfd.conf b/tests/topotests/bgp_soo/pe2/ospfd.conf new file mode 100644 index 0000000..4c4b137 --- /dev/null +++ b/tests/topotests/bgp_soo/pe2/ospfd.conf @@ -0,0 +1,7 @@ +interface pe2-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +! +router ospf + router-id 10.10.10.20 + network 0.0.0.0/0 area 0 diff --git a/tests/topotests/bgp_soo/pe2/zebra.conf b/tests/topotests/bgp_soo/pe2/zebra.conf new file mode 100644 index 0000000..8049a74 --- /dev/null +++ b/tests/topotests/bgp_soo/pe2/zebra.conf @@ -0,0 +1,12 @@ +! +interface lo + ip address 10.10.10.20/32 +! +interface pe2-eth1 vrf RED + ip address 192.168.2.2/24 +! +interface pe2-eth0 + ip address 10.0.1.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_soo/test_bgp_soo.py b/tests/topotests/bgp_soo/test_bgp_soo.py new file mode 100644 index 0000000..967bed0 --- /dev/null +++ b/tests/topotests/bgp_soo/test_bgp_soo.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Test if BGP SoO per neighbor works correctly. Routes having SoO +extended community MUST be rejected if the neighbor is configured +with soo (neighbor soo). +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("cpe1") + tgen.add_router("cpe2") + tgen.add_router("pe1") + tgen.add_router("pe2") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["cpe1"]) + switch.add_link(tgen.gears["pe1"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["pe1"]) + switch.add_link(tgen.gears["pe2"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["pe2"]) + switch.add_link(tgen.gears["cpe2"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["cpe2"]) + switch.add_link(tgen.gears["cpe1"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + pe1 = tgen.gears["pe1"] + pe2 = tgen.gears["pe2"] + + pe1.run("ip link add RED type vrf table 1001") + pe1.run("ip link set up dev RED") + pe2.run("ip link add RED type vrf table 1001") + pe2.run("ip link set up dev RED") + pe1.run("ip link set pe1-eth0 master RED") + pe2.run("ip link set pe2-eth1 master RED") + + pe1.run("sysctl -w net.ipv4.ip_forward=1") + pe2.run("sysctl -w net.ipv4.ip_forward=1") + pe1.run("sysctl -w net.mpls.conf.pe1-eth0.input=1") + pe2.run("sysctl -w net.mpls.conf.pe2-eth1.input=1") + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_soo(): + tgen = get_topogen() + + pe2 = tgen.gears["pe2"] + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_soo_unconfigured(): + output = json.loads( + pe2.vtysh_cmd( + "show bgp vrf RED ipv4 unicast neighbors 192.168.2.1 advertised-routes json" + ) + ) + expected = {"advertisedRoutes": {"172.16.255.1/32": {"path": "65001"}}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_soo_unconfigured) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed to see BGP convergence in pe2" + + step("Configure SoO (65000:1) for PE2 -- CPE2 session") + pe2.vtysh_cmd( + """ + configure terminal + router bgp 65001 vrf RED + address-family ipv4 unicast + neighbor 192.168.2.1 soo 65000:1 + """ + ) + + def _bgp_soo_configured(): + output = json.loads( + pe2.vtysh_cmd( + "show bgp vrf RED ipv4 unicast neighbors 192.168.2.1 advertised-routes json" + ) + ) + expected = {"advertisedRoutes": {"172.16.255.1/32": None}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_soo_configured) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "SoO filtering does not work from pe2" + + step("Configure SoO (65000:2) for PE2 -- CPE2 session") + pe2.vtysh_cmd( + """ + configure terminal + router bgp 65001 vrf RED + address-family ipv4 unicast + neighbor 192.168.2.1 soo 65000:2 + """ + ) + + test_func = functools.partial(_bgp_soo_unconfigured) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "SoO filtering does not work from pe2" + + step("Unconfigure SoO for PE2 -- CPE2 session") + pe2.vtysh_cmd( + """ + configure terminal + router bgp 65001 vrf RED + address-family ipv4 unicast + no neighbor 192.168.2.1 soo + """ + ) + + test_func = functools.partial(_bgp_soo_unconfigured) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "SoO filtering does not work from pe2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/bgpd.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/staticd.conf new file mode 100644 index 0000000..bcf5a04 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/staticd.conf @@ -0,0 +1,4 @@ +! +ip route 0.0.0.0/0 192.168.1.254 +ipv6 route ::/0 2001:1::ffff +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/zebra.conf new file mode 100644 index 0000000..0615cf9 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/zebra.conf @@ -0,0 +1,6 @@ +hostname c11 +! +interface eth0 + ip address 192.168.1.1/24 + ipv6 address 2001:1::1/64 +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/bgpd.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/staticd.conf new file mode 100644 index 0000000..bcf5a04 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/staticd.conf @@ -0,0 +1,4 @@ +! +ip route 0.0.0.0/0 192.168.1.254 +ipv6 route ::/0 2001:1::ffff +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/zebra.conf new file mode 100644 index 0000000..18985aa --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/zebra.conf @@ -0,0 +1,6 @@ +hostname c12 +! +interface eth0 + ip address 192.168.1.1/24 + ipv6 address 2001:1::1/64 +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/bgpd.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/staticd.conf new file mode 100644 index 0000000..608e600 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/staticd.conf @@ -0,0 +1,4 @@ +! +ip route 0.0.0.0/0 192.168.2.254 +ipv6 route ::/0 2001:2::ffff +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/zebra.conf new file mode 100644 index 0000000..b8b70ac --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/zebra.conf @@ -0,0 +1,6 @@ +hostname c21 +! +interface eth0 + ip address 192.168.2.1/24 + ipv6 address 2001:2::1/64 +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/bgpd.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/staticd.conf new file mode 100644 index 0000000..277aae9 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/staticd.conf @@ -0,0 +1,5 @@ + +! +ip route 0.0.0.0/0 192.168.2.254 +ipv6 route ::/0 2001:2::ffff +! \ No newline at end of file diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/zebra.conf new file mode 100644 index 0000000..cc764cc --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/zebra.conf @@ -0,0 +1,9 @@ +hostname c22 +! +interface eth0 + ip address 192.168.2.1/24 + ipv6 address 2001:2::1/64 +! +ip route 0.0.0.0/0 192.168.2.254 +ipv6 route ::/0 2001:2::ffff +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/bgpd.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/staticd.conf new file mode 100644 index 0000000..0c88575 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/staticd.conf @@ -0,0 +1,4 @@ +! +ip route 0.0.0.0/0 192.168.3.254 +ipv6 route ::/0 2001:3::ffff +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/zebra.conf new file mode 100644 index 0000000..3f75641 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/zebra.conf @@ -0,0 +1,6 @@ +hostname c31 +! +interface eth0 + ip address 192.168.3.1/24 + ipv6 address 2001:3::1/64 +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/bgpd.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/staticd.conf new file mode 100644 index 0000000..0c88575 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/staticd.conf @@ -0,0 +1,4 @@ +! +ip route 0.0.0.0/0 192.168.3.254 +ipv6 route ::/0 2001:3::ffff +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/zebra.conf new file mode 100644 index 0000000..c06a7d1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/zebra.conf @@ -0,0 +1,6 @@ +hostname c32 +! +interface eth0 + ip address 192.168.3.1/24 + ipv6 address 2001:3::1/64 +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf new file mode 100644 index 0000000..22b9014 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf @@ -0,0 +1,57 @@ +frr defaults traditional +! +hostname r1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +router bgp 65001 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001:db8:12::2 remote-as 65002 + neighbor 2001:db8:12::2 timers 3 10 + neighbor 2001:db8:12::2 timers connect 1 + neighbor 2001:db8:12::2 capability extended-nexthop + neighbor 2001:db8:13::3 remote-as 65001 + neighbor 2001:db8:13::3 timers 3 10 + neighbor 2001:db8:13::3 timers connect 1 + neighbor 2001:db8:13::3 capability extended-nexthop + ! + segment-routing srv6 + locator default + ! + address-family ipv4 vpn + neighbor 2001:db8:12::2 activate + neighbor 2001:db8:13::3 activate + exit-address-family + ! +! +router bgp 65001 vrf vrf10 + bgp router-id 192.0.2.1 + ! + address-family ipv4 unicast + redistribute connected + sid vpn export 1 + rd vpn export 65001:10 + rt vpn both 0:10 + import vpn + export vpn + exit-address-family + ! +! +router bgp 65001 vrf vrf20 + bgp router-id 192.0.2.1 + ! + address-family ipv4 unicast + redistribute connected + sid vpn export 2 + rd vpn export 65001:20 + rt vpn both 0:20 + import vpn + export vpn + exit-address-family + ! +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/staticd.conf new file mode 100644 index 0000000..49b64fd --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/staticd.conf @@ -0,0 +1,4 @@ +! +ipv6 route 2001:db8:2:2::/64 2001:db8:12::2 +ipv6 route 2001:db8:3:3::/64 2001:db8:13::3 +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/zebra.conf new file mode 100644 index 0000000..79dbf95 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/zebra.conf @@ -0,0 +1,32 @@ +log file zebra.log +! +hostname r1 +! +interface lo + ipv6 address 2001:db8:1:1::1/128 +! +interface eth0 + ipv6 address 2001:db8:12::1/64 +! +interface eth1 + ipv6 address 2001:db8:13::1/64 +! +interface eth2 vrf vrf10 + ip address 192.168.1.254/24 +! +interface eth3 vrf vrf20 + ip address 192.168.1.254/24 +! +segment-routing + srv6 + locators + locator default + prefix 2001:db8:1:1::/64 + ! + ! +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf new file mode 100644 index 0000000..42b9d51 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf @@ -0,0 +1,52 @@ +frr defaults traditional +! +hostname r2 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +router bgp 65002 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001:db8:12::1 remote-as 65001 + neighbor 2001:db8:12::1 timers 3 10 + neighbor 2001:db8:12::1 timers connect 1 + neighbor 2001:db8:12::1 capability extended-nexthop + ! + segment-routing srv6 + locator default + ! + address-family ipv4 vpn + neighbor 2001:db8:12::1 activate + exit-address-family + ! +! +router bgp 65002 vrf vrf10 + bgp router-id 192.0.2.2 + ! + address-family ipv4 unicast + redistribute connected + sid vpn export 1 + rd vpn export 65002:10 + rt vpn both 0:10 + import vpn + export vpn + exit-address-family + ! +! +router bgp 65002 vrf vrf20 + bgp router-id 192.0.2.2 + ! + address-family ipv4 unicast + redistribute connected + sid vpn export 2 + rd vpn export 65002:20 + rt vpn both 0:20 + import vpn + export vpn + exit-address-family + ! +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/staticd.conf new file mode 100644 index 0000000..8d80c1e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/staticd.conf @@ -0,0 +1,4 @@ +! +ipv6 route 2001:db8:1:1::/64 2001:db8:12::1 +ipv6 route 2001:db8:3:3::/64 2001:db8:12::1 +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/zebra.conf new file mode 100644 index 0000000..09a65b9 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/zebra.conf @@ -0,0 +1,29 @@ +log file zebra.log +! +hostname r2 +! +interface lo + ipv6 address 2001:db8:2:2::1/128 +! +interface eth0 + ipv6 address 2001:db8:12::2/64 +! +interface eth1 vrf vrf10 + ip address 192.168.2.254/24 +! +interface eth2 vrf vrf20 + ip address 192.168.2.254/24 +! +segment-routing + srv6 + locators + locator default + prefix 2001:db8:2:2::/64 + ! + ! +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf new file mode 100644 index 0000000..339b4eb --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf @@ -0,0 +1,52 @@ +frr defaults traditional +! +hostname r2 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +router bgp 65001 + bgp router-id 192.0.2.3 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001:db8:13::1 remote-as 65001 + neighbor 2001:db8:13::1 timers 3 10 + neighbor 2001:db8:13::1 timers connect 1 + neighbor 2001:db8:13::1 capability extended-nexthop + ! + segment-routing srv6 + locator default + ! + address-family ipv4 vpn + neighbor 2001:db8:13::1 activate + exit-address-family + ! +! +router bgp 65001 vrf vrf10 + bgp router-id 192.0.2.3 + ! + address-family ipv4 unicast + redistribute connected + sid vpn export 1 + rd vpn export 65001:10 + rt vpn both 0:10 + import vpn + export vpn + exit-address-family + ! +! +router bgp 65001 vrf vrf20 + bgp router-id 192.0.2.2 + ! + address-family ipv4 unicast + redistribute connected + sid vpn export 2 + rd vpn export 65001:20 + rt vpn both 0:20 + import vpn + export vpn + exit-address-family + ! +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/staticd.conf new file mode 100644 index 0000000..9d672d5 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/staticd.conf @@ -0,0 +1,6 @@ +! +ipv6 route 2001:db8:12::/64 2001:db8:13::1 +! +ipv6 route 2001:db8:1:1::/64 2001:db8:13::1 +ipv6 route 2001:db8:2:2::/64 2001:db8:13::1 +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/zebra.conf new file mode 100644 index 0000000..a20cb76 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/zebra.conf @@ -0,0 +1,29 @@ +log file zebra.log +! +hostname r2 +! +interface lo + ipv6 address 2001:db8:3:3::1/128 +! +interface eth0 + ipv6 address 2001:db8:13::3/64 +! +interface eth1 vrf vrf10 + ip address 192.168.3.254/24 +! +interface eth2 vrf vrf20 + ip address 192.168.3.254/24 +! +segment-routing + srv6 + locators + locator default + prefix 2001:db8:3:3::/64 + ! + ! +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/test_bgp_srv6l3vpn_over_ipv6.py b/tests/topotests/bgp_srv6l3vpn_over_ipv6/test_bgp_srv6l3vpn_over_ipv6.py new file mode 100755 index 0000000..14b9ba8 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/test_bgp_srv6l3vpn_over_ipv6.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright (c) 2018, LabN Consulting, L.L.C. +# Authored by Lou Berger +# + +import os +import re +import sys +import json +import functools +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import required_linux_kernel_version +from lib.checkping import check_ping + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r3") + + tgen.add_router("c11") + tgen.add_router("c12") + tgen.add_router("c21") + tgen.add_router("c22") + tgen.add_router("c31") + tgen.add_router("c32") + + tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0") + tgen.add_link(tgen.gears["r1"], tgen.gears["r3"], "eth1", "eth0") + tgen.add_link(tgen.gears["r1"], tgen.gears["c11"], "eth2", "eth0") + tgen.add_link(tgen.gears["r1"], tgen.gears["c12"], "eth3", "eth0") + tgen.add_link(tgen.gears["r2"], tgen.gears["c21"], "eth1", "eth0") + tgen.add_link(tgen.gears["r2"], tgen.gears["c22"], "eth2", "eth0") + tgen.add_link(tgen.gears["r3"], tgen.gears["c31"], "eth1", "eth0") + tgen.add_link(tgen.gears["r3"], tgen.gears["c32"], "eth2", "eth0") + + +def setup_module(mod): + result = required_linux_kernel_version("5.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + for rname, router in tgen.routers().items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.gears["r1"].run("sysctl net.vrf.strict_mode=1") + tgen.gears["r1"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r1"].run("ip link set vrf10 up") + tgen.gears["r1"].run("ip link add vrf20 type vrf table 20") + tgen.gears["r1"].run("ip link set vrf20 up") + tgen.gears["r1"].run("ip link set eth2 master vrf10") + tgen.gears["r1"].run("ip link set eth3 master vrf20") + + tgen.gears["r2"].run("sysctl net.vrf.strict_mode=1") + tgen.gears["r2"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r2"].run("ip link set vrf10 up") + tgen.gears["r2"].run("ip link add vrf20 type vrf table 20") + tgen.gears["r2"].run("ip link set vrf20 up") + tgen.gears["r2"].run("ip link set eth1 master vrf10") + tgen.gears["r2"].run("ip link set eth2 master vrf20") + + tgen.gears["r3"].run("sysctl net.vrf.strict_mode=1") + tgen.gears["r3"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r3"].run("ip link set vrf10 up") + tgen.gears["r3"].run("ip link add vrf20 type vrf table 20") + tgen.gears["r3"].run("ip link set vrf20 up") + tgen.gears["r3"].run("ip link set eth1 master vrf10") + tgen.gears["r3"].run("ip link set eth2 master vrf20") + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_ping(): + tgen = get_topogen() + + check_ping("c11", "192.168.2.1", True, 10, 1) + check_ping("c11", "192.168.3.1", True, 10, 1) + check_ping("c12", "192.168.2.1", True, 10, 1) + check_ping("c12", "192.168.3.1", True, 10, 1) + check_ping("c21", "192.168.3.1", True, 10, 1) + check_ping("c22", "192.168.3.1", True, 10, 1) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/ce1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_route_leak/ce1/bgpd.conf new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/ce1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_route_leak/ce1/zebra.conf new file mode 100644 index 0000000..823a56d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_route_leak/ce1/zebra.conf @@ -0,0 +1,9 @@ +log file zebra.log +! +hostname ce1 +! +interface eth0 + ip address 172.16.0.1/24 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/bgpd.conf new file mode 100644 index 0000000..15779aa --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/bgpd.conf @@ -0,0 +1,41 @@ +frr defaults traditional +! +hostname pe1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +router bgp 65001 + bgp router-id 192.0.2.1 + ! + segment-routing srv6 + locator default + exit + ! +! +router bgp 65001 vrf vrf10 + bgp router-id 192.0.2.1 + ! + address-family ipv4 unicast + redistribute connected + sid vpn export auto + rd vpn export 65001:10 + rt vpn both 0:10 + import vpn + export vpn + exit-address-family + ! +! +router bgp 65001 vrf vrf20 + bgp router-id 192.0.2.1 + ! + address-family ipv4 unicast + rd vpn export 65001:20 + rt vpn both 0:10 + import vpn + export vpn + exit-address-family + ! +! diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/default_ipv4_vpn.json b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/default_ipv4_vpn.json new file mode 100644 index 0000000..dc86d7c --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/default_ipv4_vpn.json @@ -0,0 +1,31 @@ +{ + "vrfName": "default", + "routerId": "192.0.2.1", + "localAS": 65001, + "routes": { + "routeDistinguishers": { + "65001:10": { + "172.16.0.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "172.16.0.0", + "prefixLen": 24, + "network": "172.16.0.0\/24", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "hostname": "pe1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf10_ipv4_unicast.json b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf10_ipv4_unicast.json new file mode 100644 index 0000000..ce2d5c1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf10_ipv4_unicast.json @@ -0,0 +1,25 @@ +{ + "vrfName": "vrf10", + "routerId": "192.0.2.1", + "localAS": 65001, + "routes": { + "172.16.0.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "172.16.0.0", + "prefixLen": 24, + "network": "172.16.0.0\/24", + "origin": "incomplete", + "nexthops": [ + { + "hostname": "pe1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json new file mode 100644 index 0000000..9f78447 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json @@ -0,0 +1,22 @@ +{ + "172.16.0.0\/24": [ + { + "prefix": "172.16.0.0\/24", + "prefixLen": 24, + "protocol": "bgp", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "installed": true, + "nexthops": [ + { + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "vrf": "vrf10", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4_unicast.json b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4_unicast.json new file mode 100644 index 0000000..6a88d39 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4_unicast.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf20", + "routerId": "192.0.2.1", + "localAS": 65001, + "routes": { + "172.16.0.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "172.16.0.0", + "prefixLen": 24, + "network": "172.16.0.0\/24", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "hostname": "pe1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/zebra.conf new file mode 100644 index 0000000..52341fc --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/zebra.conf @@ -0,0 +1,27 @@ +log file zebra.log +! +hostname pe1 +! +interface lo + ip address 10.0.0.1/32 +! +interface eth0 vrf vrf10 + ip address 172.16.0.254/24 +! +line vty +! +segment-routing + srv6 + locators + locator default + prefix 2001:db8:2::/64 block-len 40 node-len 24 func-bits 16 + exit + ! + exit + ! + exit + ! +exit +! +end +! diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/test_bgp_srv6l3vpn_route_leak.py b/tests/topotests/bgp_srv6l3vpn_route_leak/test_bgp_srv6l3vpn_route_leak.py new file mode 100755 index 0000000..900d0c2 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_route_leak/test_bgp_srv6l3vpn_route_leak.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2022, LINE Corporation +# Authored by Ryoga Saito +# + +import os +import re +import sys +import json +import functools +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import required_linux_kernel_version + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("pe1") + tgen.add_router("ce1") + + tgen.add_link(tgen.gears["pe1"], tgen.gears["ce1"], "eth0", "eth0") + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + for rname, router in tgen.routers().items(): + router.load_config(TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname))) + router.load_config(TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname))) + + tgen.gears["pe1"].run("ip link add vrf10 type vrf table 10") + tgen.gears["pe1"].run("ip link set vrf10 up") + tgen.gears["pe1"].run("ip link add vrf20 type vrf table 20") + tgen.gears["pe1"].run("ip link set vrf20 up") + tgen.gears["pe1"].run("ip link set eth0 master vrf10") + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def open_json_file(path): + try: + with open(path, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(path) + + +def check(name, command, checker): + tgen = get_topogen() + router = tgen.gears[name] + + def _check(): + try: + return checker(router.vtysh_cmd(command)) + except: + return False + + logger.info('[+] check {} "{}"'.format(name, command)) + _, result = topotest.run_and_expect(_check, None, count=10, wait=0.5) + assert result is None, "Failed" + + +def check_vrf10_bgp_rib(output): + expected = open_json_file("%s/pe1/results/vrf10_ipv4_unicast.json" % CWD) + actual = json.loads(output) + return topotest.json_cmp(actual, expected) + + +def check_default_bgp_vpn_rib(output): + expected = open_json_file("%s/pe1/results/default_ipv4_vpn.json" % CWD) + actual = json.loads(output) + return topotest.json_cmp(actual, expected) + + +def check_vrf20_bgp_rib(output): + expected = open_json_file("%s/pe1/results/vrf20_ipv4_unicast.json" % CWD) + actual = json.loads(output) + return topotest.json_cmp(actual, expected) + + +def check_vrf20_rib(output): + expected = open_json_file("%s/pe1/results/vrf20_ipv4.json" % CWD) + actual = json.loads(output) + return topotest.json_cmp(actual, expected) + + +def test_rib(): + check("pe1", "show bgp vrf vrf10 ipv4 unicast json", check_vrf10_bgp_rib) + check("pe1", "show bgp ipv4 vpn json", check_default_bgp_vpn_rib) + check("pe1", "show bgp vrf vrf20 ipv4 unicast json", check_vrf20_bgp_rib) + check("pe1", "show ip route vrf vrf20 json", check_vrf20_rib) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf new file mode 100644 index 0000000..3459796 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce1 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json new file mode 100644 index 0000000..d19e315 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:1::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:1::/64": [ + { + "prefix": "2001:1::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf new file mode 100644 index 0000000..bb5f93f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf @@ -0,0 +1,16 @@ +log file zebra.log +! +hostname ce1 +! +interface eth0 + ipv6 address 2001:1::2/64 + ip address 192.168.1.2/24 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:1::1 +ip route 0.0.0.0/0 192.168.1.1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf new file mode 100644 index 0000000..8ed9978 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce2 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json new file mode 100644 index 0000000..35ff14e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:2::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:2::/64": [ + { + "prefix": "2001:2::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf new file mode 100644 index 0000000..a52b83f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf @@ -0,0 +1,16 @@ +log file zebra.log +! +hostname ce2 +! +interface eth0 + ipv6 address 2001:2::2/64 + ip address 192.168.2.2/24 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:2::1 +ip route 0.0.0.0/0 192.168.2.1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf new file mode 100644 index 0000000..a85d970 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce3 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json new file mode 100644 index 0000000..2f2931f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:3::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:3::/64": [ + { + "prefix": "2001:3::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf new file mode 100644 index 0000000..beca0b1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce3 +! +interface eth0 + ipv6 address 2001:3::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:3::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf new file mode 100644 index 0000000..93fb32f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce4 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json new file mode 100644 index 0000000..8a98768 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:4::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:4::/64": [ + { + "prefix": "2001:4::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf new file mode 100644 index 0000000..7b21074 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce4 +! +interface eth0 + ipv6 address 2001:4::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:4::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf new file mode 100644 index 0000000..2ab6f2d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce5 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json new file mode 100644 index 0000000..80ff52a --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:5::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:5::/64": [ + { + "prefix": "2001:5::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf new file mode 100644 index 0000000..b5ad48e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce5 +! +interface eth0 + ipv6 address 2001:5::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:5::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf new file mode 100644 index 0000000..e0b6540 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce6 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json new file mode 100644 index 0000000..ace6136 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:6::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:6::/64": [ + { + "prefix": "2001:6::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf new file mode 100644 index 0000000..7d19d98 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce6 +! +interface eth0 + ipv6 address 2001:6::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:6::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf new file mode 100644 index 0000000..bfc9db9 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf @@ -0,0 +1,79 @@ +frr defaults traditional +! +hostname r1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug bgp neighbor-events +!debug bgp zebra +!debug bgp vnc verbose +!debug bgp update-groups +!debug bgp updates in +!debug bgp updates out +!debug bgp vpn label +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp vpn rmap-event +! +router bgp 1 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001::2 remote-as 2 + neighbor 2001::2 timers 3 10 + neighbor 2001::2 timers connect 1 + neighbor 2001::2 update-source 2001::1 + neighbor 2001::2 capability extended-nexthop + ! + address-family ipv4 vpn + neighbor 2001::2 activate + exit-address-family + ! + address-family ipv6 vpn + neighbor 2001::2 activate + exit-address-family + ! + segment-routing srv6 + locator loc1 + ! +! +router bgp 1 vrf vrf10 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 1:10 + rt vpn both 99:99 + import vpn + export vpn + redistribute connected + exit-address-family +! + address-family ipv4 unicast + network 192.168.1.0/24 + sid vpn export auto + rd vpn export 11:10 + rt vpn both 77:77 + import vpn + export vpn + redistribute connected + exit-address-family +! +router bgp 1 vrf vrf20 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 1:20 + rt vpn both 88:88 + import vpn + export vpn + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json new file mode 100644 index 0000000..6fc43e1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json @@ -0,0 +1,169 @@ +{ + "vrfName": "default", + "tableVersion": 2, + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json new file mode 100644 index 0000000..9783c7e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json @@ -0,0 +1,23 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json new file mode 100644 index 0000000..80c1acf --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json @@ -0,0 +1,53 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "labels":[ + 16 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:1::" + } + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json new file mode 100644 index 0000000..9783c7e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json @@ -0,0 +1,23 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json new file mode 100644 index 0000000..07ca64b --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json @@ -0,0 +1,54 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"2001::2", + "afi":"ipv6", + "labels":[ + 128 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:8::" + } + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json new file mode 100644 index 0000000..6ac8dac --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json @@ -0,0 +1,77 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json new file mode 100644 index 0000000..fac3d1d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json @@ -0,0 +1,107 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "labels":[ + 32 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:2::" + } + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json new file mode 100644 index 0000000..69ce312 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json @@ -0,0 +1,77 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json new file mode 100644 index 0000000..04e2305 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json @@ -0,0 +1,106 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "labels":[ + 128 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:8::" + } + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json new file mode 100644 index 0000000..3cac156 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json @@ -0,0 +1,54 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"2001::2", + "afi":"ipv6", + "labels":[ + 16 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:1::" + } + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json new file mode 100644 index 0000000..163e9d6 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json @@ -0,0 +1,53 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"2001::2", + "afi":"ipv6", + "labels":[ + 128 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:8::" + } + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json new file mode 100644 index 0000000..1313f20 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json @@ -0,0 +1,106 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "labels":[ + 16 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:1::" + } + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json new file mode 100644 index 0000000..51f249b --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json @@ -0,0 +1,106 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "labels":[ + 128 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:8::" + } + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json new file mode 100644 index 0000000..6ac8dac --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json @@ -0,0 +1,77 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json new file mode 100644 index 0000000..1c3dad0 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json @@ -0,0 +1,22 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json new file mode 100644 index 0000000..9579bb1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json @@ -0,0 +1,112 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 32 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:2::" + } + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json new file mode 100644 index 0000000..25f146f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json @@ -0,0 +1,106 @@ +{ + "2001:4::\/64":[ + { + "prefix":"2001:4::\/64", + "protocol":"bgp", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 48 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:3::" + } + } + ] + } + ], + "2001:5::\/64":[ + { + "prefix":"2001:5::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "2001:6::\/64":[ + { + "prefix":"2001:6::\/64", + "protocol":"bgp", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 48 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:3::" + } + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf new file mode 100644 index 0000000..cf31a5c --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf @@ -0,0 +1,43 @@ +log file zebra.log +! +hostname r1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +interface eth0 + ipv6 address 2001::1/64 +! +interface eth1 vrf vrf10 + ipv6 address 2001:1::1/64 + ip address 192.168.1.1/24 +! +interface eth2 vrf vrf10 + ipv6 address 2001:3::1/64 +! +interface eth3 vrf vrf20 + ipv6 address 2001:5::1/64 +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:1:1::/64 block-len 40 node-len 24 func-bits 16 + ! + ! +! +ip forwarding +ipv6 forwarding +! +ipv6 route 2001:db8:2:1::/64 2001::2 +ipv6 route 2001:db8:2:2::/64 2001::2 +ipv6 route 2001:db8:2:3::/64 2001::2 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf new file mode 100644 index 0000000..892a9f7 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf @@ -0,0 +1,80 @@ +frr defaults traditional +! +hostname r2 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug bgp neighbor-events +!debug bgp zebra +!debug bgp vnc verbose +!debug bgp update-groups +!debug bgp updates in +!debug bgp updates out +!debug bgp updates +!debug bgp vpn label +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp vpn rmap-event +! +router bgp 2 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001::1 remote-as 1 + neighbor 2001::1 update-source 2001::2 + neighbor 2001::1 timers 3 10 + neighbor 2001::1 timers connect 1 + neighbor 2001::1 capability extended-nexthop + ! + address-family ipv4 vpn + neighbor 2001::1 activate + exit-address-family + ! + address-family ipv6 vpn + neighbor 2001::1 activate + exit-address-family + ! + segment-routing srv6 + locator loc1 + ! +! +router bgp 2 vrf vrf10 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 2:10 + rt vpn both 99:99 + import vpn + export vpn + redistribute connected + exit-address-family +! + address-family ipv4 unicast + network 192.168.2.0/24 + sid vpn export auto + rd vpn export 22:10 + rt vpn both 77:77 + import vpn + export vpn + redistribute connected + exit-address-family +! +router bgp 2 vrf vrf20 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 2:20 + rt vpn both 88:88 + import vpn + export vpn + redistribute connected + exit-address-family +!! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json new file mode 100644 index 0000000..538e895 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json @@ -0,0 +1,169 @@ +{ + "vrfName": "default", + "tableVersion": 2, + "routerId": "192.0.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json new file mode 100644 index 0000000..446bb8e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json @@ -0,0 +1,106 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 32 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:1:1:2::" + } + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 32 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:1:1:2::" + } + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json new file mode 100644 index 0000000..8bc2fc2 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json @@ -0,0 +1,112 @@ +{ + "2001:4::\/64":[ + { + "prefix":"2001:4::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "2001:5::\/64":[ + { + "prefix":"2001:5::\/64", + "protocol":"bgp", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 48 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:1:1:3::" + } + } + ] + } + ], + "2001:6::\/64":[ + { + "prefix":"2001:6::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf new file mode 100644 index 0000000..9771ee1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf @@ -0,0 +1,43 @@ +log file zebra.log +! +hostname r2 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +interface eth0 + ipv6 address 2001::2/64 +! +interface eth1 vrf vrf10 + ipv6 address 2001:2::1/64 + ip address 192.168.2.1/24 +! +interface eth2 vrf vrf20 + ipv6 address 2001:4::1/64 +! +interface eth3 vrf vrf20 + ipv6 address 2001:6::1/64 +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:2:2::/64 block-len 40 node-len 24 func-bits 16 + ! + ! +! +ip forwarding +ipv6 forwarding +! +ipv6 route 2001:db8:1:1::/64 2001::1 +ipv6 route 2001:db8:1:2::/64 2001::1 +ipv6 route 2001:db8:1:3::/64 2001::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py b/tests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py new file mode 100755 index 0000000..984cf97 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py @@ -0,0 +1,438 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright 2023 6WIND S.A. +# Authored by Dmytro Shytyi +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +import os +import re +import sys +import json +import functools +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import required_linux_kernel_version +from lib.checkping import check_ping + + +def build_topo(tgen): + r""" + CE1 CE3 CE5 + (eth0) (eth0) (eth0) + :2 :2 :2 + | | | + 192.168.1.0 | | + /24 | | + 2001: 2001: 2001: + 1::/64 3::/64 5::/64 + | | | + :1 :1 :1 + +-(eth1)--(eth2)---(eth3)-+ + | \ / | | + | (vrf10) (vrf20) | + | R1 | + +----------(eth0)---------+ + :1 + | + 2001::/64 + | + :2 + (eth0) + +----------(eth0)--------------+ + | R2 | + | (vrf10) (vrf20) | + | / / \ | + +-(eth1)-----(eth2)-----(eth3)-+ + :1 :1 :1 + | | | + +------+ +------+ +------+ + / 2001: \ / 2001: \ / 2001: \ + / 2::/64 \ 4::/64 / \ 6::/64 / + /192.168.2.0| / \ / + \ /24 / \ | | | + +------+ +------+ +------+ + | | | + :2 :2 :2 + (eth0) (eth0) (eth0) + CE2 CE4 CE6 + """ + + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("ce1") + tgen.add_router("ce2") + tgen.add_router("ce3") + tgen.add_router("ce4") + tgen.add_router("ce5") + tgen.add_router("ce6") + + tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0") + tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "eth0", "eth1") + tgen.add_link(tgen.gears["ce2"], tgen.gears["r2"], "eth0", "eth1") + tgen.add_link(tgen.gears["ce3"], tgen.gears["r1"], "eth0", "eth2") + tgen.add_link(tgen.gears["ce4"], tgen.gears["r2"], "eth0", "eth2") + tgen.add_link(tgen.gears["ce5"], tgen.gears["r1"], "eth0", "eth3") + tgen.add_link(tgen.gears["ce6"], tgen.gears["r2"], "eth0", "eth3") + + +def setup_module(mod): + result = required_linux_kernel_version("5.11") + if result is not True: + pytest.skip("Kernel requirements are not met") + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + router_list = tgen.routers() + for i, (rname, router) in enumerate(tgen.routers().items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.gears["r1"].run("modprobe vrf") + tgen.gears["r1"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r1"].run("ip link set vrf10 up") + tgen.gears["r1"].run("ip link add vrf20 type vrf table 20") + tgen.gears["r1"].run("ip link set vrf20 up") + tgen.gears["r1"].run("ip link set eth1 master vrf10") + tgen.gears["r1"].run("ip link set eth2 master vrf10") + tgen.gears["r1"].run("ip link set eth3 master vrf20") + tgen.gears["r1"].run("sysctl net.vrf.strict_mode=1") + tgen.gears["r1"].run("sysctl net.ipv4.conf.default.rp_filter=0") + tgen.gears["r1"].run("sysctl net.ipv4.conf.all.rp_filter=0") + tgen.gears["r1"].run("sysctl net.ipv4.conf.lo.rp_filter=0") + tgen.gears["r1"].run("sysctl net.ipv4.conf.eth0.rp_filter=0") + tgen.gears["r1"].run("sysctl net.ipv4.conf.eth1.rp_filter=0") + tgen.gears["r1"].run("sysctl net.ipv4.conf.vrf10.rp_filter=0") + + tgen.gears["r2"].run("modprobe vrf") + tgen.gears["r2"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r2"].run("ip link set vrf10 up") + tgen.gears["r2"].run("ip link add vrf20 type vrf table 20") + tgen.gears["r2"].run("ip link set vrf20 up") + tgen.gears["r2"].run("ip link set eth1 master vrf10") + tgen.gears["r2"].run("ip link set eth2 master vrf20") + tgen.gears["r2"].run("ip link set eth3 master vrf20") + tgen.gears["r2"].run("sysctl net.vrf.strict_mode=1") + tgen.gears["r2"].run("sysctl net.ipv4.conf.default.rp_filter=0") + tgen.gears["r2"].run("sysctl net.ipv4.conf.all.rp_filter=0") + tgen.gears["r2"].run("sysctl net.ipv4.conf.lo.rp_filter=0") + tgen.gears["r2"].run("sysctl net.ipv4.conf.eth0.rp_filter=0") + tgen.gears["r2"].run("sysctl net.ipv4.conf.eth1.rp_filter=0") + tgen.gears["r2"].run("sysctl net.ipv4.conf.vrf10.rp_filter=0") + tgen.start_router() + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + # Example: + # tgen=get_topogen() + # tgen.mininet_cli() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def check_rib(name, cmd, expected_file): + def _check(name, cmd, expected_file): + logger.info("polling") + tgen = get_topogen() + router = tgen.gears[name] + output = json.loads(router.vtysh_cmd(cmd)) + expected = open_json_file("{}/{}".format(CWD, expected_file)) + return topotest.json_cmp(output, expected) + + logger.info('[+] check {} "{}" {}'.format(name, cmd, expected_file)) + tgen = get_topogen() + func = functools.partial(_check, name, cmd, expected_file) + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "Failed" + + +def test_rib(): + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json") + check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_rib.json") + check_rib("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20_rib.json") + check_rib("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10_rib.json") + check_rib("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20_rib.json") + check_rib("ce1", "show ipv6 route json", "ce1/ipv6_rib.json") + check_rib("ce2", "show ipv6 route json", "ce2/ipv6_rib.json") + check_rib("ce3", "show ipv6 route json", "ce3/ipv6_rib.json") + check_rib("ce4", "show ipv6 route json", "ce4/ipv6_rib.json") + check_rib("ce5", "show ipv6 route json", "ce5/ipv6_rib.json") + check_rib("ce6", "show ipv6 route json", "ce6/ipv6_rib.json") + + +def test_ping(): + check_ping("ce1", "2001:2::2", True, 10, 0.5) + check_ping("ce1", "2001:3::2", True, 10, 0.5) + check_ping("ce1", "2001:4::2", False, 10, 0.5) + check_ping("ce1", "2001:5::2", False, 10, 0.5) + check_ping("ce1", "2001:6::2", False, 10, 0.5) + check_ping("ce4", "2001:1::2", False, 10, 0.5) + check_ping("ce4", "2001:2::2", False, 10, 0.5) + check_ping("ce4", "2001:3::2", False, 10, 0.5) + check_ping("ce4", "2001:5::2", True, 10, 0.5) + check_ping("ce4", "2001:6::2", True, 10, 0.5) + + +def test_sid_per_afv6_auto(): + check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_sid_rib.json") + check_ping("ce1", "2001:2::2", True, 10, 0.5) + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv6 unicast + no sid vpn export auto + """ + ) + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False, 10, 0.5) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv6 unicast + sid vpn export auto + """ + ) + check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_sid_rib.json") + check_ping("ce1", "2001:2::2", True, 10, 0.5) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv6 unicast + no sid vpn export auto + """ + ) + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False, 10, 0.5) + + +def test_sid_per_afv6_manual(): + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False, 10, 0.5) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv6 unicast + sid vpn export 8 + """ + ) + + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", True, 10, 0.5) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv6 unicast + no sid vpn export 8 + """ + ) + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False, 10, 0.5) + + +def test_sid_per_afv4_auto(): + check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_sid_rib.json") + check_ping("ce1", "192.168.2.2", True, 10, 0.5) + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv4 unicast + no sid vpn export auto + """ + ) + + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_no_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", False, 10, 0.5) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv4 unicast + sid vpn export auto + """ + ) + + check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_sid_rib.json") + check_ping("ce1", "192.168.2.2", True, 10, 0.5) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv4 unicast + no sid vpn export auto + """ + ) + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_no_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", False, 10, 0.5) + + +def test_sid_per_afv4_manual(): + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_no_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", False, 10, 0.5) + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv4 unicast + sid vpn export 8 + """ + ) + + check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_sid_rib.json") + check_ping("ce1", "192.168.2.2", True, 10, 0.5) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv4 unicast + no sid vpn export 8 + """ + ) + check_ping("ce1", "192.168.2.2", False, 10, 0.5) + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_no_sid_rib.json" + ) + + +def test_sid_per_vrf_auto(): + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf_auto_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False, 10, 0.5) + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + sid vpn per-vrf export auto + """ + ) + + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf6_auto_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", True, 10, 0.5) + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf4_auto_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", True, 10, 0.5) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + no sid vpn per-vrf export auto + """ + ) + + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf_auto_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False, 10, 0.5) + + +def test_sid_per_vrf_manual(): + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf_manual_no_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", False, 10, 0.5) + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + sid vpn per-vrf export 8 + """ + ) + + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf6_manual_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", True, 10, 0.5) + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf4_manual_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", True, 10, 0.5) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + no sid vpn per-vrf export 8 + """ + ) + + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf_manual_no_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", False, 10, 0.5) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/bgpd.conf new file mode 100644 index 0000000..3459796 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce1 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/ipv6_rib.json new file mode 100644 index 0000000..d19e315 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:1::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:1::/64": [ + { + "prefix": "2001:1::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/zebra.conf new file mode 100644 index 0000000..665808a --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce1 +! +interface eth0 + ipv6 address 2001:1::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:1::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/bgpd.conf new file mode 100644 index 0000000..8ed9978 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce2 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/ipv6_rib.json new file mode 100644 index 0000000..35ff14e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:2::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:2::/64": [ + { + "prefix": "2001:2::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/zebra.conf new file mode 100644 index 0000000..cc9b90a --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce2 +! +interface eth0 + ipv6 address 2001:2::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:2::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/bgpd.conf new file mode 100644 index 0000000..a85d970 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce3 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/ipv6_rib.json new file mode 100644 index 0000000..2f2931f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:3::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:3::/64": [ + { + "prefix": "2001:3::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/zebra.conf new file mode 100644 index 0000000..beca0b1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce3 +! +interface eth0 + ipv6 address 2001:3::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:3::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/bgpd.conf new file mode 100644 index 0000000..93fb32f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce4 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/ipv6_rib.json new file mode 100644 index 0000000..8a98768 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:4::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:4::/64": [ + { + "prefix": "2001:4::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/zebra.conf new file mode 100644 index 0000000..7b21074 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce4 +! +interface eth0 + ipv6 address 2001:4::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:4::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/bgpd.conf new file mode 100644 index 0000000..2ab6f2d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce5 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/ipv6_rib.json new file mode 100644 index 0000000..80ff52a --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:5::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:5::/64": [ + { + "prefix": "2001:5::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/zebra.conf new file mode 100644 index 0000000..b5ad48e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce5 +! +interface eth0 + ipv6 address 2001:5::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:5::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/bgpd.conf new file mode 100644 index 0000000..e0b6540 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce6 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/ipv6_rib.json new file mode 100644 index 0000000..ace6136 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:6::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:6::/64": [ + { + "prefix": "2001:6::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/zebra.conf new file mode 100644 index 0000000..7d19d98 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce6 +! +interface eth0 + ipv6 address 2001:6::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:6::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf new file mode 100644 index 0000000..d113db1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf @@ -0,0 +1,66 @@ +frr defaults traditional +! +bgp send-extra-data zebra +! +hostname r1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug bgp neighbor-events +!debug bgp zebra +!debug bgp vnc verbose +!debug bgp update-groups +!debug bgp updates in +!debug bgp updates out +!debug bgp vpn label +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp vpn rmap-event +! +router bgp 1 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001::2 remote-as 2 + neighbor 2001::2 timers 3 10 + neighbor 2001::2 timers connect 1 + ! + address-family ipv6 vpn + neighbor 2001::2 activate + exit-address-family + ! + segment-routing srv6 + locator loc1 + ! +! +router bgp 1 vrf vrf10 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 1:10 + rt vpn both 99:99 + import vpn + export vpn + redistribute connected + exit-address-family +! +router bgp 1 vrf vrf20 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 1:20 + rt vpn both 88:88 + import vpn + export vpn + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib.json new file mode 100644 index 0000000..25b7a86 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib.json @@ -0,0 +1,170 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 2, + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json new file mode 100644 index 0000000..f2df9be --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json @@ -0,0 +1,160 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json new file mode 100644 index 0000000..0fdd3d6 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json @@ -0,0 +1,169 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json new file mode 100644 index 0000000..141c1cb --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json @@ -0,0 +1,86 @@ +{ + "2001:1::/64": [ + { + "prefix": "2001:1::/64", + "protocol": "connected", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth1", + "active": true + } + ] + } + ], + "2001:2::/64": [ + { + "prefix": "2001:2::/64", + "protocol": "bgp", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:2:2:100::" + } + } + ], + "asPath": "2" + } + ], + "2001:3::/64": [ + { + "prefix": "2001:3::/64", + "protocol": "connected", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth2", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json new file mode 100644 index 0000000..e209980 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json @@ -0,0 +1,92 @@ +{ + "2001:4::/64": [ + { + "prefix": "2001:4::/64", + "protocol": "bgp", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:2:2:200::" + } + } + ], + "asPath": "2" + } + ], + "2001:5::/64": [ + { + "prefix": "2001:5::/64", + "protocol": "connected", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth3", + "active": true + } + ] + } + ], + "2001:6::/64": [ + { + "prefix": "2001:6::/64", + "protocol": "bgp", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:2:2:200::" + } + } + ], + "asPath": "2" + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf new file mode 100644 index 0000000..8ccf7a2 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf @@ -0,0 +1,42 @@ +log file zebra.log +! +hostname r1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +! debug zebra packet +! debug zebra dplane +! debug zebra kernel +! +interface eth0 + ipv6 address 2001::1/64 +! +interface eth1 vrf vrf10 + ipv6 address 2001:1::1/64 +! +interface eth2 vrf vrf10 + ipv6 address 2001:3::1/64 +! +interface eth3 vrf vrf20 + ipv6 address 2001:5::1/64 +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:1:1::/64 func-bits 8 + ! + ! +! +ip forwarding +ipv6 forwarding +! +ipv6 route 2001:db8:2:1::/64 2001::2 +ipv6 route 2001:db8:2:2::/64 2001::2 +ipv6 route 2001:db8:2:3::/64 2001::2 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf new file mode 100644 index 0000000..9a7831d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf @@ -0,0 +1,67 @@ +frr defaults traditional +! +bgp send-extra-data zebra +! +hostname r2 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug bgp neighbor-events +!debug bgp zebra +!debug bgp vnc verbose +!debug bgp update-groups +!debug bgp updates in +!debug bgp updates out +!debug bgp updates +!debug bgp vpn label +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp vpn rmap-event +! +router bgp 2 + bgp router-id 2.2.2.2 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001::1 remote-as 1 + neighbor 2001::1 timers 3 10 + neighbor 2001::1 timers connect 1 + ! + address-family ipv6 vpn + neighbor 2001::1 activate + exit-address-family + ! + segment-routing srv6 + locator loc1 + ! +! +router bgp 2 vrf vrf10 + bgp router-id 2.2.2.2 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 2:10 + rt vpn both 99:99 + import vpn + export vpn + redistribute connected + exit-address-family +! +router bgp 2 vrf vrf20 + bgp router-id 2.2.2.2 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 2:20 + rt vpn both 88:88 + import vpn + export vpn + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib.json new file mode 100644 index 0000000..2cd47b9 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib.json @@ -0,0 +1,170 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 2, + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json new file mode 100644 index 0000000..25cdf03 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json @@ -0,0 +1,93 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json new file mode 100644 index 0000000..03bbcc0 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json @@ -0,0 +1,169 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json new file mode 100644 index 0000000..7f8a930 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json @@ -0,0 +1,92 @@ +{ + "2001:1::/64": [ + { + "prefix": "2001:1::/64", + "protocol": "bgp", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:1:1:100::" + } + } + ], + "asPath": "1" + } + ], + "2001:2::/64": [ + { + "prefix": "2001:2::/64", + "protocol": "connected", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth1", + "active": true + } + ] + } + ], + "2001:3::/64": [ + { + "prefix": "2001:3::/64", + "protocol": "bgp", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:1:1:100::" + } + } + ], + "asPath": "1" + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json new file mode 100644 index 0000000..104bdc3 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json @@ -0,0 +1,86 @@ +{ + "2001:4::/64": [ + { + "prefix": "2001:4::/64", + "protocol": "connected", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth2", + "active": true + } + ] + } + ], + "2001:5::/64": [ + { + "prefix": "2001:5::/64", + "protocol": "bgp", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:1:1:200::" + } + } + ], + "asPath": "1" + } + ], + "2001:6::/64": [ + { + "prefix": "2001:6::/64", + "protocol": "connected", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth3", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf new file mode 100644 index 0000000..839454d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf @@ -0,0 +1,42 @@ +log file zebra.log +! +hostname r2 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +! debug zebra packet +! debug zebra dplane +! debug zebra kernel +! +interface eth0 + ipv6 address 2001::2/64 +! +interface eth1 vrf vrf10 + ipv6 address 2001:2::1/64 +! +interface eth2 vrf vrf20 + ipv6 address 2001:4::1/64 +! +interface eth3 vrf vrf20 + ipv6 address 2001:6::1/64 +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:2:2::/64 func-bits 8 + ! + ! +! +ip forwarding +ipv6 forwarding +! +ipv6 route 2001:db8:1:1::/64 2001::1 +ipv6 route 2001:db8:1:2::/64 2001::1 +ipv6 route 2001:db8:1:3::/64 2001::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py new file mode 100755 index 0000000..4afaeaf --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright (c) 2018, LabN Consulting, L.L.C. +# Authored by Lou Berger +# + +import os +import sys +import json +import functools +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import required_linux_kernel_version +from lib.checkping import check_ping + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + r""" + CE1 CE3 CE5 + (eth0) (eth0) (eth0) + :2 :2 :2 + | | | + 2001: 2001: 2001: + 1::/64 3::/64 5::/64 + | | | + :1 :1 :1 + +-(eth1)--(eth2)---(eth3)-+ + | \ / | | + | (vrf10) (vrf20) | + | R1 | + +----------(eth0)---------+ + :1 + | + 2001::/64 + | + :2 + (eth0) + +----------(eth0)--------------+ + | R2 | + | (vrf10) (vrf20) | + | / / \ | + +-(eth1)-----(eth2)-----(eth3)-+ + :1 :1 :1 + | | | + +------+ +------+ +------+ + / 2001: \ / 2001: \ / 2001: \ + \ 2::/64 / \ 4::/64 / \ 6::/64 / + +------+ +------+ +------+ + | | | + :2 :2 :2 + (eth0) (eth0) (eth0) + CE2 CE4 CE6 + """ + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("ce1") + tgen.add_router("ce2") + tgen.add_router("ce3") + tgen.add_router("ce4") + tgen.add_router("ce5") + tgen.add_router("ce6") + + tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0") + tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "eth0", "eth1") + tgen.add_link(tgen.gears["ce2"], tgen.gears["r2"], "eth0", "eth1") + tgen.add_link(tgen.gears["ce3"], tgen.gears["r1"], "eth0", "eth2") + tgen.add_link(tgen.gears["ce4"], tgen.gears["r2"], "eth0", "eth2") + tgen.add_link(tgen.gears["ce5"], tgen.gears["r1"], "eth0", "eth3") + tgen.add_link(tgen.gears["ce6"], tgen.gears["r2"], "eth0", "eth3") + + +def setup_module(mod): + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + router_list = tgen.routers() + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname)) + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.gears["r1"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r1"].run("ip link set vrf10 up") + tgen.gears["r1"].run("ip link add vrf20 type vrf table 20") + tgen.gears["r1"].run("ip link set vrf20 up") + tgen.gears["r1"].run("ip link set eth1 master vrf10") + tgen.gears["r1"].run("ip link set eth2 master vrf10") + tgen.gears["r1"].run("ip link set eth3 master vrf20") + + tgen.gears["r2"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r2"].run("ip link set vrf10 up") + tgen.gears["r2"].run("ip link add vrf20 type vrf table 20") + tgen.gears["r2"].run("ip link set vrf20 up") + tgen.gears["r2"].run("ip link set eth1 master vrf10") + tgen.gears["r2"].run("ip link set eth2 master vrf20") + tgen.gears["r2"].run("ip link set eth3 master vrf20") + tgen.start_router() + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def check_rib(name, cmd, expected_file): + def _check(name, cmd, expected_file): + logger.info("polling") + tgen = get_topogen() + router = tgen.gears[name] + output = json.loads(router.vtysh_cmd(cmd)) + expected = open_json_file("{}/{}".format(CWD, expected_file)) + return topotest.json_cmp(output, expected) + + logger.info('[+] check {} "{}" {}'.format(name, cmd, expected_file)) + tgen = get_topogen() + func = functools.partial(_check, name, cmd, expected_file) + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "Failed" + + +def test_rib(): + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json") + check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_rib.json") + check_rib("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20_rib.json") + check_rib("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10_rib.json") + check_rib("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20_rib.json") + check_rib("ce1", "show ipv6 route json", "ce1/ipv6_rib.json") + check_rib("ce2", "show ipv6 route json", "ce2/ipv6_rib.json") + check_rib("ce3", "show ipv6 route json", "ce3/ipv6_rib.json") + check_rib("ce4", "show ipv6 route json", "ce4/ipv6_rib.json") + check_rib("ce5", "show ipv6 route json", "ce5/ipv6_rib.json") + check_rib("ce6", "show ipv6 route json", "ce6/ipv6_rib.json") + + +def test_ping(): + check_ping("ce1", "2001:2::2", True, 10, 1) + check_ping("ce1", "2001:3::2", True, 10, 1) + check_ping("ce1", "2001:4::2", False, 10, 1) + check_ping("ce1", "2001:5::2", False, 10, 1) + check_ping("ce1", "2001:6::2", False, 10, 1) + check_ping("ce4", "2001:1::2", False, 10, 1) + check_ping("ce4", "2001:2::2", False, 10, 1) + check_ping("ce4", "2001:3::2", False, 10, 1) + check_ping("ce4", "2001:5::2", True, 10, 1) + check_ping("ce4", "2001:6::2", True, 10, 1) + + +def test_locator_delete(): + check_ping("ce1", "2001:2::2", True, 10, 1) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + no locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") + check_ping("ce1", "2001:2::2", False, 10, 1) + + +def test_locator_recreate(): + check_ping("ce1", "2001:2::2", False, 10, 1) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:1:1::/64 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") + check_ping("ce1", "2001:2::2", True, 10, 1) + + +def test_bgp_locator_unset(): + check_ping("ce1", "2001:2::2", True, 10, 1) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + segment-routing srv6 + no locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") + check_ping("ce1", "2001:2::2", False, 10, 1) + + +def test_bgp_locator_reset(): + check_ping("ce1", "2001:2::2", False, 10, 1) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + segment-routing srv6 + locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") + check_ping("ce1", "2001:2::2", True, 10, 1) + + +def test_bgp_srv6_unset(): + check_ping("ce1", "2001:2::2", True, 10, 1) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + no segment-routing srv6 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") + check_ping("ce1", "2001:2::2", False, 10, 1) + + +def test_bgp_srv6_reset(): + check_ping("ce1", "2001:2::2", False, 10, 1) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + segment-routing srv6 + locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") + check_ping("ce1", "2001:2::2", True, 10, 1) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/bgpd.conf new file mode 100644 index 0000000..3459796 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce1 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/ip_rib.json new file mode 100644 index 0000000..1d33fee --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/ip_rib.json @@ -0,0 +1,58 @@ +{ + "0.0.0.0/0": [ + { + "prefix": "0.0.0.0/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "192.168.1.1", + "afi": "ipv4", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "192.168.1.0/24": [ + { + "prefix": "192.168.1.0/24", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/zebra.conf new file mode 100644 index 0000000..447d1b4 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce1 +! +interface eth0 + ip address 192.168.1.2/24 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.1.1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/bgpd.conf new file mode 100644 index 0000000..8ed9978 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce2 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/ip_rib.json new file mode 100644 index 0000000..a21f4a1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/ip_rib.json @@ -0,0 +1,58 @@ +{ + "0.0.0.0/0": [ + { + "prefix": "0.0.0.0/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "192.168.2.1", + "afi": "ipv4", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "192.168.2.0/24": [ + { + "prefix": "192.168.2.0/24", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/zebra.conf new file mode 100644 index 0000000..1165225 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce2 +! +interface eth0 + ip address 192.168.2.2/24 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.2.1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/bgpd.conf new file mode 100644 index 0000000..a85d970 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce3 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/ip_rib.json new file mode 100644 index 0000000..38a7807 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/ip_rib.json @@ -0,0 +1,58 @@ +{ + "0.0.0.0/0": [ + { + "prefix": "0.0.0.0/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "192.168.3.1", + "afi": "ipv4", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "192.168.3.0/24": [ + { + "prefix": "192.168.3.0/24", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/zebra.conf new file mode 100644 index 0000000..299c659 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce3 +! +interface eth0 + ip address 192.168.3.2/24 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.3.1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/bgpd.conf new file mode 100644 index 0000000..93fb32f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce4 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/ip_rib.json new file mode 100644 index 0000000..a0be78e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/ip_rib.json @@ -0,0 +1,58 @@ +{ + "0.0.0.0/0": [ + { + "prefix": "0.0.0.0/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "192.168.4.1", + "afi": "ipv4", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "192.168.4.0/24": [ + { + "prefix": "192.168.4.0/24", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/zebra.conf new file mode 100644 index 0000000..30f3736 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce4 +! +interface eth0 + ip address 192.168.4.2/24 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.4.1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/bgpd.conf new file mode 100644 index 0000000..2ab6f2d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce5 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/ip_rib.json new file mode 100644 index 0000000..dc338d5 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/ip_rib.json @@ -0,0 +1,58 @@ +{ + "0.0.0.0/0": [ + { + "prefix": "0.0.0.0/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "192.168.5.1", + "afi": "ipv4", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "192.168.5.0/24": [ + { + "prefix": "192.168.5.0/24", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/zebra.conf new file mode 100644 index 0000000..208dcb1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce5 +! +interface eth0 + ip address 192.168.5.2/24 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.5.1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/bgpd.conf new file mode 100644 index 0000000..e0b6540 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce6 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/ip_rib.json new file mode 100644 index 0000000..4a603a5 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/ip_rib.json @@ -0,0 +1,58 @@ +{ + "0.0.0.0/0": [ + { + "prefix": "0.0.0.0/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "192.168.6.1", + "afi": "ipv4", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "192.168.6.0/24": [ + { + "prefix": "192.168.6.0/24", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/zebra.conf new file mode 100644 index 0000000..d68a008 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce6 +! +interface eth0 + ip address 192.168.6.2/24 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.6.1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf new file mode 100644 index 0000000..30a0f8f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf @@ -0,0 +1,68 @@ +frr defaults traditional +! +bgp send-extra-data zebra +! +hostname r1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug bgp neighbor-events +!debug bgp zebra +!debug bgp vnc verbose +!debug bgp update-groups +!debug bgp updates in +!debug bgp updates out +!debug bgp vpn label +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp vpn rmap-event +! +router bgp 1 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001::2 remote-as 2 + neighbor 2001::2 timers 3 10 + neighbor 2001::2 timers connect 1 + neighbor 2001::2 capability extended-nexthop + ! + address-family ipv4 vpn + neighbor 2001::2 activate + exit-address-family + ! + segment-routing srv6 + locator loc1 + ! +! +router bgp 1 vrf vrf10 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + ! + address-family ipv4 unicast + sid vpn export auto + nexthop vpn export 2001::1 + rd vpn export 1:10 + rt vpn both 99:99 + import vpn + export vpn + redistribute connected + ! + exit-address-family +! +router bgp 1 vrf vrf20 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + ! + address-family ipv4 unicast + sid vpn export auto + nexthop vpn export 2001::1 + rd vpn export 1:20 + rt vpn both 88:88 + import vpn + export vpn + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json new file mode 100644 index 0000000..3cc2fdd --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json @@ -0,0 +1,167 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 2, + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "192.168.1.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.1.0", + "prefixLen": 24, + "network": "192.168.1.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.3.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.3.0", + "prefixLen": 24, + "network": "192.168.3.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "192.168.5.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.5.0", + "prefixLen": 24, + "network": "192.168.5.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "192.168.2.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.2.0", + "prefixLen": 24, + "network": "192.168.2.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "192.168.4.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.4.0", + "prefixLen": 24, + "network": "192.168.4.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.6.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.6.0", + "prefixLen": 24, + "network": "192.168.6.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf10_rib.json new file mode 100644 index 0000000..8daa9b1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf10_rib.json @@ -0,0 +1,86 @@ +{ + "192.168.1.0/24": [ + { + "prefix": "192.168.1.0/24", + "protocol": "connected", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth1", + "active": true + } + ] + } + ], + "192.168.2.0/24": [ + { + "prefix": "192.168.2.0/24", + "protocol": "bgp", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:2:2:100::" + } + } + ], + "asPath": "2" + } + ], + "192.168.3.0/24": [ + { + "prefix": "192.168.3.0/24", + "protocol": "connected", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth2", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf20_rib.json new file mode 100644 index 0000000..6f123cf --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf20_rib.json @@ -0,0 +1,92 @@ +{ + "192.168.4.0/24": [ + { + "prefix": "192.168.4.0/24", + "protocol": "bgp", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:2:2:200::" + } + } + ], + "asPath": "2" + } + ], + "192.168.5.0/24": [ + { + "prefix": "192.168.5.0/24", + "protocol": "connected", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth3", + "active": true + } + ] + } + ], + "192.168.6.0/24": [ + { + "prefix": "192.168.6.0/24", + "protocol": "bgp", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:2:2:200::" + } + } + ], + "asPath": "2" + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf new file mode 100644 index 0000000..cbc5ce1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf @@ -0,0 +1,41 @@ +log file zebra.log +! +hostname r1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +interface eth0 + ipv6 address 2001::1/64 +! +interface eth1 vrf vrf10 + ip address 192.168.1.1/24 + ipv6 address 2001:1::1/64 +! +interface eth2 vrf vrf10 + ip address 192.168.3.1/24 +! +interface eth3 vrf vrf20 + ip address 192.168.5.1/24 +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:1:1::/64 func-bits 8 + ! + ! +! +ip forwarding +ipv6 forwarding +! +ipv6 route 2001:db8:2:2::/64 2001::2 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf new file mode 100644 index 0000000..7ca2300 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf @@ -0,0 +1,68 @@ +frr defaults traditional +! +bgp send-extra-data zebra +! +hostname r2 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug bgp neighbor-events +!debug bgp zebra +!debug bgp vnc verbose +!debug bgp update-groups +!debug bgp updates in +!debug bgp updates out +!debug bgp updates +!debug bgp vpn label +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp vpn rmap-event +! +router bgp 2 + bgp router-id 2.2.2.2 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001::1 remote-as 1 + neighbor 2001::1 timers 3 10 + neighbor 2001::1 timers connect 1 + neighbor 2001::1 capability extended-nexthop + ! + address-family ipv4 vpn + neighbor 2001::1 activate + exit-address-family + ! + segment-routing srv6 + locator loc1 + ! +! +router bgp 2 vrf vrf10 + bgp router-id 2.2.2.2 + no bgp ebgp-requires-policy + ! + address-family ipv4 unicast + sid vpn export auto + nexthop vpn export 2001::2 + rd vpn export 2:10 + rt vpn both 99:99 + import vpn + export vpn + redistribute connected + exit-address-family +! +router bgp 2 vrf vrf20 + bgp router-id 2.2.2.2 + no bgp ebgp-requires-policy + ! + address-family ipv4 unicast + sid vpn export auto + nexthop vpn export 2001::2 + rd vpn export 2:20 + rt vpn both 88:88 + import vpn + export vpn + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json new file mode 100644 index 0000000..9557054 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json @@ -0,0 +1,167 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 2, + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:10": { + "192.168.1.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.1.0", + "prefixLen": 24, + "network": "192.168.1.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.3.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.3.0", + "prefixLen": 24, + "network": "192.168.3.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "192.168.5.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.5.0", + "prefixLen": 24, + "network": "192.168.5.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "192.168.2.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.2.0", + "prefixLen": 24, + "network": "192.168.2.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "192.168.4.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.4.0", + "prefixLen": 24, + "network": "192.168.4.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.6.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.6.0", + "prefixLen": 24, + "network": "192.168.6.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf10_rib.json new file mode 100644 index 0000000..6268031 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf10_rib.json @@ -0,0 +1,92 @@ +{ + "192.168.1.0/24": [ + { + "prefix": "192.168.1.0/24", + "protocol": "bgp", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:1:1:100::" + } + } + ], + "asPath": "1" + } + ], + "192.168.2.0/24": [ + { + "prefix": "192.168.2.0/24", + "protocol": "connected", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth1", + "active": true + } + ] + } + ], + "192.168.3.0/24": [ + { + "prefix": "192.168.3.0/24", + "protocol": "bgp", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:1:1:100::" + } + } + ], + "asPath": "1" + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf20_rib.json new file mode 100644 index 0000000..ffe2e07 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf20_rib.json @@ -0,0 +1,86 @@ +{ + "192.168.4.0/24": [ + { + "prefix": "192.168.4.0/24", + "protocol": "connected", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth2", + "active": true + } + ] + } + ], + "192.168.5.0/24": [ + { + "prefix": "192.168.5.0/24", + "protocol": "bgp", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:1:1:200::" + } + } + ], + "asPath": "1" + } + ], + "192.168.6.0/24": [ + { + "prefix": "192.168.6.0/24", + "protocol": "connected", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth3", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf new file mode 100644 index 0000000..449ca74 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf @@ -0,0 +1,40 @@ +log file zebra.log +! +hostname r2 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +interface eth0 + ipv6 address 2001::2/64 +! +interface eth1 vrf vrf10 + ip address 192.168.2.1/24 +! +interface eth2 vrf vrf20 + ip address 192.168.4.1/24 +! +interface eth3 vrf vrf20 + ip address 192.168.6.1/24 +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:2:2::/64 func-bits 8 + ! + ! +! +ip forwarding +ipv6 forwarding +! +ipv6 route 2001:db8:1:1::/64 2001::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/test_bgp_srv6l3vpn_to_bgp_vrf2.py b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/test_bgp_srv6l3vpn_to_bgp_vrf2.py new file mode 100755 index 0000000..914c29f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/test_bgp_srv6l3vpn_to_bgp_vrf2.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright (c) 2018, LabN Consulting, L.L.C. +# Authored by Lou Berger +# + +import os +import re +import sys +import json +import functools +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import required_linux_kernel_version +from lib.checkping import check_ping + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("ce1") + tgen.add_router("ce2") + tgen.add_router("ce3") + tgen.add_router("ce4") + tgen.add_router("ce5") + tgen.add_router("ce6") + + tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0") + tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "eth0", "eth1") + tgen.add_link(tgen.gears["ce2"], tgen.gears["r2"], "eth0", "eth1") + tgen.add_link(tgen.gears["ce3"], tgen.gears["r1"], "eth0", "eth2") + tgen.add_link(tgen.gears["ce4"], tgen.gears["r2"], "eth0", "eth2") + tgen.add_link(tgen.gears["ce5"], tgen.gears["r1"], "eth0", "eth3") + tgen.add_link(tgen.gears["ce6"], tgen.gears["r2"], "eth0", "eth3") + + +def setup_module(mod): + result = required_linux_kernel_version("5.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname)) + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.gears["r1"].run("sysctl net.vrf.strict_mode=1") + tgen.gears["r1"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r1"].run("ip link set vrf10 up") + tgen.gears["r1"].run("ip link add vrf20 type vrf table 20") + tgen.gears["r1"].run("ip link set vrf20 up") + tgen.gears["r1"].run("ip link set eth1 master vrf10") + tgen.gears["r1"].run("ip link set eth2 master vrf10") + tgen.gears["r1"].run("ip link set eth3 master vrf20") + + tgen.gears["r2"].run("sysctl net.vrf.strict_mode=1") + tgen.gears["r2"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r2"].run("ip link set vrf10 up") + tgen.gears["r2"].run("ip link add vrf20 type vrf table 20") + tgen.gears["r2"].run("ip link set vrf20 up") + tgen.gears["r2"].run("ip link set eth1 master vrf10") + tgen.gears["r2"].run("ip link set eth2 master vrf20") + tgen.gears["r2"].run("ip link set eth3 master vrf20") + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def check_rib(name, cmd, expected_file): + def _check(name, dest_addr, match): + logger.info("polling") + tgen = get_topogen() + router = tgen.gears[name] + output = json.loads(router.vtysh_cmd(cmd)) + expected = open_json_file("{}/{}".format(CWD, expected_file)) + return topotest.json_cmp(output, expected) + + logger.info('[+] check {} "{}" {}'.format(name, cmd, expected_file)) + tgen = get_topogen() + func = functools.partial(_check, name, cmd, expected_file) + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "Failed" + + +def test_rib(): + check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib.json") + check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib.json") + check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_rib.json") + check_rib("r1", "show ip route vrf vrf20 json", "r1/vrf20_rib.json") + check_rib("r2", "show ip route vrf vrf10 json", "r2/vrf10_rib.json") + check_rib("r2", "show ip route vrf vrf20 json", "r2/vrf20_rib.json") + check_rib("ce1", "show ip route json", "ce1/ip_rib.json") + check_rib("ce2", "show ip route json", "ce2/ip_rib.json") + check_rib("ce3", "show ip route json", "ce3/ip_rib.json") + check_rib("ce4", "show ip route json", "ce4/ip_rib.json") + check_rib("ce5", "show ip route json", "ce5/ip_rib.json") + check_rib("ce6", "show ip route json", "ce6/ip_rib.json") + + +def test_ping(): + check_ping("ce1", "192.168.2.2", True, 10, 0.5) + check_ping("ce1", "192.168.3.2", True, 10, 0.5) + check_ping("ce1", "192.168.4.2", False, 10, 0.5) + check_ping("ce1", "192.168.5.2", False, 10, 0.5) + check_ping("ce1", "192.168.6.2", False, 10, 0.5) + check_ping("ce4", "192.168.1.2", False, 10, 0.5) + check_ping("ce4", "192.168.2.2", False, 10, 0.5) + check_ping("ce4", "192.168.3.2", False, 10, 0.5) + check_ping("ce4", "192.168.5.2", True, 10, 0.5) + check_ping("ce4", "192.168.6.2", True, 10, 0.5) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/bgpd.conf new file mode 100644 index 0000000..3459796 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce1 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/ip_rib.json new file mode 100644 index 0000000..1d33fee --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/ip_rib.json @@ -0,0 +1,58 @@ +{ + "0.0.0.0/0": [ + { + "prefix": "0.0.0.0/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "192.168.1.1", + "afi": "ipv4", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "192.168.1.0/24": [ + { + "prefix": "192.168.1.0/24", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/ipv6_rib.json new file mode 100644 index 0000000..d19e315 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:1::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:1::/64": [ + { + "prefix": "2001:1::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/zebra.conf new file mode 100644 index 0000000..58e851d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/zebra.conf @@ -0,0 +1,16 @@ +log file zebra.log +! +hostname ce1 +! +interface eth0 + ip address 192.168.1.2/24 + ipv6 address 2001:1::2/64 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.1.1 +ipv6 route ::/0 2001:1::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/bgpd.conf new file mode 100644 index 0000000..8ed9978 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce2 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/ip_rib.json new file mode 100644 index 0000000..a21f4a1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/ip_rib.json @@ -0,0 +1,58 @@ +{ + "0.0.0.0/0": [ + { + "prefix": "0.0.0.0/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "192.168.2.1", + "afi": "ipv4", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "192.168.2.0/24": [ + { + "prefix": "192.168.2.0/24", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/ipv6_rib.json new file mode 100644 index 0000000..35ff14e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:2::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:2::/64": [ + { + "prefix": "2001:2::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/zebra.conf new file mode 100644 index 0000000..0612c53 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/zebra.conf @@ -0,0 +1,16 @@ +log file zebra.log +! +hostname ce2 +! +interface eth0 + ip address 192.168.2.2/24 + ipv6 address 2001:2::2/64 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.2.1 +ipv6 route ::/0 2001:2::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/bgpd.conf new file mode 100644 index 0000000..a85d970 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce3 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/ip_rib.json new file mode 100644 index 0000000..38a7807 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/ip_rib.json @@ -0,0 +1,58 @@ +{ + "0.0.0.0/0": [ + { + "prefix": "0.0.0.0/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "192.168.3.1", + "afi": "ipv4", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "192.168.3.0/24": [ + { + "prefix": "192.168.3.0/24", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/ipv6_rib.json new file mode 100644 index 0000000..2f2931f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:3::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:3::/64": [ + { + "prefix": "2001:3::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/zebra.conf new file mode 100644 index 0000000..d08fdad --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/zebra.conf @@ -0,0 +1,16 @@ +log file zebra.log +! +hostname ce3 +! +interface eth0 + ip address 192.168.3.2/24 + ipv6 address 2001:3::2/64 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.3.1 +ipv6 route ::/0 2001:3::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/bgpd.conf new file mode 100644 index 0000000..93fb32f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce4 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/ip_rib.json new file mode 100644 index 0000000..a0be78e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/ip_rib.json @@ -0,0 +1,58 @@ +{ + "0.0.0.0/0": [ + { + "prefix": "0.0.0.0/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "192.168.4.1", + "afi": "ipv4", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "192.168.4.0/24": [ + { + "prefix": "192.168.4.0/24", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/ipv6_rib.json new file mode 100644 index 0000000..8a98768 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:4::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:4::/64": [ + { + "prefix": "2001:4::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/zebra.conf new file mode 100644 index 0000000..897ed46 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/zebra.conf @@ -0,0 +1,16 @@ +log file zebra.log +! +hostname ce4 +! +interface eth0 + ip address 192.168.4.2/24 + ipv6 address 2001:4::2/64 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.4.1 +ipv6 route ::/0 2001:4::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/bgpd.conf new file mode 100644 index 0000000..2ab6f2d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce5 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/ip_rib.json new file mode 100644 index 0000000..dc338d5 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/ip_rib.json @@ -0,0 +1,58 @@ +{ + "0.0.0.0/0": [ + { + "prefix": "0.0.0.0/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "192.168.5.1", + "afi": "ipv4", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "192.168.5.0/24": [ + { + "prefix": "192.168.5.0/24", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/ipv6_rib.json new file mode 100644 index 0000000..80ff52a --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:5::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:5::/64": [ + { + "prefix": "2001:5::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/zebra.conf new file mode 100644 index 0000000..a290213 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/zebra.conf @@ -0,0 +1,16 @@ +log file zebra.log +! +hostname ce5 +! +interface eth0 + ip address 192.168.5.2/24 + ipv6 address 2001:5::2/64 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.5.1 +ipv6 route ::/0 2001:5::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/bgpd.conf new file mode 100644 index 0000000..e0b6540 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce6 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/ip_rib.json new file mode 100644 index 0000000..4a603a5 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/ip_rib.json @@ -0,0 +1,58 @@ +{ + "0.0.0.0/0": [ + { + "prefix": "0.0.0.0/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "192.168.6.1", + "afi": "ipv4", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "192.168.6.0/24": [ + { + "prefix": "192.168.6.0/24", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/ipv6_rib.json new file mode 100644 index 0000000..ace6136 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:6::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:6::/64": [ + { + "prefix": "2001:6::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/zebra.conf new file mode 100644 index 0000000..5a83e90 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/zebra.conf @@ -0,0 +1,16 @@ +log file zebra.log +! +hostname ce6 +! +interface eth0 + ip address 192.168.6.2/24 + ipv6 address 2001:6::2/64 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.6.1 +ipv6 route ::/0 2001:6::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf new file mode 100644 index 0000000..01b0cc3 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf @@ -0,0 +1,87 @@ +frr defaults traditional +! +bgp send-extra-data zebra +! +hostname r1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug bgp neighbor-events +!debug bgp zebra +!debug bgp vnc verbose +!debug bgp update-groups +!debug bgp updates in +!debug bgp updates out +!debug bgp vpn label +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp vpn rmap-event +! +router bgp 1 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + !no bgp default ipv4-unicast + neighbor 2001::2 remote-as 2 + neighbor 2001::2 timers 3 10 + neighbor 2001::2 timers connect 1 + neighbor 2001::2 capability extended-nexthop + ! + address-family ipv4 vpn + neighbor 2001::2 activate + exit-address-family + ! + address-family ipv6 vpn + neighbor 2001::2 activate + exit-address-family + ! + segment-routing srv6 + locator loc1 + ! +! +router bgp 1 vrf vrf10 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + sid vpn per-vrf export auto + ! + address-family ipv4 unicast + nexthop vpn export 2001::1 + rd vpn export 1:10 + rt vpn both 99:99 + import vpn + export vpn + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + rd vpn export 1:10 + rt vpn both 99:99 + import vpn + export vpn + redistribute connected + exit-address-family +! +router bgp 1 vrf vrf20 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + sid vpn per-vrf export auto + ! + address-family ipv4 unicast + nexthop vpn export 2001::1 + rd vpn export 1:20 + rt vpn both 88:88 + import vpn + export vpn + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + rd vpn export 1:20 + rt vpn both 88:88 + import vpn + export vpn + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json new file mode 100644 index 0000000..3cc2fdd --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json @@ -0,0 +1,167 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 2, + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "192.168.1.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.1.0", + "prefixLen": 24, + "network": "192.168.1.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.3.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.3.0", + "prefixLen": 24, + "network": "192.168.3.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "192.168.5.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.5.0", + "prefixLen": 24, + "network": "192.168.5.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "192.168.2.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.2.0", + "prefixLen": 24, + "network": "192.168.2.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "192.168.4.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.4.0", + "prefixLen": 24, + "network": "192.168.4.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.6.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.6.0", + "prefixLen": 24, + "network": "192.168.6.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_locator_deleted.json new file mode 100644 index 0000000..5645540 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_locator_deleted.json @@ -0,0 +1,90 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "192.168.1.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.1.0", + "prefixLen": 24, + "network": "192.168.1.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.3.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.3.0", + "prefixLen": 24, + "network": "192.168.3.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "192.168.5.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.5.0", + "prefixLen": 24, + "network": "192.168.5.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_locator_recreated.json new file mode 100644 index 0000000..7a4e0d7 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_locator_recreated.json @@ -0,0 +1,166 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "192.168.1.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.1.0", + "prefixLen": 24, + "network": "192.168.1.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.3.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.3.0", + "prefixLen": 24, + "network": "192.168.3.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "192.168.5.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.5.0", + "prefixLen": 24, + "network": "192.168.5.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "192.168.2.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.2.0", + "prefixLen": 24, + "network": "192.168.2.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "192.168.4.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.4.0", + "prefixLen": 24, + "network": "192.168.4.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.6.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.6.0", + "prefixLen": 24, + "network": "192.168.6.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json new file mode 100644 index 0000000..eb34333 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json @@ -0,0 +1,115 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 4, + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:20": { + "192.168.5.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.5.0", + "prefixLen": 24, + "network": "192.168.5.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "192.168.2.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.2.0", + "prefixLen": 24, + "network": "192.168.2.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "192.168.4.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.4.0", + "prefixLen": 24, + "network": "192.168.4.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.6.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.6.0", + "prefixLen": 24, + "network": "192.168.6.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json new file mode 100644 index 0000000..5517fc7 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json @@ -0,0 +1,167 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 6, + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "192.168.1.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.1.0", + "prefixLen": 24, + "network": "192.168.1.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.3.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.3.0", + "prefixLen": 24, + "network": "192.168.3.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "192.168.5.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.5.0", + "prefixLen": 24, + "network": "192.168.5.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "192.168.2.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.2.0", + "prefixLen": 24, + "network": "192.168.2.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "192.168.4.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.4.0", + "prefixLen": 24, + "network": "192.168.4.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.6.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.6.0", + "prefixLen": 24, + "network": "192.168.6.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json new file mode 100644 index 0000000..25b7a86 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json @@ -0,0 +1,170 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 2, + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_locator_deleted.json new file mode 100644 index 0000000..f2df9be --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_locator_deleted.json @@ -0,0 +1,160 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_locator_recreated.json new file mode 100644 index 0000000..0fdd3d6 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_locator_recreated.json @@ -0,0 +1,169 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json new file mode 100644 index 0000000..a1f2158 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json @@ -0,0 +1,116 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 4, + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json new file mode 100644 index 0000000..7eeccd1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json @@ -0,0 +1,170 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 6, + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v4_rib.json new file mode 100644 index 0000000..cc96f43 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v4_rib.json @@ -0,0 +1,86 @@ +{ + "192.168.1.0/24": [ + { + "prefix": "192.168.1.0/24", + "protocol": "connected", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth1", + "active": true + } + ] + } + ], + "192.168.2.0/24": [ + { + "prefix": "192.168.2.0/24", + "protocol": "bgp", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:2:2:1::" + } + } + ], + "asPath": "2" + } + ], + "192.168.3.0/24": [ + { + "prefix": "192.168.3.0/24", + "protocol": "connected", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth2", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v6_rib.json new file mode 100644 index 0000000..0c9ae73 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v6_rib.json @@ -0,0 +1,86 @@ +{ + "2001:1::/64": [ + { + "prefix": "2001:1::/64", + "protocol": "connected", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth1", + "active": true + } + ] + } + ], + "2001:2::/64": [ + { + "prefix": "2001:2::/64", + "protocol": "bgp", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:2:2:1::" + } + } + ], + "asPath": "2" + } + ], + "2001:3::/64": [ + { + "prefix": "2001:3::/64", + "protocol": "connected", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth2", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v4_rib.json new file mode 100644 index 0000000..cf0fd18 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v4_rib.json @@ -0,0 +1,92 @@ +{ + "192.168.4.0/24": [ + { + "prefix": "192.168.4.0/24", + "protocol": "bgp", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:2:2:2::" + } + } + ], + "asPath": "2" + } + ], + "192.168.5.0/24": [ + { + "prefix": "192.168.5.0/24", + "protocol": "connected", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth3", + "active": true + } + ] + } + ], + "192.168.6.0/24": [ + { + "prefix": "192.168.6.0/24", + "protocol": "bgp", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:2:2:2::" + } + } + ], + "asPath": "2" + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v6_rib.json new file mode 100644 index 0000000..e486e74 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v6_rib.json @@ -0,0 +1,92 @@ +{ + "2001:4::/64": [ + { + "prefix": "2001:4::/64", + "protocol": "bgp", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:2:2:2::" + } + } + ], + "asPath": "2" + } + ], + "2001:5::/64": [ + { + "prefix": "2001:5::/64", + "protocol": "connected", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth3", + "active": true + } + ] + } + ], + "2001:6::/64": [ + { + "prefix": "2001:6::/64", + "protocol": "bgp", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:2:2:2::" + } + } + ], + "asPath": "2" + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf new file mode 100644 index 0000000..f913b9f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf @@ -0,0 +1,43 @@ +log file zebra.log +! +hostname r1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +interface eth0 + ipv6 address 2001::1/64 +! +interface eth1 vrf vrf10 + ip address 192.168.1.1/24 + ipv6 address 2001:1::1/64 +! +interface eth2 vrf vrf10 + ip address 192.168.3.1/24 + ipv6 address 2001:3::1/64 +! +interface eth3 vrf vrf20 + ip address 192.168.5.1/24 + ipv6 address 2001:5::1/64 +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:1:1::/64 + ! + ! +! +ip forwarding +ipv6 forwarding +! +ipv6 route 2001:db8:2:2::/64 2001::2 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf new file mode 100644 index 0000000..8700f12 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf @@ -0,0 +1,88 @@ +frr defaults traditional +! +bgp send-extra-data zebra +! +hostname r2 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug bgp neighbor-events +!debug bgp zebra +!debug bgp vnc verbose +!debug bgp update-groups +!debug bgp updates in +!debug bgp updates out +!debug bgp updates +!debug bgp vpn label +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp vpn rmap-event +! +router bgp 2 + bgp router-id 2.2.2.2 + no bgp ebgp-requires-policy + !no bgp default ipv4-unicast + neighbor 2001::1 remote-as 1 + neighbor 2001::1 timers 3 10 + neighbor 2001::1 timers connect 1 + neighbor 2001::1 capability extended-nexthop + ! + address-family ipv4 vpn + neighbor 2001::1 activate + exit-address-family + ! + address-family ipv6 vpn + neighbor 2001::1 activate + exit-address-family + ! + segment-routing srv6 + locator loc1 + ! +! +router bgp 2 vrf vrf10 + bgp router-id 2.2.2.2 + no bgp ebgp-requires-policy + sid vpn per-vrf export auto + ! + address-family ipv4 unicast + nexthop vpn export 2001::2 + rd vpn export 2:10 + rt vpn both 99:99 + import vpn + export vpn + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + rd vpn export 2:10 + rt vpn both 99:99 + import vpn + export vpn + redistribute connected + exit-address-family +! +router bgp 2 vrf vrf20 + bgp router-id 2.2.2.2 + no bgp ebgp-requires-policy + sid vpn per-vrf export auto + ! + address-family ipv4 unicast + nexthop vpn export 2001::2 + rd vpn export 2:20 + rt vpn both 88:88 + import vpn + export vpn + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + rd vpn export 2:20 + rt vpn both 88:88 + import vpn + export vpn + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json new file mode 100644 index 0000000..9557054 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json @@ -0,0 +1,167 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 2, + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:10": { + "192.168.1.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.1.0", + "prefixLen": 24, + "network": "192.168.1.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.3.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.3.0", + "prefixLen": 24, + "network": "192.168.3.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "192.168.5.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.5.0", + "prefixLen": 24, + "network": "192.168.5.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "192.168.2.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.2.0", + "prefixLen": 24, + "network": "192.168.2.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "192.168.4.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.4.0", + "prefixLen": 24, + "network": "192.168.4.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.6.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.6.0", + "prefixLen": 24, + "network": "192.168.6.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_locator_deleted.json new file mode 100644 index 0000000..e3edee1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_locator_deleted.json @@ -0,0 +1,90 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "2:10": { + "192.168.2.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.2.0", + "prefixLen": 24, + "network": "192.168.2.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "192.168.4.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.4.0", + "prefixLen": 24, + "network": "192.168.4.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.6.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.6.0", + "prefixLen": 24, + "network": "192.168.6.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_locator_recreated.json new file mode 100644 index 0000000..0dcdec6 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_locator_recreated.json @@ -0,0 +1,166 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:10": { + "192.168.1.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.1.0", + "prefixLen": 24, + "network": "192.168.1.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.3.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.3.0", + "prefixLen": 24, + "network": "192.168.3.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "192.168.5.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.5.0", + "prefixLen": 24, + "network": "192.168.5.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "192.168.2.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.2.0", + "prefixLen": 24, + "network": "192.168.2.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "192.168.4.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.4.0", + "prefixLen": 24, + "network": "192.168.4.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.6.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.6.0", + "prefixLen": 24, + "network": "192.168.6.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json new file mode 100644 index 0000000..d801671 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json @@ -0,0 +1,117 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 4, + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:20": { + "192.168.5.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.5.0", + "prefixLen": 24, + "network": "192.168.5.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "192.168.2.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.2.0", + "prefixLen": 24, + "network": "192.168.2.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "192.168.4.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.4.0", + "prefixLen": 24, + "network": "192.168.4.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.6.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.6.0", + "prefixLen": 24, + "network": "192.168.6.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json new file mode 100644 index 0000000..25da05b --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json @@ -0,0 +1,167 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 6, + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:10": { + "192.168.1.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.1.0", + "prefixLen": 24, + "network": "192.168.1.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.3.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.3.0", + "prefixLen": 24, + "network": "192.168.3.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "192.168.5.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.5.0", + "prefixLen": 24, + "network": "192.168.5.0/24", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "192.168.2.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.2.0", + "prefixLen": 24, + "network": "192.168.2.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "192.168.4.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.4.0", + "prefixLen": 24, + "network": "192.168.4.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192.168.6.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "192.168.6.0", + "prefixLen": 24, + "network": "192.168.6.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json new file mode 100644 index 0000000..2cd47b9 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json @@ -0,0 +1,170 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 2, + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_locator_deleted.json new file mode 100644 index 0000000..25cdf03 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_locator_deleted.json @@ -0,0 +1,93 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_locator_recreated.json new file mode 100644 index 0000000..03bbcc0 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_locator_recreated.json @@ -0,0 +1,169 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json new file mode 100644 index 0000000..f390ef6 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json @@ -0,0 +1,120 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 4, + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json new file mode 100644 index 0000000..3353d75 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json @@ -0,0 +1,170 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 6, + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v4_rib.json new file mode 100644 index 0000000..1534e98 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v4_rib.json @@ -0,0 +1,92 @@ +{ + "192.168.1.0/24": [ + { + "prefix": "192.168.1.0/24", + "protocol": "bgp", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:1:1:1::" + } + } + ], + "asPath": "1" + } + ], + "192.168.2.0/24": [ + { + "prefix": "192.168.2.0/24", + "protocol": "connected", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth1", + "active": true + } + ] + } + ], + "192.168.3.0/24": [ + { + "prefix": "192.168.3.0/24", + "protocol": "bgp", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:1:1:1::" + } + } + ], + "asPath": "1" + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v6_rib.json new file mode 100644 index 0000000..a2e329d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v6_rib.json @@ -0,0 +1,92 @@ +{ + "2001:1::/64": [ + { + "prefix": "2001:1::/64", + "protocol": "bgp", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:1:1:1::" + } + } + ], + "asPath": "1" + } + ], + "2001:2::/64": [ + { + "prefix": "2001:2::/64", + "protocol": "connected", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth1", + "active": true + } + ] + } + ], + "2001:3::/64": [ + { + "prefix": "2001:3::/64", + "protocol": "bgp", + "vrfName": "vrf10", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 10, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:1:1:1::" + } + } + ], + "asPath": "1" + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v4_rib.json new file mode 100644 index 0000000..49d1861 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v4_rib.json @@ -0,0 +1,86 @@ +{ + "192.168.4.0/24": [ + { + "prefix": "192.168.4.0/24", + "protocol": "connected", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth2", + "active": true + } + ] + } + ], + "192.168.5.0/24": [ + { + "prefix": "192.168.5.0/24", + "protocol": "bgp", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:1:1:2::" + } + } + ], + "asPath": "1" + } + ], + "192.168.6.0/24": [ + { + "prefix": "192.168.6.0/24", + "protocol": "connected", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth3", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v6_rib.json new file mode 100644 index 0000000..f7433d5 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v6_rib.json @@ -0,0 +1,86 @@ +{ + "2001:4::/64": [ + { + "prefix": "2001:4::/64", + "protocol": "connected", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth2", + "active": true + } + ] + } + ], + "2001:5::/64": [ + { + "prefix": "2001:5::/64", + "protocol": "bgp", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "eth0", + "vrf": "default", + "active": true, + "weight": 1, + "seg6": { + "segs": "2001:db8:1:1:2::" + } + } + ], + "asPath": "1" + } + ], + "2001:6::/64": [ + { + "prefix": "2001:6::/64", + "protocol": "connected", + "vrfName": "vrf20", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 20, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth3", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf new file mode 100644 index 0000000..201d0cc --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf @@ -0,0 +1,43 @@ +log file zebra.log +! +hostname r2 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +interface eth0 + ipv6 address 2001::2/64 +! +interface eth1 vrf vrf10 + ip address 192.168.2.1/24 + ipv6 address 2001:2::1/64 +! +interface eth2 vrf vrf20 + ip address 192.168.4.1/24 + ipv6 address 2001:4::1/64 +! +interface eth3 vrf vrf20 + ip address 192.168.6.1/24 + ipv6 address 2001:6::1/64 +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:2:2::/64 + ! + ! +! +ip forwarding +ipv6 forwarding +! +ipv6 route 2001:db8:1:1::/64 2001::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/test_bgp_srv6l3vpn_to_bgp_vrf3.py b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/test_bgp_srv6l3vpn_to_bgp_vrf3.py new file mode 100644 index 0000000..8a7b558 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/test_bgp_srv6l3vpn_to_bgp_vrf3.py @@ -0,0 +1,337 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2022, University of Rome Tor Vergata +# Authored by Carmine Scarpitta +# + +import os +import re +import sys +import json +import functools +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import required_linux_kernel_version +from lib.checkping import check_ping, check_ping + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("ce1") + tgen.add_router("ce2") + tgen.add_router("ce3") + tgen.add_router("ce4") + tgen.add_router("ce5") + tgen.add_router("ce6") + + tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0") + tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "eth0", "eth1") + tgen.add_link(tgen.gears["ce2"], tgen.gears["r2"], "eth0", "eth1") + tgen.add_link(tgen.gears["ce3"], tgen.gears["r1"], "eth0", "eth2") + tgen.add_link(tgen.gears["ce4"], tgen.gears["r2"], "eth0", "eth2") + tgen.add_link(tgen.gears["ce5"], tgen.gears["r1"], "eth0", "eth3") + tgen.add_link(tgen.gears["ce6"], tgen.gears["r2"], "eth0", "eth3") + + +def setup_module(mod): + result = required_linux_kernel_version("5.14") + if result is not True: + pytest.skip("Kernel requirements are not met") + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname)) + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.gears["r1"].run("sysctl net.vrf.strict_mode=1") + tgen.gears["r1"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r1"].run("ip link set vrf10 up") + tgen.gears["r1"].run("ip link add vrf20 type vrf table 20") + tgen.gears["r1"].run("ip link set vrf20 up") + tgen.gears["r1"].run("ip link set eth1 master vrf10") + tgen.gears["r1"].run("ip link set eth2 master vrf10") + tgen.gears["r1"].run("ip link set eth3 master vrf20") + + tgen.gears["r2"].run("sysctl net.vrf.strict_mode=1") + tgen.gears["r2"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r2"].run("ip link set vrf10 up") + tgen.gears["r2"].run("ip link add vrf20 type vrf table 20") + tgen.gears["r2"].run("ip link set vrf20 up") + tgen.gears["r2"].run("ip link set eth1 master vrf10") + tgen.gears["r2"].run("ip link set eth2 master vrf20") + tgen.gears["r2"].run("ip link set eth3 master vrf20") + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def check_rib(name, cmd, expected_file): + def _check(name, dest_addr, match): + logger.info("polling") + tgen = get_topogen() + router = tgen.gears[name] + output = json.loads(router.vtysh_cmd(cmd)) + expected = open_json_file("{}/{}".format(CWD, expected_file)) + return topotest.json_cmp(output, expected) + + logger.info('[+] check {} "{}" {}'.format(name, cmd, expected_file)) + tgen = get_topogen() + func = functools.partial(_check, name, cmd, expected_file) + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "Failed" + + +def test_rib(): + check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib.json") + check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib.json") + check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10v4_rib.json") + check_rib("r1", "show ip route vrf vrf20 json", "r1/vrf20v4_rib.json") + check_rib("r2", "show ip route vrf vrf10 json", "r2/vrf10v4_rib.json") + check_rib("r2", "show ip route vrf vrf20 json", "r2/vrf20v4_rib.json") + check_rib("ce1", "show ip route json", "ce1/ip_rib.json") + check_rib("ce2", "show ip route json", "ce2/ip_rib.json") + check_rib("ce3", "show ip route json", "ce3/ip_rib.json") + check_rib("ce4", "show ip route json", "ce4/ip_rib.json") + check_rib("ce5", "show ip route json", "ce5/ip_rib.json") + check_rib("ce6", "show ip route json", "ce6/ip_rib.json") + + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json") + check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10v6_rib.json") + check_rib("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20v6_rib.json") + check_rib("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10v6_rib.json") + check_rib("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20v6_rib.json") + check_rib("ce1", "show ipv6 route json", "ce1/ipv6_rib.json") + check_rib("ce2", "show ipv6 route json", "ce2/ipv6_rib.json") + check_rib("ce3", "show ipv6 route json", "ce3/ipv6_rib.json") + check_rib("ce4", "show ipv6 route json", "ce4/ipv6_rib.json") + check_rib("ce5", "show ipv6 route json", "ce5/ipv6_rib.json") + check_rib("ce6", "show ipv6 route json", "ce6/ipv6_rib.json") + + +def test_ping(): + check_ping("ce1", "192.168.2.2", True, 10, 0.5) + check_ping("ce1", "192.168.3.2", True, 10, 0.5) + check_ping("ce1", "192.168.4.2", False, 10, 0.5) + check_ping("ce1", "192.168.5.2", False, 10, 0.5) + check_ping("ce1", "192.168.6.2", False, 10, 0.5) + check_ping("ce4", "192.168.1.2", False, 10, 0.5) + check_ping("ce4", "192.168.2.2", False, 10, 0.5) + check_ping("ce4", "192.168.3.2", False, 10, 0.5) + check_ping("ce4", "192.168.5.2", True, 10, 0.5) + check_ping("ce4", "192.168.6.2", True, 10, 0.5) + + check_ping("ce1", "2001:2::2", True, 10, 1) + check_ping("ce1", "2001:3::2", True, 10, 1) + check_ping("ce1", "2001:4::2", False, 10, 1) + check_ping("ce1", "2001:5::2", False, 10, 1) + check_ping("ce1", "2001:6::2", False, 10, 1) + check_ping("ce4", "2001:1::2", False, 10, 1) + check_ping("ce4", "2001:2::2", False, 10, 1) + check_ping("ce4", "2001:3::2", False, 10, 1) + check_ping("ce4", "2001:5::2", True, 10, 1) + check_ping("ce4", "2001:6::2", True, 10, 1) + + +def test_bgp_sid_vpn_export_disable(): + check_ping("ce1", "192.168.2.2", True, 10, 0.5) + check_ping("ce1", "2001:2::2", True, 10, 1) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 vrf vrf10 + segment-routing srv6 + no sid vpn per-vrf export + """ + ) + check_rib( + "r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_sid_vpn_export_disabled.json" + ) + check_rib( + "r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_sid_vpn_export_disabled.json" + ) + check_rib( + "r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_sid_vpn_export_disabled.json" + ) + check_rib( + "r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_sid_vpn_export_disabled.json" + ) + check_ping("ce1", "192.168.2.2", False, 10, 0.5) + check_ping("ce1", "2001:2::2", False, 10, 1) + + +def test_bgp_sid_vpn_export_reenable(): + check_ping("ce1", "192.168.2.2", False, 10, 0.5) + check_ping("ce1", "2001:2::2", False, 10, 1) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 vrf vrf10 + segment-routing srv6 + sid vpn per-vrf export auto + """ + ) + check_rib( + "r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_sid_vpn_export_reenabled.json" + ) + check_rib( + "r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_sid_vpn_export_reenabled.json" + ) + check_rib( + "r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_sid_vpn_export_reenabled.json" + ) + check_rib( + "r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_sid_vpn_export_reenabled.json" + ) + check_ping("ce1", "192.168.2.2", True, 10, 0.5) + check_ping("ce1", "2001:2::2", True, 10, 1) + + +def test_locator_delete(): + check_ping("ce1", "192.168.2.2", True, 10, 0.5) + check_ping("ce1", "2001:2::2", True, 10, 1) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + no locator loc1 + """ + ) + check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_deleted.json") + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") + check_ping("ce1", "192.168.2.2", False, 10, 0.5) + check_ping("ce1", "2001:2::2", False, 10, 1) + + +def test_locator_recreate(): + check_ping("ce1", "192.168.2.2", False, 10, 0.5) + check_ping("ce1", "2001:2::2", False, 10, 1) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:1:1::/64 + """ + ) + check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_recreated.json") + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") + check_ping("ce1", "192.168.2.2", True, 10, 0.5) + check_ping("ce1", "2001:2::2", True, 10, 1) + + +def test_bgp_locator_unset(): + check_ping("ce1", "192.168.2.2", True, 10, 0.5) + check_ping("ce1", "2001:2::2", True, 10, 1) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + segment-routing srv6 + no locator loc1 + """ + ) + check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_deleted.json") + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") + check_ping("ce1", "192.168.2.2", False, 10, 0.5) + check_ping("ce1", "2001:2::2", False, 10, 1) + + +def test_bgp_locator_reset(): + check_ping("ce1", "192.168.2.2", False, 10, 0.5) + check_ping("ce1", "2001:2::2", False, 10, 1) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + segment-routing srv6 + locator loc1 + """ + ) + check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_recreated.json") + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") + check_ping("ce1", "192.168.2.2", True, 10, 0.5) + check_ping("ce1", "2001:2::2", True, 10, 1) + + +def test_bgp_srv6_unset(): + check_ping("ce1", "192.168.2.2", True, 10, 0.5) + check_ping("ce1", "2001:2::2", True, 10, 1) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + no segment-routing srv6 + """ + ) + check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_deleted.json") + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") + check_ping("ce1", "192.168.2.2", False, 10, 0.5) + check_ping("ce1", "2001:2::2", False, 10, 1) + + +def test_bgp_srv6_reset(): + check_ping("ce1", "192.168.2.2", False, 10, 0.5) + check_ping("ce1", "2001:2::2", False, 10, 1) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + segment-routing srv6 + locator loc1 + """ + ) + check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_recreated.json") + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") + check_ping("ce1", "192.168.2.2", True, 10, 0.5) + check_ping("ce1", "2001:2::2", True, 10, 1) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_suppress_fib/r1/bgp_ipv4_allowas.json b/tests/topotests/bgp_suppress_fib/r1/bgp_ipv4_allowas.json new file mode 100644 index 0000000..1a5ede2 --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/r1/bgp_ipv4_allowas.json @@ -0,0 +1,40 @@ +{ + "prefix":"192.168.1.1/32", + "paths":[ + { + "aspath":{ + "string":"2", + "segments":[ + { + "type":"as-sequence", + "list":[ + 2 + ] + } + ], + "length":1 + }, + "origin":"incomplete", + "metric":0, + "valid":true, + "bestpath":{ + "overall":true, + "selectionReason":"First path received" + }, + "nexthops":[ + { + "ip":"10.0.0.2", + "afi":"ipv4", + "metric":0, + "accessible":true, + "used":true + } + ], + "peer":{ + "peerId":"10.0.0.2", + "routerId":"60.0.0.1", + "type":"external" + } + } + ] +} diff --git a/tests/topotests/bgp_suppress_fib/r1/bgpd.conf b/tests/topotests/bgp_suppress_fib/r1/bgpd.conf new file mode 100644 index 0000000..69c563d --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/r1/bgpd.conf @@ -0,0 +1,15 @@ +! exit1 +router bgp 1 + no bgp ebgp-requires-policy + neighbor 10.0.0.2 remote-as 2 + + address-family ipv4 unicast + redistribute static + neighbor 10.0.0.2 route-map rmap out + exit-address-family + +ip prefix-list plist seq 5 permit any + +route-map rmap permit 1 + match ip address prefix-list plist +! diff --git a/tests/topotests/bgp_suppress_fib/r1/zebra.conf b/tests/topotests/bgp_suppress_fib/r1/zebra.conf new file mode 100644 index 0000000..7b44216 --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/r1/zebra.conf @@ -0,0 +1,9 @@ +! exit1 +interface r1-eth0 + ip address 10.0.0.1/30 +! +ip forwarding +! +ip route 40.0.0.0/8 blackhole +ip route 50.0.0.0/8 blackhole +! diff --git a/tests/topotests/bgp_suppress_fib/r2/bgp_ipv4_allowas.json b/tests/topotests/bgp_suppress_fib/r2/bgp_ipv4_allowas.json new file mode 100644 index 0000000..4a35abf --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/r2/bgp_ipv4_allowas.json @@ -0,0 +1,68 @@ +{ + "prefix":"192.168.1.1/32", + "paths":[ + { + "aspath":{ + "string":"1 2", + "segments":[ + { + "type":"as-sequence", + "list":[ + 1, + 2 + ] + } + ], + "length":2 + }, + "origin":"incomplete", + "valid":true, + "fibInstalled":true, + "nexthops":[ + { + "ip":"10.0.0.1", + "afi":"ipv4", + "metric":0, + "accessible":true, + "used":true + } + ], + "peer":{ + "peerId":"10.0.0.1", + "routerId":"10.0.0.1", + "type":"external" + } + }, + { + "aspath":{ + "string":"Local", + "segments":[ + ], + "length":0 + }, + "origin":"incomplete", + "metric":0, + "weight":32768, + "valid":true, + "sourced":true, + "bestpath":{ + "overall":true, + "selectionReason":"Weight" + }, + "fibInstalled":true, + "nexthops":[ + { + "ip":"10.0.0.10", + "afi":"ipv4", + "metric":0, + "accessible":true, + "used":true + } + ], + "peer":{ + "peerId":"0.0.0.0", + "routerId":"60.0.0.1" + } + } + ] +} diff --git a/tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf b/tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf new file mode 100644 index 0000000..fb6980a --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf @@ -0,0 +1,18 @@ +access-list access seq 10 permit 192.168.1.1/32 +! +ip route 192.168.1.1/32 10.0.0.10 +! +!debug bgp bestpath +!debug bgp nht +!debug bgp updates +!debug bgp update-groups +!debug bgp zebra +!debug zebra rib detail +! +router bgp 2 + address-family ipv4 uni + redistribute static + neighbor 10.0.0.10 allowas-in 1 + neighbor 10.0.0.1 allowas-in 1 + ! +! diff --git a/tests/topotests/bgp_suppress_fib/r2/bgpd.conf b/tests/topotests/bgp_suppress_fib/r2/bgpd.conf new file mode 100644 index 0000000..129b812 --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/r2/bgpd.conf @@ -0,0 +1,11 @@ +!debug bgp updates +!debug bgp bestpath 40.0.0.0/8 +!debug bgp zebra +! +router bgp 2 + no bgp ebgp-requires-policy + bgp suppress-fib-pending + neighbor 10.0.0.1 remote-as 1 + neighbor 10.0.0.10 remote-as 3 + address-family ipv4 uni + network 60.0.0.0/24 diff --git a/tests/topotests/bgp_suppress_fib/r2/no_bgp_ipv4_allowas.json b/tests/topotests/bgp_suppress_fib/r2/no_bgp_ipv4_allowas.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/r2/no_bgp_ipv4_allowas.json @@ -0,0 +1 @@ +{} diff --git a/tests/topotests/bgp_suppress_fib/r2/v4_override.json b/tests/topotests/bgp_suppress_fib/r2/v4_override.json new file mode 100644 index 0000000..f17907f --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/r2/v4_override.json @@ -0,0 +1,20 @@ +{ + "40.0.0.0\/8":[ + { + "prefix":"40.0.0.0\/8", + "protocol":"static", + "vrfName":"default", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.0.10", + "afi":"ipv4", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_suppress_fib/r2/zebra.conf b/tests/topotests/bgp_suppress_fib/r2/zebra.conf new file mode 100644 index 0000000..6e8bce0 --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/r2/zebra.conf @@ -0,0 +1,16 @@ +! +interface lo + ip address 60.0.0.1/24 +! +interface r2-eth0 + ip address 10.0.0.2/30 +! +interface r2-eth1 + ip address 10.0.0.9/30 + +access-list access seq 5 permit 40.0.0.0/8 + +route-map LIMIT permit 10 + match ip address access + +ip protocol bgp route-map LIMIT diff --git a/tests/topotests/bgp_suppress_fib/r3/bgpd.conf b/tests/topotests/bgp_suppress_fib/r3/bgpd.conf new file mode 100644 index 0000000..11715d4 --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/r3/bgpd.conf @@ -0,0 +1,9 @@ +! +router bgp 3 + no bgp ebgp-requires-policy + neighbor 10.0.0.9 remote-as 2 + +route-map rmap permit 1 + match ip address prefix-list plist + ! +! diff --git a/tests/topotests/bgp_suppress_fib/r3/v4_override.json b/tests/topotests/bgp_suppress_fib/r3/v4_override.json new file mode 100644 index 0000000..a35d49e --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/r3/v4_override.json @@ -0,0 +1,4 @@ +{ + "0.0.0.0\/0":[ + ] +} diff --git a/tests/topotests/bgp_suppress_fib/r3/v4_route.json b/tests/topotests/bgp_suppress_fib/r3/v4_route.json new file mode 100644 index 0000000..e255d07 --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/r3/v4_route.json @@ -0,0 +1,28 @@ +{ + "40.0.0.0\/8":[ + { + "prefix":"40.0.0.0\/8", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.0.9", + "afi":"ipv4", + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_suppress_fib/r3/v4_route2.json b/tests/topotests/bgp_suppress_fib/r3/v4_route2.json new file mode 100644 index 0000000..a35d49e --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/r3/v4_route2.json @@ -0,0 +1,4 @@ +{ + "0.0.0.0\/0":[ + ] +} diff --git a/tests/topotests/bgp_suppress_fib/r3/v4_route3.json b/tests/topotests/bgp_suppress_fib/r3/v4_route3.json new file mode 100644 index 0000000..ab8c3aa --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/r3/v4_route3.json @@ -0,0 +1,23 @@ +{ + "60.0.0.0/24":[ + { + "prefix":"60.0.0.0/24", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "fib":true, + "ip":"10.0.0.9", + "afi":"ipv4", + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_suppress_fib/r3/zebra.conf b/tests/topotests/bgp_suppress_fib/r3/zebra.conf new file mode 100644 index 0000000..793b043 --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/r3/zebra.conf @@ -0,0 +1,6 @@ +! +interface r3-eth0 + ip address 10.0.0.10/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py new file mode 100644 index 0000000..fd8a78b --- /dev/null +++ b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py @@ -0,0 +1,237 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_suppress_fib.py +# +# Copyright (c) 2019 by +# + +""" +""" + +import os +import sys +import json +import pytest +from functools import partial +from time import sleep +from lib.topolog import logger + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_route(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r3 = tgen.gears["r3"] + + json_file = "{}/r3/v4_route.json".format(CWD) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, + r3, + "show ip route 40.0.0.0 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5) + assertmsg = '"r3" JSON output mismatches' + assert result is None, assertmsg + + json_file = "{}/r3/v4_route2.json".format(CWD) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, + r3, + "show ip route 50.0.0.0 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, assertmsg + + json_file = "{}/r3/v4_route3.json".format(CWD) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, + r3, + "show ip route 60.0.0.0 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, assertmsg + + +def test_bgp_better_admin_won(): + "A better Admin distance protocol may come along and knock us out" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + r2.vtysh_cmd("conf\nip route 40.0.0.0/8 10.0.0.10") + + json_file = "{}/r2/v4_override.json".format(CWD) + expected = json.loads(open(json_file).read()) + + logger.info(expected) + test_func = partial( + topotest.router_json_cmp, r2, "show ip route 40.0.0.0 json", expected + ) + + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"r2" static route did not take over' + assert result is None, assertmsg + + r3 = tgen.gears["r3"] + + json_file = "{}/r3/v4_override.json".format(CWD) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 40.0.0.0 json", expected + ) + + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"r3" route to 40.0.0.0 should have been lost' + assert result is None, assertmsg + + r2.vtysh_cmd("conf\nno ip route 40.0.0.0/8 10.0.0.10") + + json_file = "{}/r3/v4_route.json".format(CWD) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, + r3, + "show ip route 40.0.0.0 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"r3" route to 40.0.0.0 did not come back' + assert result is None, assertmsg + + +def test_bgp_allow_as_in(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + + config_file = "{}/r2/bgpd.allowas_in.conf".format(CWD) + r2.run("vtysh -f {}".format(config_file)) + + json_file = "{}/r2/bgp_ipv4_allowas.json".format(CWD) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, + r2, + "show bgp ipv4 uni 192.168.1.1/32 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"r2" static redistribution failed into bgp' + assert result is None, assertmsg + + r1 = tgen.gears["r1"] + + json_file = "{}/r1/bgp_ipv4_allowas.json".format(CWD) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, + r1, + "show bgp ipv4 uni 192.168.1.1/32 json", + expected, + ) + + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"r1" 192.168.1.1/32 route should have arrived' + assert result is None, assertmsg + + r2.vtysh_cmd("conf\nno ip route 192.168.1.1/32 10.0.0.10") + + json_file = "{}/r2/no_bgp_ipv4_allowas.json".format(CWD) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, + r2, + "show bgp ipv4 uni 192.168.1.1/32 json", + expected, + ) + + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"r2" 192.168.1.1/32 route should be gone' + assert result is None, assertmsg + +def test_local_vs_non_local(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + + output = json.loads(r2.vtysh_cmd("show bgp ipv4 uni 60.0.0.0/24 json")) + paths = output["paths"] + for i in range(len(paths)): + if "fibPending" in paths[i]: + assert(False), "Route 60.0.0.0/24 should not have fibPending" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_tcp_mss/__init__.py b/tests/topotests/bgp_tcp_mss/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_tcp_mss/bgp_vrf_tcp_mss.json b/tests/topotests/bgp_tcp_mss/bgp_vrf_tcp_mss.json new file mode 100644 index 0000000..17cee03 --- /dev/null +++ b/tests/topotests/bgp_tcp_mss/bgp_vrf_tcp_mss.json @@ -0,0 +1,222 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "20.20.20.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.10.10.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 24, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + } + }, + "vrfs": [ + { + "name": "RED", + "id": "1" + } + ], + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + } + } + } + ] + }, + "r2": { + "links": { + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "vrfs": [ + { + "name": "RED", + "id": "1" + } + ], + "bgp": [ + { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + } + } + }, + { + "local_as": "200", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + } + } + } + ] + }, + "r3": { + "links": { + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [ + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + } + } + } + ] + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_tcp_mss/r1/bgpd.conf b/tests/topotests/bgp_tcp_mss/r1/bgpd.conf new file mode 100644 index 0000000..07cfe2e --- /dev/null +++ b/tests/topotests/bgp_tcp_mss/r1/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + exit-address-family +! diff --git a/tests/topotests/bgp_tcp_mss/r1/zebra.conf b/tests/topotests/bgp_tcp_mss/r1/zebra.conf new file mode 100644 index 0000000..6e9b0b4 --- /dev/null +++ b/tests/topotests/bgp_tcp_mss/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_tcp_mss/r2/bgpd.conf b/tests/topotests/bgp_tcp_mss/r2/bgpd.conf new file mode 100644 index 0000000..b2d9455 --- /dev/null +++ b/tests/topotests/bgp_tcp_mss/r2/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + exit-address-family +! diff --git a/tests/topotests/bgp_tcp_mss/r2/zebra.conf b/tests/topotests/bgp_tcp_mss/r2/zebra.conf new file mode 100644 index 0000000..6c14de5 --- /dev/null +++ b/tests/topotests/bgp_tcp_mss/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py b/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py new file mode 100644 index 0000000..e7948ea --- /dev/null +++ b/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# bgp_tcp_mss.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 by +# Abhinay Ramesh +# + +""" +bgp_tcp_mss.py: + +Test if works the following commands: +router bgp 65000 + neighbor 192.168.255.2 tcp-mss 500 + +Need to verify if the tcp-mss value is reflected in the TCP session. +""" + +import os +import sys +import json +import pytest +import functools + +# add after imports, before defining classes or functions: +pytestmark = [pytest.mark.bgpd] + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_tcp_mss(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 0}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_conf_tcp_mss(router, as_num, neigh): + router.vtysh_cmd( + """configure terminal + router bgp {0} + neighbor {1} tcp-mss 500""".format( + as_num, neigh + ) + ) + + def _bgp_clear_session(router): + router.vtysh_cmd("clear bgp *") + + def _bgp_check_neighbor_tcp_mss(router, neigh): + output = json.loads(router.vtysh_cmd("show bgp neighbor {} json".format(neigh))) + expected = { + "{}".format(neigh): {"bgpTcpMssConfigured": 500, "bgpTcpMssSynced": 488} + } + return topotest.json_cmp(output, expected) + + logger.info("Check if neighbor sessions are up in {}".format(router1.name)) + test_func = functools.partial(_bgp_converge, router1) + success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + assert result is None, 'Failed to see BGP convergence in "{}"'.format(router1.name) + + logger.info("BGP neighbor session is up in {}".format(router1.name)) + + logger.info( + "Configure tcp-mss 500 on {} and reset the session".format(router1.name) + ) + _bgp_conf_tcp_mss(router1, "65000", "192.168.255.2") + _bgp_clear_session(router1) + + logger.info( + "Configure tcp-mss 500 on {} and reset the session".format(router2.name) + ) + _bgp_conf_tcp_mss(router2, "65001", "192.168.255.1") + _bgp_clear_session(router2) + + logger.info( + "Check if neighbor session is up after reset in {}".format(router1.name) + ) + test_func = functools.partial(_bgp_converge, router1) + success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + assert result is None, 'Failed to see BGP convergence after reset in "{}"'.format( + router1.name + ) + + logger.info( + "Verify if TCP MSS value is synced with neighbor in {}".format(router1.name) + ) + test_func = functools.partial(_bgp_check_neighbor_tcp_mss, router1, "192.168.255.2") + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert ( + result is None + ), 'Failed to sync TCP MSS value over BGP session in "{}"'.format(router1.name) + logger.info("TCP MSS value is synced with neighbor in {}".format(router1.name)) + + logger.info( + "Verify if TCP MSS value is synced with neighbor in {}".format(router2.name) + ) + test_func = functools.partial(_bgp_check_neighbor_tcp_mss, router2, "192.168.255.1") + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert ( + result is None + ), 'Failed to sync TCP MSS value over BGP session in "{}"'.format(router2.name) + logger.info("TCP MSS value is synced with neighbor in {}".format(router2.name)) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_tcp_mss/test_bgp_vrf_tcp_mss.py b/tests/topotests/bgp_tcp_mss/test_bgp_vrf_tcp_mss.py new file mode 100644 index 0000000..6fe044f --- /dev/null +++ b/tests/topotests/bgp_tcp_mss/test_bgp_vrf_tcp_mss.py @@ -0,0 +1,753 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# bgp_tcp_mss.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 by +# Shreenidhi A R +# + +""" +bgp_tcp_mss_vrf.py: + +Need to verify if the tcp-mss value is reflected in the TCP session and in VRF. +""" + +import os +import sys +import json +import pytest +import functools +import platform +import socket +import subprocess + +# add after imports, before defining classes or functions: +pytestmark = [pytest.mark.bgpd] + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topojson import build_config_from_json +from lib.topolog import logger +import time +from lib.bgp import ( + clear_bgp, + clear_bgp_and_verify, + create_router_bgp, + modify_as_number, + verify_as_numbers, + verify_bgp_convergence, + verify_bgp_rib, + verify_bgp_timers_and_functionality, + verify_router_id, + verify_tcp_mss +) +from lib.common_config import ( + kill_router_daemons, + start_router_daemons, + addKernelRoute, + apply_raw_config, + check_address_types, + create_prefix_lists, + create_route_maps, + create_static_routes, + required_linux_kernel_version, + reset_config_on_routers, + start_topology, + step, + verify_admin_distance_for_static_routes, + verify_bgp_community, + verify_fib_routes, + verify_rib, + write_test_footer, + write_test_header +) + +pytestmark = [pytest.mark.bgpd] +# Global variables +NETWORK1_1 = {"ipv4": "1.1.1.1/32", "ipv6": "1::1/128"} +NETWORK1_2 = {"ipv4": "1.1.1.2/32", "ipv6": "1::2/128"} +NETWORK2_1 = {"ipv4": "2.1.1.1/32", "ipv6": "2::1/128"} +NETWORK2_2 = {"ipv4": "2.1.1.2/32", "ipv6": "2::2/128"} +NETWORK3_1 = {"ipv4": "3.1.1.1/32", "ipv6": "3::1/128"} +NETWORK3_2 = {"ipv4": "3.1.1.2/32", "ipv6": "3::2/128"} +NETWORK4_1 = {"ipv4": "4.1.1.1/32", "ipv6": "4::1/128"} +NETWORK4_2 = {"ipv4": "4.1.1.2/32", "ipv6": "4::2/128"} +NETWORK5_1 = {"ipv4": "5.1.1.1/32", "ipv6": "5::1/128"} +NETWORK5_2 = {"ipv4": "5.1.1.2/32", "ipv6": "5::2/128"} + +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + +## File name +TCPDUMP_FILE="test_tcp_packet_test.txt" + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo,TCPDUMP_FILE + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + step("Testsuite start time: {}".format(testsuite_run_time)) + step("=" * 40) + + step("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_vrf_tcp_mss.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global ADDR_TYPES + global BGP_CONVERGENCE + ADDR_TYPES = check_address_types() + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Running setup_module() done") + +def teardown_module(): + """Teardown the pytest environment""" + + step("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + step( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + step("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + +def test_bgp_vrf_tcp_mss(request): + tgen = get_topogen() + tc_name = request.node.name + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Verify the router failures") + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configuring 5 static Routes in Router R3 with NULL0 as Next hop") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK5_1[addr_type]] + [NETWORK5_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + result = create_static_routes(tgen, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify the static Routes in R3 on default VRF") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK5_1[addr_type]] + [NETWORK5_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + dut = "r3" + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Verify the static Routes in R2 on default VRF") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK5_1[addr_type]] + [NETWORK5_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + dut = "r2" + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("importing default vrf on R2 under VRF RED Address Family") + for addr_type in ADDR_TYPES: + input_import_vrf = { + "r2": { + "bgp": [ + { + "local_as": 200, + "vrf": "RED", + "address_family": { + addr_type: {"unicast": {"import": {"vrf": "default"}}} + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_import_vrf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify the static Routes in R2 on RED VRF") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + }, + { + "network": [NETWORK5_1[addr_type]] + [NETWORK5_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + }, + ] + } + } + dut = "r2" + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Verify the static Routes in R1 on RED VRF") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + }, + { + "network": [NETWORK5_1[addr_type]] + [NETWORK5_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + }, + ] + } + } + dut = "r1" + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Enabling tcp-mss 150 on Router R1 in VRF RED") + TCP_MSS = 150 + raw_config = { + "r1": { + "raw_config": [ + "router bgp {} vrf {}".format( + topo["routers"]["r1"]["bgp"][0]["local_as"], + topo["routers"]["r1"]["bgp"][0]["vrf"], + ), + "neighbor {} tcp-mss {}".format( + topo["routers"]["r2"]["links"]["r1-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ), + "neighbor {} tcp-mss {}".format( + topo["routers"]["r2"]["links"]["r1-link1"]["ipv6"].split("/")[0], + TCP_MSS, + ), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Clearing BGP on R1 and R2 ") + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r1", vrf=topo["routers"]["r1"]["bgp"][0]["vrf"]) + clear_bgp(tgen, addr_type, "r2", vrf=topo["routers"]["r2"]["bgp"][1]["vrf"]) + + step("Verify the BGP Convergence at R1 & R2 after Clear BGP") + r1_convergence = verify_bgp_convergence(tgen, topo, dut="r1") + assert ( + r1_convergence is True + ), "BGP convergence after Clear BGP :Failed \n Error: {}".format(r1_convergence) + r2_convergence = verify_bgp_convergence(tgen, topo, dut="r2") + assert ( + r2_convergence is True + ), "BGP convergence after Clear BGP :Failed \n Error: {}".format(r2_convergence) + + step("Verify the TCP-MSS value on both Router R1 and R2") + for addr_type in ADDR_TYPES: + dut = "r1" + tcp_mss_result = verify_tcp_mss( + tgen, + dut, + topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0], + TCP_MSS, + "RED", + ) + assert tcp_mss_result is True, " TCP-MSS mismatch :Failed \n Error: {}".format( + tcp_mss_result + ) + + + + + step("Enabling tcp-mss 500 between R2 and R3 of VRF Default") + TCP_MSS = 500 + raw_config = { + "r2": { + "raw_config": [ + "router bgp {} ".format(topo["routers"]["r2"]["bgp"][0]["local_as"]), + "neighbor {} tcp-mss {}".format( + topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ), + "neighbor {} tcp-mss {}".format( + topo["routers"]["r3"]["links"]["r2-link1"]["ipv6"].split("/")[0], + TCP_MSS, + ), + ] + }, + "r3": { + "raw_config": [ + "router bgp {} ".format(topo["routers"]["r3"]["bgp"][0]["local_as"]), + "neighbor {} tcp-mss {}".format( + topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ), + "neighbor {} tcp-mss {}".format( + topo["routers"]["r2"]["links"]["r3-link1"]["ipv6"].split("/")[0], + TCP_MSS, + ), + ] + }, + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + + + step("Clear BGP at router R2 and R3") + for addr_type in ADDR_TYPES: + clear_bgp(tgen, topo, "r2", addr_type) + clear_bgp(tgen, topo, "r3", addr_type) + + step("Verify the BGP Convergence at R2 & R3 after Clear BGP") + r1_convergence = verify_bgp_convergence(tgen, topo, dut="r2") + assert ( + r1_convergence is True + ), "BGP convergence after Clear BGP :Failed \n Error: {}".format(r2_convergence) + r2_convergence = verify_bgp_convergence(tgen, topo, dut="r3") + assert ( + r2_convergence is True + ), "BGP convergence after Clear BGP :Failed \n Error: {}".format(r2_convergence) + + step("Verify the TCP-MSS value on both Router R2 and R3") + for addr_type in ADDR_TYPES: + dut = "r2" + tcp_mss_result = verify_tcp_mss( + tgen, + dut, + topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ) + assert tcp_mss_result is True, " TCP-MSS mismatch :Failed \n Error: {}".format( + tcp_mss_result + ) + + dut = "r3" + tcp_mss_result = verify_tcp_mss( + tgen, + dut, + topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ) + assert tcp_mss_result is True, " TCP-MSS mismatch :Failed \n Error: {}".format( + tcp_mss_result + ) + + step("Removing tcp-mss 150 between R1 and R2 of VRF RED ") + TCP_MSS = 150 + raw_config = { + "r1": { + "raw_config": [ + "router bgp {} vrf {}".format( + topo["routers"]["r1"]["bgp"][0]["local_as"], + topo["routers"]["r1"]["bgp"][0]["vrf"], + ), + "no neighbor {} tcp-mss {}".format( + topo["routers"]["r2"]["links"]["r1-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ), + "no neighbor {} tcp-mss {}".format( + topo["routers"]["r2"]["links"]["r1-link1"]["ipv6"].split("/")[0], + TCP_MSS, + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + raw_config = { + "r2": { + "raw_config": [ + "router bgp {} vrf {}".format( + topo["routers"]["r2"]["bgp"][0]["local_as"], + topo["routers"]["r2"]["bgp"][1]["vrf"], + ), + "no neighbor {} tcp-mss {}".format( + topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ), + "no neighbor {} tcp-mss {}".format( + topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0], + TCP_MSS, + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify the TCP-MSS value cleared on both Router R1 and R2") + for addr_type in ADDR_TYPES: + dut = "r1" + tcp_mss_result = verify_tcp_mss( + tgen, + dut, + topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0], + TCP_MSS, + "RED", + ) + assert ( + tcp_mss_result is not True + ), " TCP-MSS mismatch :Failed \n Error: {}".format(tcp_mss_result) + + dut = "r2" + tcp_mss_result = verify_tcp_mss( + tgen, + dut, + topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0], + TCP_MSS, + "RED", + ) + assert ( + tcp_mss_result is not True + ), " TCP-MSS mismatch :Failed \n Error: {}".format(tcp_mss_result) + + + step("Removing tcp-mss 500 between R2 and R3 of VRF Default ") + TCP_MSS = 500 + raw_config = { + "r2": { + "raw_config": [ + "router bgp {} ".format(topo["routers"]["r2"]["bgp"][0]["local_as"]), + "no neighbor {} tcp-mss {}".format( + topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ), + "no neighbor {} tcp-mss {}".format( + topo["routers"]["r3"]["links"]["r2-link1"]["ipv6"].split("/")[0], + TCP_MSS, + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + raw_config = { + "r3": { + "raw_config": [ + "router bgp {} ".format(topo["routers"]["r3"]["bgp"][0]["local_as"]), + "no neighbor {} tcp-mss {}".format( + topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ), + "no neighbor {} tcp-mss {}".format( + topo["routers"]["r2"]["links"]["r3-link1"]["ipv6"].split("/")[0], + TCP_MSS, + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify the TCP-MSS value got cleared on both Router R2 and R3") + for addr_type in ADDR_TYPES: + dut = "r2" + tcp_mss_result = verify_tcp_mss( + tgen, + dut, + topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ) + assert ( + tcp_mss_result is not True + ), " TCP-MSS mismatch :Failed \n Error: {}".format(tcp_mss_result) + + dut = "r3" + tcp_mss_result = verify_tcp_mss( + tgen, + dut, + topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ) + assert ( + tcp_mss_result is not True + ), " TCP-MSS mismatch :Failed \n Error: {}".format(tcp_mss_result) + + step("Configuring different TCP-MSS R2 and R3 ") + TCP_MSS = 500 + raw_config = { + "r2": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r2"]["bgp"][0]["local_as"]), + "neighbor {} tcp-mss {}".format( + topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ), + "neighbor {} tcp-mss {}".format( + topo["routers"]["r3"]["links"]["r2-link1"]["ipv6"].split("/")[0], + TCP_MSS, + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + TCP_MSS = 300 + raw_config = { + "r3": { + "raw_config": [ + "router bgp {} ".format(topo["routers"]["r3"]["bgp"][0]["local_as"]), + "neighbor {} tcp-mss {}".format( + topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ), + "neighbor {} tcp-mss {}".format( + topo["routers"]["r2"]["links"]["r3-link1"]["ipv6"].split("/")[0], + TCP_MSS, + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify the TCP-MSS value on both Router R2 and R3") + for addr_type in ADDR_TYPES: + TCP_MSS = 500 + dut = "r2" + tcp_mss_result = verify_tcp_mss( + tgen, + dut, + topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ) + assert tcp_mss_result is True, " TCP-MSS mismatch :Failed \n Error: {}".format( + tcp_mss_result + ) + + TCP_MSS = 300 + dut = "r3" + tcp_mss_result = verify_tcp_mss( + tgen, + dut, + topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ) + assert tcp_mss_result is True, " TCP-MSS mismatch :Failed \n Error: {}".format( + tcp_mss_result + ) + + step("Configure TCP_MSS > MTU on R2 and R3 and it should be 1460 ") + TCP_MSS = 4096 + REF_TCP_MSS = 1460 + raw_config = { + "r2": { + "raw_config": [ + "router bgp {} ".format(topo["routers"]["r2"]["bgp"][0]["local_as"]), + "neighbor {} tcp-mss {}".format( + topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ), + "neighbor {} tcp-mss {}".format( + topo["routers"]["r3"]["links"]["r2-link1"]["ipv6"].split("/")[0], + TCP_MSS, + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + raw_config = { + "r3": { + "raw_config": [ + "router bgp {} ".format(topo["routers"]["r3"]["bgp"][0]["local_as"]), + "neighbor {} tcp-mss {}".format( + topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ), + "neighbor {} tcp-mss {}".format( + topo["routers"]["r2"]["links"]["r3-link1"]["ipv6"].split("/")[0], + TCP_MSS, + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Restarting BGP Daemon on R3") + + kill_router_daemons(tgen, "r3", ["bgpd"]) + start_router_daemons(tgen, "r3", ["bgpd"]) + + step( + "Verify the configured TCP-MSS 4096 value on restarting both Daemon both Router R2 and R3 " + ) + for addr_type in ADDR_TYPES: + TCP_MSS = 4096 + dut = "r2" + tcp_mss_result = verify_tcp_mss( + tgen, + dut, + topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ) + assert tcp_mss_result is True, " TCP-MSS mismatch :Failed \n Error: {}".format( + tcp_mss_result + ) + dut = "r3" + tcp_mss_result = verify_tcp_mss( + tgen, + dut, + topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0], + TCP_MSS, + ) + assert tcp_mss_result is True, " TCP-MSS mismatch :Failed \n Error: {}".format( + tcp_mss_result + ) + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_tcp_mss_passive/__init__.py b/tests/topotests/bgp_tcp_mss_passive/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_tcp_mss_passive/r1/frr.conf b/tests/topotests/bgp_tcp_mss_passive/r1/frr.conf new file mode 100644 index 0000000..a0fcd52 --- /dev/null +++ b/tests/topotests/bgp_tcp_mss_passive/r1/frr.conf @@ -0,0 +1,12 @@ +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 192.168.1.2 passive + neighbor 192.168.1.2 tcp-mss 300 +! diff --git a/tests/topotests/bgp_tcp_mss_passive/r2/frr.conf b/tests/topotests/bgp_tcp_mss_passive/r2/frr.conf new file mode 100644 index 0000000..7213975 --- /dev/null +++ b/tests/topotests/bgp_tcp_mss_passive/r2/frr.conf @@ -0,0 +1,10 @@ +! +interface r2-eth0 + ip address 192.168.1.2/24 +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 +! diff --git a/tests/topotests/bgp_tcp_mss_passive/test_bgp_tcp_mss_passive.py b/tests/topotests/bgp_tcp_mss_passive/test_bgp_tcp_mss_passive.py new file mode 100644 index 0000000..cd405f7 --- /dev/null +++ b/tests/topotests/bgp_tcp_mss_passive/test_bgp_tcp_mss_passive.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if TCP MSS is synced with passive neighbor. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_tcp_mss_passive(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_check_tcp_mss_configured(router, neighbor, mss): + output = json.loads(router.vtysh_cmd("show bgp neighbors json")) + expected = { + neighbor: { + "bgpTcpMssConfigured": mss, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_tcp_mss_configured, tgen.gears["r1"], "192.168.1.2", 300 + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "r1 is not configured with TCP MSS 300" + + test_func = functools.partial( + _bgp_check_tcp_mss_configured, tgen.gears["r2"], "192.168.1.1", 0 + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "r2 is not configured with the default TCP MSS (1500)" + + def _bgp_check_tcp_mss_synced(router, neighbor, mss): + output = json.loads(router.vtysh_cmd("show bgp neighbors json")) + expected = { + neighbor: { + "bgpTcpMssSynced": mss, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_tcp_mss_synced, tgen.gears["r1"], "192.168.1.2", 288 + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "r1 is not synced with TCP MSS 300" + + test_func = functools.partial( + _bgp_check_tcp_mss_synced, tgen.gears["r2"], "192.168.1.1", 288 + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "r2 is not synced with the default TCP MSS (1488)" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_unique_rid/bgp_unique_rid.json b/tests/topotests/bgp_unique_rid/bgp_unique_rid.json new file mode 100644 index 0000000..c42ce29 --- /dev/null +++ b/tests/topotests/bgp_unique_rid/bgp_unique_rid.json @@ -0,0 +1,505 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r5": { + "ipv4": "auto", + "ipv6": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r4-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link7": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {}, + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {}, + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + }, + "ospf": { + "router_id": "100.1.1.3", + "neighbors": { + "r4": {}, + "r5": {} + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link7": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {}, + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {}, + "r4-link5": {}, + "r4-link6": {}, + "r4-link7": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {}, + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {}, + "r4-link5": {}, + "r4-link6": {}, + "r4-link7": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + }, + "ospf": { + "router_id": "10.10.10.10", + "neighbors": { + "r3": {} + } + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + }, + "ospf": { + "router_id": "100.1.1.5", + "neighbors": { + "r3": {} + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_unique_rid/bgp_unique_rid_vrf.json b/tests/topotests/bgp_unique_rid/bgp_unique_rid_vrf.json new file mode 100644 index 0000000..1e280f1 --- /dev/null +++ b/tests/topotests/bgp_unique_rid/bgp_unique_rid_vrf.json @@ -0,0 +1,529 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback", + "vrf": "GREEN" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "GREEN" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "GREEN" + } + }, + "bgp": [ + { + "local_as": "100", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + } + ], + "vrfs": [ + { + "name": "GREEN", + "id": "1" + } + ] + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback", + "vrf": "GREEN" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "GREEN" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "GREEN" + } + }, + "bgp": [ + { + "local_as": "100", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + } + ], + "vrfs": [ + { + "name": "GREEN", + "id": "1" + } + ] + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback", + "vrf": "GREEN" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "GREEN" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "GREEN" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r5": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + }, + "r4-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link7": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [ + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {}, + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {}, + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + ], + "vrfs": [ + { + "name": "RED", + "id": "1" + }, + { + "name": "GREEN", + "id": "2" + } + ] + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link7": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {}, + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {}, + "r4-link5": {}, + "r4-link6": {}, + "r4-link7": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {}, + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {}, + "r4-link5": {}, + "r4-link6": {}, + "r4-link7": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + }] + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback", + "vrf": "RED" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto", + "vrf": "RED" + } + }, + "bgp": [ + { + "local_as": "300", + "vrf":"RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + } + ], + "vrfs": [ + { + "name": "RED", + "id": "1" + } + ] + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py new file mode 100644 index 0000000..f89f337 --- /dev/null +++ b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py @@ -0,0 +1,891 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +import sys +import time +import pytest +import inspect +import os +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +"""Following tests are covered to test bgp unique rid functionality. +1. Verify eBGP session when same and different router ID is configured. +2. Verify iBGP session when same and different router ID is configured. +3. Verify two different eBGP sessions initiated with same router ID. +4. Chaos - Verify bgp unique rid functionality in chaos scenarios. +5. Chaos - Verify bgp unique rid functionality when router reboots with same loopback id. +6. Chaos - Verify bgp unique rid functionality when router reboots without any ip addresses. +""" + +################################# +# TOPOLOGY +################################# +""" + + +-------+ + +--------- | R2 | + | +-------+ + |iBGP | + +-------+ | + | R1 | |iBGP + +-------+ | + | | + | iBGP +-------+ eBGP +-------+ + +---------- | R3 |========= | R4 | + +-------+ +-------+ + | + |eBGP + | + +-------+ + | R5 | + +-------+ + + +""" + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd, pytest.mark.staticd] + +# Required to instantiate the topology builder class. +from lib.common_config import ( + start_topology, + write_test_header, + step, + write_test_footer, + verify_rib, + check_address_types, + reset_config_on_routers, + check_router_status, + stop_router, + kill_router_daemons, + start_router_daemons, + start_router, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + clear_bgp_and_verify, +) + +# Global variables +topo = None +bgp_convergence = False +NETWORK = { + "ipv4": [ + "192.168.20.1/32", + "192.168.20.2/32", + "192.168.21.1/32", + "192.168.21.2/32", + "192.168.22.1/32", + "192.168.22.2/32", + ], + "ipv6": [ + "fc07:50::1/128", + "fc07:50::2/128", + "fc07:150::1/128", + "fc07:150::2/128", + "fc07:1::1/128", + "fc07:1::2/128", + ], +} + +bgp_convergence = False +ADDR_TYPES = check_address_types() +routerid = {"ipv4": "10.10.10.14", "ipv6": "fd00:0:0:3::2"} + + +def setup_module(mod): + """setup_module. + + Set up the pytest environment + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_unique_rid.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global bgp_convergence + global ADDR_TYPES + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format( + bgp_convergence + ) + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# Tests starting +##################################################### + + +def test_bgp_unique_rid_ebgp_p0(): + """ + TC: 1 + Verify eBGP session when same and different router ID is configured. + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure base config as per the topology") + reset_config_on_routers(tgen) + + step( + "Base config should be up, verify using BGP convergence on all \ + the routers for IPv4 and IPv6 nbrs" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure the same router id between R4 and R3 10.10.10.10") + input_dict = { + "r3": {"bgp": {"router_id": "10.10.10.10"}}, + "r4": {"bgp": {"router_id": "10.10.10.10"}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in ESTAB state.") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure the same router id between R5 and R3 (10.10.10.10)") + input_dict = { + "r3": {"bgp": {"router_id": "10.10.10.10"}}, + "r5": {"bgp": {"router_id": "10.10.10.10"}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in ESTAB state.") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("modify the router id on r3 to different router id (11.11.11.11)") + input_dict = {"r3": {"bgp": {"router_id": "11.11.11.11"}}} + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in ESTAB state.") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Reset bgp process") + step("Verify neighbours are in ESTAB state.") + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Clear ip bgp process with *") + step("Verify neighbours are in ESTAB state.") + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbours between R3 and R4 in EVPN address family.") + input_dict = { + "r3": { + "bgp": { + "address_family": { + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": {"unicast": {}}, + "ipv6": {"unicast": {}}, + } + } + } + } + } + }, + "r4": { + "bgp": { + "address_family": { + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": {"unicast": {}}, + "ipv6": {"unicast": {}}, + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in ESTAB state.") + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_bgp_unique_rid_ibgp_p0(): + """ + TC: 2 + Verify iBGP session when same and different router ID is configured. + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure base config as per the topology") + reset_config_on_routers(tgen) + + step( + "Base config should be up, verify using BGP convergence on all \ + the routers for IPv4 and IPv6 nbrs" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure the same router id between R1 and R3 (10.10.10.10)") + input_dict = { + "r3": {"bgp": {"router_id": "10.10.10.10"}}, + "r1": {"bgp": {"router_id": "10.10.10.10"}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in idle state.") + result = verify_bgp_convergence(tgen, topo, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure the same router id between R2 and R3 (10.10.10.10)") + input_dict = { + "r3": {"bgp": {"router_id": "10.10.10.10"}}, + "r2": {"bgp": {"router_id": "10.10.10.10"}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in idle state.") + result = verify_bgp_convergence(tgen, topo, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("modify the router id on r3 to different router id (11.11.11.11)") + input_dict = {"r3": {"bgp": {"router_id": "11.11.11.11"}}} + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in ESTAB state.") + result = verify_bgp_convergence(tgen, topo, dut="r3") + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Reset bgp process") + step("Verify neighbours are in ESTAB state.") + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Clear ip bgp process with *") + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_bgp_unique_rid_multi_bgp_nbrs_p0(): + """ + TC: 3 + 3. Verify two different eBGP sessions initiated with same router ID + + """ + tgen = get_topogen() + global bgp_convergence, topo + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure base config as per the topology") + reset_config_on_routers(tgen) + + step( + "Base config should be up, verify using BGP convergence on all \ + the routers for IPv4 and IPv6 nbrs" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure the same router id between R3, R4 and R5 (10.10.10.10)") + input_dict = { + "r3": {"bgp": {"router_id": "10.10.10.10"}}, + "r4": {"bgp": {"router_id": "10.10.10.10"}}, + "r5": {"bgp": {"router_id": "10.10.10.10"}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in ESTAB state.") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Configure the same IP address on on R4 and R5 loopback address and \ + change the neighborship to loopback neighbours between R3 to R4 \ + and R3 to R5 respectively." + ) + + topo1 = deepcopy(topo) + + for rtr in ["r4", "r5"]: + topo1["routers"][rtr]["links"]["lo"]["ipv4"] = "192.168.1.1/32" + + topo1["routers"]["r3"]["links"]["lo"]["ipv4"] = "192.168.1.3/32" + build_config_from_json(tgen, topo1, save_bkup=False) + + step( + "change the neighborship to loopback neighbours between R3 to R4 and R3 to R5 respectively." + ) + for rtr in ["r4", "r5"]: + configure_bgp_on_rtr = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"neighbor": {rtr: {"dest_link": {"lo": {}}}}} + } + } + }, + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_rtr) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + bgp_convergence = verify_bgp_convergence(tgen, topo1, dut="r3") + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step("Change the IP address on the R4 loopback.") + topo1["routers"]["r4"]["links"]["lo"]["ipv4"] = "192.168.1.4/32" + build_config_from_json(tgen, topo1, save_bkup=False) + + step("Verify neighbours should be again in ESTAB state. (show ip bgp neighbours)") + bgp_convergence = verify_bgp_convergence(tgen, topo1, dut="r3") + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step("Clear ip bgp process with *") + result = clear_bgp_and_verify(tgen, topo, router="r3") + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_bgp_unique_rid_chaos1_p2(): + """ + TC: 4 + 4. Chaos - Verify bgp unique rid functionality in chaos scenarios. + + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure base config as per the topology") + reset_config_on_routers(tgen) + + step( + "Base config should be up, verify using BGP convergence on all \ + the routers for IPv4 and IPv6 nbrs" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure the same router id between R3, R4 and R5 (10.10.10.10)") + input_dict = { + "r3": {"bgp": {"router_id": "10.10.10.10"}}, + "r4": {"bgp": {"router_id": "10.10.10.10"}}, + "r5": {"bgp": {"router_id": "10.10.10.10"}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in ESTAB state.") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify eBGP session when same router ID is configured and bgpd process is restarted" + ) + + # restart bgpd router and verify + kill_router_daemons(tgen, "r3", ["bgpd"]) + start_router_daemons(tgen, "r3", ["bgpd"]) + + step( + "The session should be established between R3 & R4. " + "Once after restart bgp, neighbor should come back up ." + ) + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step( + "Verify eBGP session when same router ID is configured and neighbor shutdown is issued and again no shutdown." + ) + + input_dict = { + "r3": { + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3-link1": {"shutdown": True}, + "r3-link2": {"shutdown": True}, + "r3-link3": {"shutdown": True}, + "r3-link4": {"shutdown": True}, + "r3-link5": {"shutdown": True}, + "r3-link6": {"shutdown": True}, + "r3-link7": {"shutdown": True}, + } + }, + "r5": {"dest_link": {"r3": {"shutdown": True}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3-link1": {"shutdown": True}, + "r3-link2": {"shutdown": True}, + "r3-link3": {"shutdown": True}, + "r3-link4": {"shutdown": True}, + "r3-link5": {"shutdown": True}, + "r3-link6": {"shutdown": True}, + "r3-link7": {"shutdown": True}, + } + }, + "r5": {"dest_link": {"r3": {"shutdown": True}}}, + } + } + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r3": { + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3-link1": {"shutdown": False}, + "r3-link2": {"shutdown": False}, + "r3-link3": {"shutdown": False}, + "r3-link4": {"shutdown": False}, + "r3-link5": {"shutdown": False}, + "r3-link6": {"shutdown": False}, + "r3-link7": {"shutdown": False}, + } + }, + "r5": {"dest_link": {"r3": {"shutdown": False}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3-link1": {"shutdown": False}, + "r3-link2": {"shutdown": False}, + "r3-link3": {"shutdown": False}, + "r3-link4": {"shutdown": False}, + "r3-link5": {"shutdown": False}, + "r3-link6": {"shutdown": False}, + "r3-link7": {"shutdown": False}, + } + }, + "r5": {"dest_link": {"r3": {"shutdown": False}}}, + } + } + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "The session should be established between R3 & R4. " + "Once after restart bgp, neighbor should come back up ." + ) + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step( + "Verify eBGP session when same router ID is configured and neighbor config is deleted & reconfigured." + ) + + input_dict = { + "r3": { + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {}, + } + }, + "r5": {"dest_link": {"r3": {}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {}, + } + }, + "r5": {"dest_link": {"r3": {}}}, + } + } + }, + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "The session should be established between R3 & R4. " + "Once after restart bgp, neighbor should come back up ." + ) + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step( + "Verify eBGP session when same router ID is configured and FRR router is restarted." + ) + stop_router(tgen, "r3") + start_router(tgen, "r3") + + step( + "The session should be established between R3 & R4. " + "Once after restart bgp, neighbor should come back up ." + ) + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step( + "Verify eBGP session when same router ID is configured and zebra process is restarted" + ) + + kill_router_daemons(tgen, "r3", ["zebra"]) + start_router_daemons(tgen, "r3", ["zebra"]) + + step( + "The session should be established between R3 & R4. " + "Once after restart bgp, neighbor should come back up ." + ) + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + write_test_footer(tc_name) + + +def test_bgp_unique_rid_chaos3_p2(): + """ + TC: 4 + 4. Chaos - Verify bgp unique rid functionality when router reboots with same loopback id. + + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure base config as per the topology") + reset_config_on_routers(tgen) + + global topo + topo1 = deepcopy(topo) + + for rtr in topo["routers"].keys(): + topo1["routers"][rtr]["links"]["lo"]["ipv4"] = "192.168.1.1/32" + + topo1["routers"]["r3"]["links"]["lo"]["ipv4"] = "192.168.1.3/32" + build_config_from_json(tgen, topo1, save_bkup=False) + + step("verify bgp convergence before starting test case") + + bgp_convergence = verify_bgp_convergence(tgen, topo1) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step( + "Configure loopback on R1 to R5 with IP address 1.1.1.1 on all the routers. Change neighborship on all the routers using loopback neighborship ids." + ) + for rtr in ["r1", "r2", "r4", "r5"]: + configure_bgp_on_rtr = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"neighbor": {rtr: {"dest_link": {"lo": {}}}}} + } + } + }, + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_rtr) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + bgp_convergence = verify_bgp_convergence(tgen, topo1, dut="r3") + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step("Reboot the router (restart frr) or using watch frr.") + stop_router(tgen, "r3") + start_router(tgen, "r3") + + step("Neighbors between R3, R4 and R3 to R5 should be in ESTB state.") + bgp_convergence = verify_bgp_convergence(tgen, topo1, dut="r3") + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step("Clear bgp process.") + clear_bgp_and_verify(tgen, topo, "r3") + + step("Neighbors between R3, R4 and R3 to R5 should be in ESTB state.") + bgp_convergence = verify_bgp_convergence(tgen, topo1, dut="r3") + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + write_test_footer(tc_name) + + +def test_bgp_unique_rid_chaos4_p2(): + """ + TC: 6 + 6. Chaos - Verify bgp unique rid functionality when router reboots without any ip addresses. + + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + reset_config_on_routers(tgen) + + global topo + topo1 = deepcopy(topo) + topo2 = deepcopy(topo) + + step( + "Configure base config as per the topology without loopback as well as Ip address on any of the interface." + ) + for rtr in topo["routers"].keys(): + for intf in topo["routers"][rtr]["links"].keys(): + topo1["routers"][rtr]["links"][intf].pop("ipv4") + topo1["routers"][rtr]["links"][intf].pop("ipv6") + + build_config_from_json(tgen, topo1, save_bkup=False) + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step("Configure the ip addresses on the physical interfaces") + build_config_from_json(tgen, topo2, save_bkup=False) + + step("All the neighbors should be in ESTAB state.") + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step("Configure loopback addresses with higher IP address ") + build_config_from_json(tgen, topo, save_bkup=False) + + step("All the neighbors should be in ESTAB state.") + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step("Reboot the router (restart frr) or using watch frr.") + stop_router(tgen, "r3") + start_router(tgen, "r3") + + step("Neighbors between R3, R4 and R3 to R5 should be in ESTB state.") + bgp_convergence = verify_bgp_convergence(tgen, topo, dut="r3") + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_unique_rid/test_bgp_unique_rid_vrf.py b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid_vrf.py new file mode 100644 index 0000000..2a9e42d --- /dev/null +++ b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid_vrf.py @@ -0,0 +1,466 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +import sys +import time +import pytest +import inspect +import os +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +"""Following tests are covered to test bgp unique rid functionality. +1. Verify iBGP session when same and different router ID is configured in user VRF(GREEN). +2. Verify eBGP session when same and different router ID is configured in user vrf (VRF RED) +3. Verify two different eBGP sessions initiated with same router ID in user VRf (RED and GREEN) +""" + +################################# +# TOPOLOGY +################################# +""" + + +-------+ + +--------- | R2 | + | +-------+ + |iBGP | + +-------+ | + | R1 | |iBGP + +-------+ | + | | + | iBGP +-------+ eBGP +-------+ + +---------- | R3 |========= | R4 | + +-------+ +-------+ + | + |eBGP + | + +-------+ + | R5 | + +-------+ + + +""" + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Required to instantiate the topology builder class. +from lib.common_config import ( + start_topology, + write_test_header, + step, + write_test_footer, + check_address_types, + reset_config_on_routers, + check_router_status, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + clear_bgp_and_verify, +) + +# Global variables +topo = None +bgp_convergence = False +NETWORK = { + "ipv4": [ + "192.168.20.1/32", + "192.168.20.2/32", + "192.168.21.1/32", + "192.168.21.2/32", + "192.168.22.1/32", + "192.168.22.2/32", + ], + "ipv6": [ + "fc07:50::1/128", + "fc07:50::2/128", + "fc07:150::1/128", + "fc07:150::2/128", + "fc07:1::1/128", + "fc07:1::2/128", + ], +} + +bgp_convergence = False +ADDR_TYPES = check_address_types() + + +def setup_module(mod): + """setup_module. + + Set up the pytest environment + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_unique_rid_vrf.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global bgp_convergence + global ADDR_TYPES + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format( + bgp_convergence + ) + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# Tests starting +##################################################### + + +def test_bgp_unique_rid_ebgp_vrf_p0(): + """ + TC: 1 + Verify iBGP session when same and different router ID is configured in user VRF(GREEN). + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure base config as per the topology") + reset_config_on_routers(tgen) + + step( + "Base config should be up, verify using BGP convergence on all \ + the routers for IPv4 and IPv6 nbrs" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure the same router id between R4 and R3 10.10.10.10") + input_dict = { + "r3": {"bgp": {"router_id": "10.10.10.10", "local_as": 100, "vrf": "RED"}}, + "r4": {"bgp": {"router_id": "10.10.10.10", "local_as": 200, "vrf": "RED"}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in ESTAB state.") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure the same router id between R5 and R3 (10.10.10.10)") + input_dict = { + "r3": {"bgp": {"router_id": "10.10.10.10", "local_as": 100, "vrf": "RED"}}, + "r5": {"bgp": {"router_id": "10.10.10.10", "local_as": 300, "vrf": "RED"}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in ESTAB state.") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("modify the router id on r3 to different router id (11.11.11.11)") + input_dict = { + "r3": {"bgp": {"router_id": "11.11.11.11", "local_as": 100, "vrf": "RED"}} + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in ESTAB state.") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Reset bgp process") + step("Verify neighbours are in ESTAB state.") + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, router="r3") + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Clear ip bgp process with *") + step("Verify neighbours are in ESTAB state.") + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbours between R3 and R4 in EVPN address family.") + input_dict = { + "r3": { + "bgp": { + "local_as": 100, + "vrf": "RED", + "address_family": { + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": {"unicast": {}}, + "ipv6": {"unicast": {}}, + } + } + } + }, + } + }, + "r4": { + "bgp": { + "local_as": 200, + "vrf": "RED", + "address_family": { + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": {"unicast": {}}, + "ipv6": {"unicast": {}}, + } + } + } + }, + } + }, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in ESTAB state.") + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_bgp_unique_rid_ibgp_vrf_p0(): + """ + TC: 2 + Verify eBGP session when same and different router ID is configured in user vrf (VRF RED) + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure base config as per the topology") + reset_config_on_routers(tgen) + + step( + "Base config should be up, verify using BGP convergence on all \ + the routers for IPv4 and IPv6 nbrs" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure the same router id between R1 and R3 (10.10.10.10)") + input_dict = { + "r3": {"bgp": {"router_id": "10.10.10.10", "local_as": 100, "vrf": "RED"}}, + "r1": {"bgp": {"router_id": "10.10.10.10", "local_as": 100, "vrf": "RED"}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in ESTAB state.") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure the same router id between R2 and R3 (10.10.10.10)") + input_dict = { + "r3": {"bgp": {"router_id": "10.10.10.10", "local_as": 100, "vrf": "RED"}}, + "r2": {"bgp": {"router_id": "10.10.10.10", "local_as": 100, "vrf": "RED"}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in ESTAB state.") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("modify the router id on r3 to different router id (11.11.11.11)") + input_dict = { + "r3": {"bgp": {"router_id": "11.11.11.11", "local_as": 100, "vrf": "RED"}} + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in ESTAB state.") + result = verify_bgp_convergence(tgen, topo, dut="r3") + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Reset bgp process") + step("Verify neighbours are in ESTAB state.") + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Clear ip bgp process with *") + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_bgp_unique_rid_multi_bgp_nbrs_vrf_p0(): + """ + TC: 3 + Verify two different eBGP sessions initiated with same router ID in user VRf (RED and GREEN) + + """ + tgen = get_topogen() + global bgp_convergence, topo + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure base config as per the topology") + reset_config_on_routers(tgen) + + step( + "Base config should be up, verify using BGP convergence on all \ + the routers for IPv4 and IPv6 nbrs" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure the same router id between R3, R4 and R5 (10.10.10.10)") + input_dict = { + "r3": {"bgp": {"router_id": "10.10.10.10", "local_as": 100, "vrf": "RED"}}, + "r4": {"bgp": {"router_id": "10.10.10.10", "local_as": 200, "vrf": "RED"}}, + "r5": {"bgp": {"router_id": "10.10.10.10", "local_as": 300, "vrf": "RED"}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify neighbours are in ESTAB state.") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Configure the same IP address on on R4 and R5 loopback address and \ + change the neighborship to loopback neighbours between R3 to R4 \ + and R3 to R5 respectively." + ) + + topo1 = deepcopy(topo) + + for rtr in ["r4", "r5"]: + topo1["routers"][rtr]["links"]["lo"]["ipv4"] = "192.168.1.1/32" + + topo1["routers"]["r3"]["links"]["lo"]["ipv4"] = "192.168.1.3/32" + build_config_from_json(tgen, topo1, save_bkup=False) + + step( + "change the neighborship to loopback neighbours between R3 to R4 and R3 to R5 respectively." + ) + for rtr in ["r4", "r5"]: + configure_bgp_on_rtr = { + "r3": { + "bgp": { + "local_as": 100, + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": {"neighbor": {rtr: {"dest_link": {"lo": {}}}}} + } + }, + }, + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_rtr) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + bgp_convergence = verify_bgp_convergence(tgen, topo1, dut="r3") + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step("Change the IP address on the R4 loopback.") + topo1["routers"]["r4"]["links"]["lo"]["ipv4"] = "192.168.1.4/32" + build_config_from_json(tgen, topo1, save_bkup=False) + + step("Verify neighbours should be again in ESTAB state. (show ip bgp neighbours)") + bgp_convergence = verify_bgp_convergence(tgen, topo1, dut="r3") + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step("Clear ip bgp process with *") + result = clear_bgp_and_verify(tgen, topo, router="r3") + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_unnumbered/__init__.py b/tests/topotests/bgp_unnumbered/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_unnumbered/r1/bgpd.conf b/tests/topotests/bgp_unnumbered/r1/bgpd.conf new file mode 100644 index 0000000..a9d0ec8 --- /dev/null +++ b/tests/topotests/bgp_unnumbered/r1/bgpd.conf @@ -0,0 +1,9 @@ +! +router bgp 65001 + timers bgp 1 9 + no bgp ebgp-requires-policy + neighbor r1-eth0 interface remote-as external + address-family ipv4 unicast + exit-address-family + ! +! diff --git a/tests/topotests/bgp_unnumbered/r1/zebra.conf b/tests/topotests/bgp_unnumbered/r1/zebra.conf new file mode 100644 index 0000000..1cbaea4 --- /dev/null +++ b/tests/topotests/bgp_unnumbered/r1/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 172.16.250.254/32 +! +interface r1-eth0 + ip address 192.168.0.1/24 +! +ip forwarding +! + diff --git a/tests/topotests/bgp_unnumbered/r2/bgpd.conf b/tests/topotests/bgp_unnumbered/r2/bgpd.conf new file mode 100644 index 0000000..fd29cd3 --- /dev/null +++ b/tests/topotests/bgp_unnumbered/r2/bgpd.conf @@ -0,0 +1,9 @@ +! +router bgp 65002 + no bgp network import-check + no bgp ebgp-requires-policy + timers bgp 1 9 + neighbor r2-eth0 interface remote-as external + address-family ipv4 uni + network 172.16.255.254/32 +! diff --git a/tests/topotests/bgp_unnumbered/r2/zebra.conf b/tests/topotests/bgp_unnumbered/r2/zebra.conf new file mode 100644 index 0000000..cf6fb6d --- /dev/null +++ b/tests/topotests/bgp_unnumbered/r2/zebra.conf @@ -0,0 +1,12 @@ +! +interface r2-eth0 + ip address 192.168.0.2/24 +! +interface r2-eth1 + ip address 192.168.1.1/24 +! +interface r2-eth2 + ip address 192.168.2.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_unnumbered/test_bgp_unnumbered.py b/tests/topotests/bgp_unnumbered/test_bgp_unnumbered.py new file mode 100644 index 0000000..2a53547 --- /dev/null +++ b/tests/topotests/bgp_unnumbered/test_bgp_unnumbered.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 by +# Donald Sharp +# + +""" +Test some bgp interface based issues that show up +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + + tgen.add_router("r1") + tgen.add_router("r2") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +# +# Test these events: +# a) create an unnumbered neighbor +# b) shutdown the interface +# c) remove the unnumbered peer in bgp and bgp does not crash +def test_bgp_unnumbered_removal(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_nexthop_cache(): + output = tgen.gears["r1"].vtysh_cmd("show bgp nexthop") + expected = "Current BGP nexthop cache:\n" + return output == expected + + def _bgp_converge(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show ip bgp 172.16.255.254/32 json") + ) + expected = {"prefix": "172.16.255.254/32"} + + return topotest.json_cmp(output, expected) + + step("Ensure Convergence of BGP") + test_func = functools.partial(_bgp_converge) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r2"]) + + step("Shutdown interface r1-eth0") + + tgen.gears["r1"].vtysh_cmd( + """ + configure + int r1-eth0 + shutdown + """ + ) + + step("Remove the neighbor from r1") + tgen.gears["r1"].vtysh_cmd( + """ + configure + router bgp + no neighbor r1-eth0 interface remote-as external + """ + ) + + step("Ensure that BGP does not crash") + test_func = functools.partial(_bgp_nexthop_cache) + success, result = topotest.run_and_expect(test_func, True, count=10, wait=1) + + assert result is True, "BGP did not crash on r1" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_update_delay/__init__.py b/tests/topotests/bgp_update_delay/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_update_delay/r1/bgpd.conf b/tests/topotests/bgp_update_delay/r1/bgpd.conf new file mode 100644 index 0000000..8ebb509 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r1/bgpd.conf @@ -0,0 +1,10 @@ +! exit1 +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65002 + neighbor 192.168.255.1 timers connect 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_update_delay/r1/zebra.conf b/tests/topotests/bgp_update_delay/r1/zebra.conf new file mode 100644 index 0000000..9904bb4 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r1/zebra.conf @@ -0,0 +1,9 @@ +! exit1 +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_update_delay/r2/bgpd.conf b/tests/topotests/bgp_update_delay/r2/bgpd.conf new file mode 100644 index 0000000..438f995 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r2/bgpd.conf @@ -0,0 +1,18 @@ +! spine +router bgp 65002 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.254.2 remote-as 65003 + neighbor 192.168.253.2 remote-as 65004 + neighbor 192.168.255.2 timers connect 10 + neighbor 192.168.254.2 timers connect 10 + neighbor 192.168.253.2 timers connect 10 +! + router bgp 65002 vrf vrf1 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.252.2 remote-as 65005 + neighbor 192.168.252.2 timers connect 10 + ! +! diff --git a/tests/topotests/bgp_update_delay/r2/zebra.conf b/tests/topotests/bgp_update_delay/r2/zebra.conf new file mode 100644 index 0000000..1fcedaa --- /dev/null +++ b/tests/topotests/bgp_update_delay/r2/zebra.conf @@ -0,0 +1,16 @@ +! spine +interface r2-eth0 + ip address 192.168.255.1/30 +! +interface r2-eth1 + ip address 192.168.254.1/30 +! +interface r2-eth2 + ip address 192.168.253.1/30 +! +interface r2-eth3 + ip address 192.168.252.1/30 + vrf vrf1 +! +ip forwarding +! diff --git a/tests/topotests/bgp_update_delay/r3/bgpd.conf b/tests/topotests/bgp_update_delay/r3/bgpd.conf new file mode 100644 index 0000000..53e5178 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r3/bgpd.conf @@ -0,0 +1,10 @@ +! exit2 +router bgp 65003 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.254.1 remote-as 65002 + neighbor 192.168.254.1 timers connect 10 + address-family ipv4 unicast + redistribute connected + ! +! diff --git a/tests/topotests/bgp_update_delay/r3/zebra.conf b/tests/topotests/bgp_update_delay/r3/zebra.conf new file mode 100644 index 0000000..f490d97 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r3/zebra.conf @@ -0,0 +1,9 @@ +! exit2 +interface lo + ip address 172.16.254.254/32 +! +interface r3-eth0 + ip address 192.168.254.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_update_delay/r4/bgpd.conf b/tests/topotests/bgp_update_delay/r4/bgpd.conf new file mode 100644 index 0000000..34cb429 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r4/bgpd.conf @@ -0,0 +1,11 @@ +! exit2 +router bgp 65004 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.253.1 remote-as 65002 + neighbor 192.168.253.1 timers connect 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_update_delay/r4/zebra.conf b/tests/topotests/bgp_update_delay/r4/zebra.conf new file mode 100644 index 0000000..baba04c --- /dev/null +++ b/tests/topotests/bgp_update_delay/r4/zebra.conf @@ -0,0 +1,9 @@ +! exit2 +interface lo + ip address 172.16.253.254/32 +! +interface r4-eth0 + ip address 192.168.253.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_update_delay/r5/bgpd.conf b/tests/topotests/bgp_update_delay/r5/bgpd.conf new file mode 100644 index 0000000..66ecc70 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r5/bgpd.conf @@ -0,0 +1,11 @@ +! exit1 +router bgp 65005 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.252.1 remote-as 65002 + neighbor 192.168.252.1 timers connect 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_update_delay/r5/zebra.conf b/tests/topotests/bgp_update_delay/r5/zebra.conf new file mode 100644 index 0000000..8adf6f8 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r5/zebra.conf @@ -0,0 +1,9 @@ +! exit1 +interface lo + ip address 172.16.252.254/32 +! +interface r1-eth0 + ip address 192.168.252.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_update_delay/test_bgp_update_delay.py b/tests/topotests/bgp_update_delay/test_bgp_update_delay.py new file mode 100644 index 0000000..4e66cf5 --- /dev/null +++ b/tests/topotests/bgp_update_delay/test_bgp_update_delay.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_update_delay.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Don Slice +# + +""" +Test the ability to define update-delay to delay bestpath, rib install +and advertisement to peers when frr is started, restarted or "clear ip +bgp *" is performed. Test both the vrf-specific and global configuration +and operation. + +r1 +| +r2----r3 +| \ +| \ +r5 r4 + + +r2 is UUT and peers with r1, r3, and r4 in default bgp instance. +r2 peers with r5 in vrf vrf1. + +Check r2 initial convergence in default table +Define update-delay with max-delay in the default bgp instance on r2 +Shutdown peering on r1 toward r2 so that delay timers can be exercised +Clear bgp neighbors on r2 and then check for the 'in progress' indicator +Check that r2 only installs route learned from r4 after the max-delay timer expires +Define update-delay with max-delay and estabish-wait and check json output showing set +Clear neighbors on r2 and check that r3 installs route from r4 after establish-wait time +Remove update-delay timer on r2 to verify that it goes back to normal behavior +Clear neighbors on r2 and check that route install time on r2 does not delay +Define global bgp update-delay with max-delay and establish-wait on r2 +Check that r2 default instance and vrf1 have the max-delay and establish set +Clear neighbors on r2 and check route-install time is after the establish-wait timer + +Note that the keepalive/hold times were changed to 3/9 and the connect retry timer +to 10 to improve the odds the convergence timing in this test case is useful in the +event of packet loss. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +CWD = os.path.dirname(os.path.realpath(__file__)) + + +def build_topo(tgen): + for routern in range(1, 6): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r5"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_update_delay(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # initial convergence without update-delay defined + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_check_update_delay(router): + output = json.loads(router.vtysh_cmd("show ip bgp sum json")) + expected = {"ipv4Unicast": {"updateDelayLimit": 20}} + + return topotest.json_cmp(output, expected) + + def _bgp_check_update_delay_in_progress(router): + output = json.loads(router.vtysh_cmd("show ip bgp sum json")) + expected = {"ipv4Unicast": {"updateDelayInProgress": True}} + + return topotest.json_cmp(output, expected) + + def _bgp_check_route_install(router): + output = json.loads(router.vtysh_cmd("show ip route 172.16.253.254/32 json")) + expected = {"172.16.253.254/32": [{"protocol": "bgp"}]} + + return topotest.json_cmp(output, expected) + + def _bgp_check_update_delay_and_wait(router): + output = json.loads(router.vtysh_cmd("show ip bgp sum json")) + expected = { + "ipv4Unicast": {"updateDelayLimit": 20, "updateDelayEstablishWait": 10} + } + + return topotest.json_cmp(output, expected) + + def _bgp_check_update_delay(router): + output = json.loads(router.vtysh_cmd("show ip bgp sum json")) + expected = {"ipv4Unicast": {"updateDelayLimit": 20}} + + return topotest.json_cmp(output, expected) + + def _bgp_check_vrf_update_delay_and_wait(router): + output = json.loads(router.vtysh_cmd("show ip bgp vrf vrf1 sum json")) + expected = { + "ipv4Unicast": {"updateDelayLimit": 20, "updateDelayEstablishWait": 10} + } + + return topotest.json_cmp(output, expected) + + # Check r2 initial convergence in default table + test_func = functools.partial(_bgp_converge, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed bgp convergence in "{}"'.format(router2) + + # Define update-delay with max-delay in the default bgp instance on r2 + router2.vtysh_cmd( + """ + configure terminal + router bgp 65002 + update-delay 20 + """ + ) + + # Shutdown peering on r1 toward r2 so that delay timers can be exercised + router1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + neighbor 192.168.255.1 shut + """ + ) + + # Clear bgp neighbors on r2 and then check for the 'in progress' indicator + router2.vtysh_cmd("""clear ip bgp *""") + + test_func = functools.partial(_bgp_check_update_delay_in_progress, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to set update-delay max-delay timer "{}"'.format( + router2 + ) + + # Check that r2 only installs route learned from r4 after the max-delay timer expires + test_func = functools.partial(_bgp_check_route_install, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to install route after update-delay "{}"'.format( + router2 + ) + + # Define update-delay with max-delay and estabish-wait and check json output showing set + router2.vtysh_cmd( + """ + configure terminal + router bgp 65002 + update-delay 20 10 + """ + ) + + test_func = functools.partial(_bgp_check_update_delay_and_wait, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert ( + result is None + ), 'Failed to set max-delay and establish-weight timers in "{}"'.format(router2) + + # Define update-delay with max-delay and estabish-wait and check json output showing set + router2.vtysh_cmd("""clear ip bgp *""") + + test_func = functools.partial(_bgp_check_route_install, router3) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert ( + result is None + ), 'Failed to installed advertised route after establish-wait timer espired "{}"'.format( + router2 + ) + + # Remove update-delay timer on r2 to verify that it goes back to normal behavior + router2.vtysh_cmd( + """ + configure terminal + router bgp 65002 + no update-delay + """ + ) + + # Clear neighbors on r2 and check that route install time on r2 does not delay + router2.vtysh_cmd("""clear ip bgp *""") + + test_func = functools.partial(_bgp_check_route_install, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to remove update-delay delay timing "{}"'.format( + router2 + ) + + # Define global bgp update-delay with max-delay and establish-wait on r2 + router2.vtysh_cmd( + """ + configure terminal + bgp update-delay 20 10 + """ + ) + + # Check that r2 default instance and vrf1 have the max-delay and establish set + test_func = functools.partial(_bgp_check_update_delay_and_wait, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to set update-delay in default instance "{}"'.format( + router2 + ) + + test_func = functools.partial(_bgp_check_vrf_update_delay_and_wait, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to set update-delay in vrf1 "{}"'.format(router2) + + # Clear neighbors on r2 and check route-install time is after the establish-wait timer + router2.vtysh_cmd("""clear ip bgp *""") + + test_func = functools.partial(_bgp_check_route_install, router3) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert ( + result is None + ), 'Failed to installed advertised route after establish-wait timer espired "{}"'.format( + router2 + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vpn_5549_route_map/__init__.py b/tests/topotests/bgp_vpn_5549_route_map/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_vpn_5549_route_map/cpe1/bgpd.conf b/tests/topotests/bgp_vpn_5549_route_map/cpe1/bgpd.conf new file mode 100644 index 0000000..013cd8c --- /dev/null +++ b/tests/topotests/bgp_vpn_5549_route_map/cpe1/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_vpn_5549_route_map/cpe1/zebra.conf b/tests/topotests/bgp_vpn_5549_route_map/cpe1/zebra.conf new file mode 100644 index 0000000..49dcfc3 --- /dev/null +++ b/tests/topotests/bgp_vpn_5549_route_map/cpe1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.1/32 +! +interface cpe1-eth0 + ip address 192.168.1.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_vpn_5549_route_map/cpe2/bgpd.conf b/tests/topotests/bgp_vpn_5549_route_map/cpe2/bgpd.conf new file mode 100644 index 0000000..d65d507 --- /dev/null +++ b/tests/topotests/bgp_vpn_5549_route_map/cpe2/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 +! diff --git a/tests/topotests/bgp_vpn_5549_route_map/cpe2/zebra.conf b/tests/topotests/bgp_vpn_5549_route_map/cpe2/zebra.conf new file mode 100644 index 0000000..a47319e --- /dev/null +++ b/tests/topotests/bgp_vpn_5549_route_map/cpe2/zebra.conf @@ -0,0 +1,6 @@ +! +interface cpe2-eth0 + ip address 192.168.2.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe1/bgpd.conf b/tests/topotests/bgp_vpn_5549_route_map/pe1/bgpd.conf new file mode 100644 index 0000000..93da025 --- /dev/null +++ b/tests/topotests/bgp_vpn_5549_route_map/pe1/bgpd.conf @@ -0,0 +1,38 @@ +router bgp 65001 + bgp router-id 10.10.10.10 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001:db8:1::2 remote-as internal + neighbor 2001:db8:1::2 update-source 2001:db8:1::1 + neighbor 2001:db8:1::2 timers 1 3 + neighbor 2001:db8:1::2 timers connect 1 + neighbor 2001:db8:1::2 capability extended-nexthop + address-family ipv4 vpn + neighbor 2001:db8:1::2 activate + neighbor 2001:db8:1::2 route-map pe2 out + exit-address-family +! +router bgp 65001 vrf RED + bgp router-id 192.168.1.2 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + address-family ipv4 unicast + label vpn export 1111 + rd vpn export 192.168.1.2:2 + rt vpn import 192.168.2.2:2 192.168.1.2:2 + rt vpn export 192.168.1.2:2 + export vpn + import vpn + exit-address-family +! +ip prefix-list cpe1 seq 5 permit 172.16.255.1/32 +! +route-map pe2 permit 10 + match ip address prefix-list cpe1 + set ipv6 vpn next-hop 2001:db8::1 +! +route-map pe2 permit 20 +exit +! diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe1/ldpd.conf b/tests/topotests/bgp_vpn_5549_route_map/pe1/ldpd.conf new file mode 100644 index 0000000..fb40f06 --- /dev/null +++ b/tests/topotests/bgp_vpn_5549_route_map/pe1/ldpd.conf @@ -0,0 +1,10 @@ +mpls ldp + router-id 10.10.10.10 + ! + address-family ipv4 + discovery transport-address 10.10.10.10 + ! + interface pe1-eth1 + ! + ! +! diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe1/ospf6d.conf b/tests/topotests/bgp_vpn_5549_route_map/pe1/ospf6d.conf new file mode 100644 index 0000000..0053d1e --- /dev/null +++ b/tests/topotests/bgp_vpn_5549_route_map/pe1/ospf6d.conf @@ -0,0 +1,12 @@ +! +interface lo + ipv6 ospf6 area 0 +! +interface pe1-eth1 + ipv6 ospf6 area 0 + ipv6 ospf6 hello-interval 1 + ipv6 ospf6 dead-interval 3 +! +router ospf6 + ospf6 router-id 10.10.10.10 +! diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe1/zebra.conf b/tests/topotests/bgp_vpn_5549_route_map/pe1/zebra.conf new file mode 100644 index 0000000..da91055 --- /dev/null +++ b/tests/topotests/bgp_vpn_5549_route_map/pe1/zebra.conf @@ -0,0 +1,14 @@ +! +interface lo + ip address 10.10.10.10/32 + ipv6 address 2001:db8:1::1/128 +! +interface pe1-eth0 vrf RED + ip address 192.168.1.2/24 +! +interface pe1-eth1 + ip address 10.0.1.1/24 + ipv6 address 2001:db8::1/64 +! +ip forwarding +! diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe2/bgpd.conf b/tests/topotests/bgp_vpn_5549_route_map/pe2/bgpd.conf new file mode 100644 index 0000000..6db1eef --- /dev/null +++ b/tests/topotests/bgp_vpn_5549_route_map/pe2/bgpd.conf @@ -0,0 +1,29 @@ +router bgp 65001 + bgp router-id 10.10.10.20 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001:db8:1::1 remote-as internal + neighbor 2001:db8:1::1 update-source 2001:db8:1::2 + neighbor 2001:db8:1::1 timers 1 3 + neighbor 2001:db8:1::1 timers connect 1 + neighbor 2001:db8:1::1 capability extended-nexthop + address-family ipv4 vpn + neighbor 2001:db8:1::1 activate + exit-address-family +! +router bgp 65001 vrf RED + bgp router-id 192.168.2.2 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 + address-family ipv4 unicast + label vpn export 2222 + rd vpn export 192.168.2.2:2 + rt vpn import 192.168.2.2:2 192.168.1.2:2 + rt vpn export 192.168.2.2:2 + export vpn + import vpn + exit-address-family +! + diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe2/ldpd.conf b/tests/topotests/bgp_vpn_5549_route_map/pe2/ldpd.conf new file mode 100644 index 0000000..e2b5359 --- /dev/null +++ b/tests/topotests/bgp_vpn_5549_route_map/pe2/ldpd.conf @@ -0,0 +1,10 @@ +mpls ldp + router-id 10.10.10.20 + ! + address-family ipv4 + discovery transport-address 10.10.10.20 + ! + interface pe2-eth0 + ! + ! +! diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe2/ospf6d.conf b/tests/topotests/bgp_vpn_5549_route_map/pe2/ospf6d.conf new file mode 100644 index 0000000..f79bb4f --- /dev/null +++ b/tests/topotests/bgp_vpn_5549_route_map/pe2/ospf6d.conf @@ -0,0 +1,12 @@ +! +interface lo + ipv6 ospf6 area 0 +! +interface pe2-eth0 + ipv6 ospf6 area 0 + ipv6 ospf6 hello-interval 1 + ipv6 ospf6 dead-interval 3 +! +router ospf6 + ospf6 router-id 10.10.10.20 +! diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe2/zebra.conf b/tests/topotests/bgp_vpn_5549_route_map/pe2/zebra.conf new file mode 100644 index 0000000..19ef7bf --- /dev/null +++ b/tests/topotests/bgp_vpn_5549_route_map/pe2/zebra.conf @@ -0,0 +1,14 @@ +! +interface lo + ip address 10.10.10.20/32 + ipv6 address 2001:db8:1::2/128 +! +interface pe2-eth1 vrf RED + ip address 192.168.2.2/24 +! +interface pe2-eth0 + ip address 10.0.1.2/24 + ipv6 address 2001:db8::2/64 +! +ip forwarding +! diff --git a/tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py b/tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py new file mode 100644 index 0000000..eb29875 --- /dev/null +++ b/tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +Check if we can override VPN underlay next-hop from PE1 to PE2. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("cpe1") + tgen.add_router("cpe2") + tgen.add_router("pe1") + tgen.add_router("pe2") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["cpe1"]) + switch.add_link(tgen.gears["pe1"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["pe1"]) + switch.add_link(tgen.gears["pe2"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["pe2"]) + switch.add_link(tgen.gears["cpe2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + pe1 = tgen.gears["pe1"] + pe2 = tgen.gears["pe2"] + + pe1.run("ip link add RED type vrf table 1001") + pe1.run("ip link set up dev RED") + pe2.run("ip link add RED type vrf table 1001") + pe2.run("ip link set up dev RED") + pe1.run("ip link set pe1-eth0 master RED") + pe2.run("ip link set pe2-eth1 master RED") + + pe1.run("sysctl -w net.ipv4.ip_forward=1") + pe2.run("sysctl -w net.ipv4.ip_forward=1") + pe1.run("sysctl -w net.mpls.conf.pe1-eth0.input=1") + pe2.run("sysctl -w net.mpls.conf.pe2-eth1.input=1") + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_vpn_5549(): + tgen = get_topogen() + + pe2 = tgen.gears["pe2"] + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_vpn_nexthop_changed(): + output = json.loads(pe2.vtysh_cmd("show bgp ipv4 vpn json")) + expected = { + "routes": { + "routeDistinguishers": { + "192.168.1.2:2": { + "172.16.255.1/32": [ + {"valid": True, "nexthops": [{"ip": "2001:db8::1"}]} + ], + "192.168.1.0/24": [ + {"valid": True, "nexthops": [{"ip": "2001:db8:1::1"}]} + ], + } + } + } + } + return topotest.json_cmp(output, expected) + + def _bgp_verify_v4_nexthop_validity(): + output = json.loads(tgen.gears["cpe1"].vtysh_cmd("show bgp nexthop json")) + expected = { + "ipv4": { + "192.168.1.2": { + "valid": True, + "complete": True, + "igpMetric": 0, + "pathCount": 0, + "nexthops": [{"interfaceName": "cpe1-eth0"}], + }, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_verify_v6_global_nexthop_validity(): + output = json.loads(tgen.gears["pe2"].vtysh_cmd("show bgp nexthop json")) + expected = { + "ipv6": { + "2001:db8::1": { + "valid": True, + "complete": True, + "igpMetric": 0, + "pathCount": 2, + "nexthops": [{"interfaceName": "pe2-eth0"}], + }, + "2001:db8:1::1": { + "valid": True, + "complete": True, + "igpMetric": 10, + "pathCount": 2, + "peer": "2001:db8:1::1", + "nexthops": [{"interfaceName": "pe2-eth0"}], + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_vpn_nexthop_changed) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed overriding IPv6 next-hop for VPN underlay" + + test_func = functools.partial(_bgp_verify_v4_nexthop_validity) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "IPv4 nexthop is invalid" + + test_func = functools.partial(_bgp_verify_v6_global_nexthop_validity) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "IPv6 nexthop is invalid" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vpnv4_asbr/__init__.py b/tests/topotests/bgp_vpnv4_asbr/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_vpnv4_asbr/h1/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/h1/zebra.conf new file mode 100644 index 0000000..2237224 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/h1/zebra.conf @@ -0,0 +1,7 @@ +log stdout +ip route 172.31.1.0/24 172.31.0.1 +ip route 172.31.2.0/24 172.31.0.1 +interface h1-eth0 + ip address 172.31.0.10/24 +! + diff --git a/tests/topotests/bgp_vpnv4_asbr/h2/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/h2/zebra.conf new file mode 100644 index 0000000..d650bc8 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/h2/zebra.conf @@ -0,0 +1,6 @@ +log stdout +ip route 172.31.0.0/24 172.31.1.1 +interface h2-eth0 + ip address 172.31.1.10/24 +! + diff --git a/tests/topotests/bgp_vpnv4_asbr/h3/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/h3/zebra.conf new file mode 100644 index 0000000..5676485 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/h3/zebra.conf @@ -0,0 +1,6 @@ +log stdout +ip route 172.31.0.0/24 172.31.2.1 +interface h3-eth0 + ip address 172.31.2.10/24 +! + diff --git a/tests/topotests/bgp_vpnv4_asbr/r1/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_asbr/r1/bgp_ipv4_routes.json new file mode 100644 index 0000000..184ab31 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r1/bgp_ipv4_routes.json @@ -0,0 +1,49 @@ +{ + "vrfName": "vrf1", + "localAS": 65500, + "routes": + { + "172.31.0.10/32": [ + { + "prefix": "172.31.0.10", + "prefixLen": 32, + "network": "172.31.0.10\/32", + "nhVrfName": "default", + "nexthops": [ + { + "ip": "192.168.0.3", + "afi": "ipv4", + "used": true + } + ] + }, + { + "prefix": "172.31.0.10", + "prefixLen": 32, + "network": "172.31.0.10\/32", + "nhVrfName": "default", + "nexthops": [ + { + "ip": "192.168.0.2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.1/32": [ + { + "prefix": "172.31.0.1", + "prefixLen": 32, + "network": "172.31.0.1\/32", + "nexthops": [ + { + "ip": "0.0.0.0", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf new file mode 100644 index 0000000..3bbcc20 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf @@ -0,0 +1,29 @@ +router bgp 65500 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + neighbor 192.0.2.100 remote-as 65500 + neighbor 192.0.2.100 update-source lo + neighbor 192.168.0.100 remote-as 65500 + address-family ipv4 unicast + no neighbor 192.168.0.100 activate + no neighbor 192.0.2.100 activate + network 192.0.2.1/32 + exit-address-family + address-family ipv4 labeled-unicast + neighbor 192.168.0.100 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.0.2.100 activate + exit-address-family +! +router bgp 65500 vrf vrf1 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + redistribute connected + label vpn export 101 + rd vpn export 444:1 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! diff --git a/tests/topotests/bgp_vpnv4_asbr/r1/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/r1/zebra.conf new file mode 100644 index 0000000..2f12b72 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r1/zebra.conf @@ -0,0 +1,10 @@ +log stdout +interface lo + ip address 192.0.2.1/32 +! +interface r1-eth1 vrf vrf1 + ip address 172.31.0.1/24 +! +interface r1-eth0 + ip address 192.168.0.1/24 +! diff --git a/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf new file mode 100644 index 0000000..4c84d52 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf @@ -0,0 +1,31 @@ +debug bgp nht +debug bgp zebra +debug bgp labelpool +router bgp 65500 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + neighbor 192.0.2.100 remote-as 65500 + neighbor 192.0.2.100 update-source lo + neighbor 192.168.0.100 remote-as 65500 + neighbor 192.168.1.200 remote-as 65502 + address-family ipv4 unicast + no neighbor 192.168.0.100 activate + no neighbor 192.168.1.200 activate + network 192.0.2.2/32 + exit-address-family + address-family ipv4 labeled-unicast + neighbor 192.168.0.100 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.0.2.100 activate + neighbor 192.0.2.100 next-hop-self + neighbor 192.168.1.200 activate + exit-address-family +! +interface r2-eth1 + mpls bgp forwarding + mpls bgp l3vpn-multi-domain-switching +! +interface r2-eth0 + mpls bgp l3vpn-multi-domain-switching +! diff --git a/tests/topotests/bgp_vpnv4_asbr/r2/ipv4_vpn_summary.json b/tests/topotests/bgp_vpnv4_asbr/r2/ipv4_vpn_summary.json new file mode 100644 index 0000000..d33c5f5 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r2/ipv4_vpn_summary.json @@ -0,0 +1,24 @@ +{ + "routerId":"192.0.2.2", + "as":65500, + "vrfId":0, + "vrfName":"default", + "peerCount":2, + "peers":{ + "192.0.2.100":{ + "remoteAs":65500, + "localAs":65500, + "version":4, + "state":"Established", + "peerState":"OK" + }, + "192.168.1.200":{ + "remoteAs":65502, + "localAs":65500, + "version":4, + "state":"Established", + "peerState":"OK" + } + }, + "totalPeers":2 +} diff --git a/tests/topotests/bgp_vpnv4_asbr/r2/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/r2/zebra.conf new file mode 100644 index 0000000..43508a4 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r2/zebra.conf @@ -0,0 +1,13 @@ +log stdout +ip route 192.168.1.3/32 r2-eth1 +interface lo + ip address 192.0.2.2/32 +! +interface r2-eth0 + ip address 192.168.0.2/24 + mpls enable +! +interface r2-eth1 + ip address 192.168.1.2/24 + mpls enable +! diff --git a/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf new file mode 100644 index 0000000..c5d5727 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf @@ -0,0 +1,25 @@ +router bgp 65501 + bgp router-id 192.0.2.3 + no bgp ebgp-requires-policy + neighbor 192.168.1.200 remote-as 65502 + address-family ipv4 unicast + no neighbor 192.168.1.200 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.168.1.200 activate + exit-address-family +! +router bgp 65501 vrf vrf1 + bgp router-id 192.0.2.3 + address-family ipv4 unicast + redistribute connected + label vpn export 102 + rd vpn export 444:3 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +interface r3-eth0 + mpls bgp forwarding +! diff --git a/tests/topotests/bgp_vpnv4_asbr/r3/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/r3/zebra.conf new file mode 100644 index 0000000..6376785 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r3/zebra.conf @@ -0,0 +1,14 @@ +log stdout +ip route 192.168.1.3/32 r3-eth0 +interface r3-eth1 vrf vrf1 + ip address 172.31.1.1/24 +! +interface r3-eth2 vrf vrf1 + ip address 172.31.2.1/24 +! +interface r3-eth3 vrf vrf1 + ip address 172.31.3.1/24 +! +interface r3-eth0 + ip address 192.168.1.3/24 +! diff --git a/tests/topotests/bgp_vpnv4_asbr/rr100/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/rr100/bgpd.conf new file mode 100644 index 0000000..845d71b --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/rr100/bgpd.conf @@ -0,0 +1,29 @@ +router bgp 65500 + bgp router-id 192.0.2.100 + no bgp ebgp-requires-policy + neighbor 192.0.2.2 remote-as 65500 + neighbor 192.0.2.2 update-source lo + neighbor 192.168.0.2 remote-as 65500 + neighbor 192.0.2.1 remote-as 65500 + neighbor 192.0.2.1 update-source lo + neighbor 192.168.0.1 remote-as 65500 + address-family ipv4 unicast + no neighbor 192.168.0.1 activate + no neighbor 192.0.2.1 activate + no neighbor 192.168.0.2 activate + no neighbor 192.0.2.2 activate + network 192.0.2.100/32 + exit-address-family + address-family ipv4 labeled-unicast + neighbor 192.168.0.1 activate + neighbor 192.168.0.2 activate + neighbor 192.168.0.1 route-reflector-client + neighbor 192.168.0.2 route-reflector-client + exit-address-family + address-family ipv4 vpn + neighbor 192.0.2.1 activate + neighbor 192.0.2.2 activate + neighbor 192.0.2.1 route-reflector-client + neighbor 192.0.2.2 route-reflector-client + exit-address-family +! diff --git a/tests/topotests/bgp_vpnv4_asbr/rr100/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/rr100/zebra.conf new file mode 100644 index 0000000..2fa5285 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/rr100/zebra.conf @@ -0,0 +1,7 @@ +log stdout +interface lo + ip address 192.0.2.100/32 +! +interface rr100-eth0 + ip address 192.168.0.100/24 +! diff --git a/tests/topotests/bgp_vpnv4_asbr/rs200/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/rs200/bgpd.conf new file mode 100644 index 0000000..fa3cb54 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/rs200/bgpd.conf @@ -0,0 +1,19 @@ +debug bgp nht +debug bgp zebra +debug bgp labelpool +router bgp 65502 + bgp router-id 192.0.2.200 + no bgp ebgp-requires-policy + neighbor 192.168.1.3 remote-as 65501 + neighbor 192.168.1.2 remote-as 65500 + address-family ipv4 unicast + no neighbor 192.168.1.2 activate + no neighbor 192.168.1.3 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.168.1.3 activate + neighbor 192.168.1.2 activate + neighbor 192.168.1.3 route-server-client + neighbor 192.168.1.2 route-server-client + exit-address-family +! \ No newline at end of file diff --git a/tests/topotests/bgp_vpnv4_asbr/rs200/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/rs200/zebra.conf new file mode 100644 index 0000000..98793ca --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/rs200/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface rs200-eth0 + ip address 192.168.1.200/24 +! diff --git a/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py b/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py new file mode 100644 index 0000000..a908e74 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py @@ -0,0 +1,917 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_vpnv4_asbr.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2023 by 6WIND +# + +""" + test_bgp_vpnv4_asbr.py: Test the FRR BGP daemon with rfc4364 option 10b + r1, r2, and r100 are in an iBGP AS, while r2, r3 do an eBGP peering + h1 is a host behind r1 VRF1, and {h2,h3} are hosts behind r3 VRF1 + The test demonstrates the connectivity across the network between h1 and h3. + + + +----------+ +----+--------+ +--------+ +--------+-----+ + | |172.31.0.0|vrf | r1 |192.168.0.0/24| r2 |192.168.1.0/24|r3 | vrf | + | h1 +----------+ | 1+------+-------+ +------+-------+3 | +--- 172.31.3.0/24 + | 10 | |VRF1|AS65500 | | | AS65500| | |AS65501 |VRF1 | + +----------+ +-------------+ | +--------+ | +--------+--+-++ + 192.0.2.1 | 192.0.2.2 | 172| | + +----------+ +----+--------+ 31| | + |rr100 | |rs200/AS65502| 1| | + +----------+ +-------------+ 0| | + 192.0.2.100 +--------+ /24| | + | | +----------+----+ | + |h3 | | | | + |10 | | h2 | | + +---+----+ | 10 | | + | +----------+ | + |172.31.2.0/24 | + +--------------------------------+ +""" + +import os +import sys +import json +from functools import partial +import pytest +import functools + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.checkping import check_ping + + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Allocate 8 devices + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r3") + tgen.add_router("h1") + tgen.add_router("h2") + tgen.add_router("h3") + tgen.add_router("rr100") + tgen.add_router("rs200") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["rr100"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["h1"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["rs200"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["h2"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["h3"]) + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r3"]) + + +def _populate_iface(): + tgen = get_topogen() + cmds_list = [ + "ip link add vrf1 type vrf table 10", + "echo 100000 > /proc/sys/net/mpls/platform_labels", + "ip link set dev vrf1 up", + "ip link set dev {0}-eth1 master vrf1", + "echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input", + ] + + for rname in ("r1", "r3"): + for cmd in cmds_list: + input = cmd.format(rname) + logger.info("input: " + cmd) + output = tgen.net[rname].cmd(cmd.format(rname)) + logger.info("output: " + output) + + cmds_list = [ + "ip link set dev {0}-eth2 master vrf1", + "ip link set dev {0}-eth3 master vrf1", + ] + for cmd in cmds_list: + input = cmd.format("r3") + logger.info("input: " + input) + output = tgen.net["r3"].cmd(input) + logger.info("output: " + output) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + _populate_iface() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + if rname in ("r1", "r2", "r3", "rr100", "rs200"): + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def bgp_vpnv4_prefix_check(router, rd, prefix, label, nexthop): + """ + Dump and check 'show bgp ipv4 vpn json' output. An assert is triggered in case test fails + * 'router': the router to check + * 'rd': The route distinguisher expected + * 'prefix': The prefix expected + * 'label': The label expected associated with the ('rd','prefix') tuple + * 'nexthop': The nexthop expected associated with the ('rd','prefix') tuple + """ + + def _check(router, prefix, rd, label, nexthop): + dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True) + if not dump: + return "{0}, {1}, route distinguisher {2} not present".format( + router.name, prefix, rd + ) + for dumped_rd, pathes in dump.items(): + if dumped_rd != rd: + continue + for path in pathes["paths"]: + if "remoteLabel" not in path.keys(): + return "{0}, {1}, rd {2}, remoteLabel not present".format( + router.name, prefix, rd + ) + if str(path["remoteLabel"]) != label: + continue + + if "nexthops" not in path.keys(): + return "{0}, {1}, rd {2}, no nexthops present".format( + router.name, prefix, rd + ) + + for nh in path["nexthops"]: + if "ip" not in nh.keys(): + return "{0}, {1}, rd {2}, no ipv4 nexthop available".format( + router.name, prefix, rd + ) + if nh["ip"] != nexthop: + continue + return None + return "{0}, {1}, rd {2}, remoteLabel {3}, nexthop {4} not found".format( + router.name, prefix, rd, label, nexthop + ) + + func = functools.partial(_check, router, prefix, rd, label, nexthop) + success, result = topotest.run_and_expect(func, None, count=20, wait=0.5) + assert_msg = "{}, show bgp ipv4 vpn {}, rd {}, label {} nexthop {}".format( + router.name, prefix, rd, label, nexthop + ) + assert result is None, assert_msg + " not found" + logger.info(assert_msg + " found") + + +def mpls_table_get_entry(router, out_label, out_nexthop): + """ + Get the in_label from tuple (out_label, out_nexthop) + * 'router': the router to check + * 'out_label': The outgoing label expected + * 'out_nexthop': The outgoing nexthop expected + """ + dump = router.vtysh_cmd("show mpls table json", isjson=True) + for in_label, label_info in dump.items(): + for nh in label_info["nexthops"]: + if nh["type"] != "BGP" or "installed" not in nh.keys(): + continue + if "nexthop" in nh.keys(): + if nh["nexthop"] != out_nexthop: + continue + if "outLabelStack" in nh.keys(): + if out_label not in nh["outLabelStack"]: + continue + return in_label + return None + + +def mpls_table_check_entry(router, out_label, out_nexthop): + """ + Dump and check 'show mpls table json' output. An assert is triggered in case test fails + * 'router': the router to check + * 'out_label': The outgoing label expected + * 'out_nexthop': The outgoing nexthop expected + """ + logger.info("Checking MPLS labels on {}".format(router.name)) + dump = router.vtysh_cmd("show mpls table json", isjson=True) + for in_label, label_info in dump.items(): + for nh in label_info["nexthops"]: + if nh["type"] != "BGP" or "installed" not in nh.keys(): + continue + if "nexthop" in nh.keys(): + if nh["nexthop"] != out_nexthop: + continue + if "outLabelStack" in nh.keys(): + if out_label not in nh["outLabelStack"]: + continue + logger.info( + "{}, show mpls table, entry in_label {} out_label {} out_nexthop {} found".format( + router.name, in_label, nh["outLabelStack"], nh["nexthop"] + ) + ) + return None + return "{}, show mpls table, entry matching in_label {} out_label {} out_nexthop {} not found".format( + router.name, in_label, out_label, out_nexthop + ) + + +def check_show_bgp_vpn_prefix_found( + router, ipversion, prefix, rd, label=None, nexthop=None +): + """ + Check if a given vpn prefix is present in the BGP RIB + * 'router': the router to check BGP VPN RIB + * 'ipversion': The ip version to check: ipv4 or ipv6 + * 'prefix': the IP prefix to check + * 'rd': the route distinguisher to check + * 'label: the label to check + """ + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + if label: + if nexthop: + expected = { + rd: { + "prefix": prefix, + "paths": [{"remoteLabel": label, "nexthops": [{"ip": nexthop}]}], + } + } + else: + expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} + else: + if nexthop: + expected = { + rd: {"prefix": prefix, "paths": [{"nexthops": [{"ip": nexthop}]}]} + } + else: + expected = {rd: {"prefix": prefix}} + return topotest.json_cmp(output, expected) + + +def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None): + """ + Check if a given vpn prefix is not present in the BGP RIB + * 'router': the router to check BGP VPN RIB + * 'ipversion': The ip version to check: ipv4 or ipv6 + * 'prefix': the IP prefix to check + * 'rd': the route distinguisher to check + * 'label: the label to check + """ + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + if label: + expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} + else: + expected = {rd: {"prefix": prefix}} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + +def check_show_mpls_table_entry_label_not_found(router, inlabel): + output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) + expected = {"inLabel": inlabel, "installed": True} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + +def check_show_bgp_vpn_ok(router, vpnv4_entries): + """ + Check on router that BGP l3vpn entries are present + Check there is an MPLS entry bound to that BGP L3VPN entry + Extract the Label value and check on the distributed router the BGP L3VPN entry + If check fail, an assert is triggered. + * 'router': the router to check BGP VPN RIB + * 'vpnv4_entries': dictionary that contains the list of prefixes, and the distributed router to look after + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + vpnv4_nexthops = {"r1": "192.0.2.2", "r3": "192.168.1.2"} + vpnv4_nht = {"192.0.2.1": "192.168.0.1", "192.168.1.3": "192.168.1.3"} + label_ip_entries = {} + + def _return_remote_label_nh_rd(router, prefix): + dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True) + assert_msg = ( + "{}, prefix {} not available or label not found", + router.name, + prefix, + ) + assert dump, assert_msg + for rd, pathes in dump.items(): + for path in pathes["paths"]: + if "remoteLabel" not in path.keys(): + assert 0, assert_msg + for nh in path["nexthops"]: + if "ip" in nh.keys(): + return path["remoteLabel"], nh["ip"], rd + assert 0, assert_msg + + def _check_nexthop_available(router, prefix): + dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True) + if not dump: + return "{0}, {1}, route distinguisher not present".format( + router.name, prefix + ) + for rd, pathes in dump.items(): + for path in pathes["paths"]: + if "remoteLabel" not in path.keys(): + return "{0}, {1}, remoteLabel not present".format( + router.name, prefix + ) + if "nexthops" not in path.keys(): + return "{0}, {1}, no nexthop available".format(router.name, prefix) + return None + + for prefix, rname_to_test in vpnv4_entries.items(): + func = functools.partial(_check_nexthop_available, router, prefix) + success, result = topotest.run_and_expect(func, None, count=20, wait=0.5) + assert result is None, "Failed to detect prefix {} on router {}".format( + prefix, router.name + ) + + for prefix, rname_to_test in vpnv4_entries.items(): + l3vpn_label, l3vpn_nh, l3vpn_rd = _return_remote_label_nh_rd(router, prefix) + logger.info( + "{0}, {1}, label value is {2}, nh is {3}".format( + router.name, prefix, l3vpn_label, l3vpn_nh + ) + ) + test_func = functools.partial( + mpls_table_check_entry, router, l3vpn_label, vpnv4_nht[l3vpn_nh] + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, result + + in_label = mpls_table_get_entry(router, l3vpn_label, vpnv4_nht[l3vpn_nh]) + label_ip_entries[prefix] = in_label + + bgp_vpnv4_prefix_check( + tgen.gears[rname_to_test], + l3vpn_rd, + prefix, + in_label, + vpnv4_nexthops[rname_to_test], + ) + + return label_ip_entries + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + Check that Labels are as expected in r1, r2,and r3 + Check ping connectivity between h1 and h2 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # check that r2 peerings are ok + logger.info("Checking BGP ipv4 vpn summary for r2") + router = tgen.gears["r2"] + json_file = "{}/{}/ipv4_vpn_summary.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp ipv4 vpn summary json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_mpls_setup_ok(): + """ + tests for the r1 to r3 direction: checks for prefix=('172.31.1.0/24','172.31.2.0/24','172.31.3.0/24') + r2. get label from 'prefix' + check that r2. show mpls table has an entry with outbound label set to the label from 172.31.1.0/24 + r2. get label from mpls entry + check that r1: show bgp ipv4 vpn 172.31.1.0/24 has label from r2.mpls entry + tests for the r3 to r1 direction + r2. get label from 172.31.0.0/24 + check that r2. show mpls table has an entry with outbound label set that includes the label from 172.31.0.0/24 + r2. get label from mpls entry + check that r3: show bgp ipv4 vpn 172.31.0.0/24 has label from r2.mpls entry + check that h1. ping 172.31.1.10 (h2) is ok. + check that h1. ping 172.31.2.10 (h3) is ok. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r2"] + + # diagnostic + logger.info("Dumping mplsvpn nexthop table") + router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + + vpnv4_checks = { + "172.31.1.0/24": "r1", + "172.31.2.0/24": "r1", + "172.31.3.0/24": "r1", + "172.31.0.0/24": "r3", + } + logger.info( + "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on all devices".format( + router.name + ) + ) + check_show_bgp_vpn_ok(router, vpnv4_checks) + + logger.info("h1, check that ping from h1 to (h2,h3) is ok") + check_ping("h1", "172.31.1.10", True, 20, 0.5) + check_ping("h1", "172.31.2.10", True, 20, 0.5) + + +def test_r3_prefixes_removed(): + """ + Remove BGP redistributed updates from r3. + Check that the BGP VPN updates from the updates are not present on r2. + Check that the 'show bgp ipv4 vpn' and 'show mpls table' are ok for 172.31.3.0/24 + Remove the 172.31.3.0/24 update from BGP on r3. + Check that the BGP VPN updates from r3 are not present on r2. + Check that the 'show mpls table' entry previously seen disappeared + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r3"] + logger.info("{}, keeping only 172.31.3.0/24 network".format(router.name)) + router.vtysh_cmd("configure terminal\ninterface r3-eth1 vrf vrf1\nshutdown\n") + router.vtysh_cmd("configure terminal\ninterface r3-eth2 vrf vrf1\nshutdown\n") + + router = tgen.gears["r2"] + logger.info( + "{}, check that 'show bgp ipv4 vpn' has only 172.31.3.0/24 network from r3".format( + router.name + ) + ) + + for prefix in ("172.31.1.0/24", "172.31.2.0/24"): + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + router, + "ipv4", + prefix, + "444:3", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still present".format(router.name, prefix) + + # diagnostic + logger.info("Dumping mplsvpn nexthop table") + router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + + prefix = "172.31.3.0/24" + logger.info( + "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format( + router.name + ) + ) + vpnv4_checks = { + prefix: "r1", + } + label_ip_entries = check_show_bgp_vpn_ok(router, vpnv4_checks) + + router = tgen.gears["r3"] + logger.info("{}, removing {} network".format(router.name, prefix)) + router.vtysh_cmd("configure terminal\ninterface r3-eth3 vrf vrf1\nshutdown\n") + + router = tgen.gears["r2"] + logger.info( + "{}, check that 'show bgp ipv4 vpn' has not {} network from r3".format( + router.name, prefix + ) + ) + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + router, + "ipv4", + prefix, + "444:3", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still present".format(router.name, prefix) + + logger.info( + "{}, check that 'show mpls table {}' is not present".format( + router.name, label_ip_entries[prefix] + ) + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_not_found, router, label_ip_entries[prefix] + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry with in_label {} still present".format( + label_ip_entries[prefix] + ) + + +def test_r3_prefixes_added_back(): + """ + Add back the 172.31.3.0/24 network from r3 + Check on r2 that MPLS switching entry appears when the 1st BGP update is received + Check the IP connectivity (h1,h2) and (h1,h3) + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r3"] + prefix = "172.31.3.0/24" + logger.info("{}, restoring the {} network from r3".format(router.name, prefix)) + router.vtysh_cmd("configure terminal\ninterface r3-eth3 vrf vrf1\nno shutdown\n") + + router = tgen.gears["r2"] + logger.info( + "{}, check that 'show bgp ipv4 vpn' has {} network from r3".format( + router.name, prefix + ) + ) + + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + router, + "ipv4", + prefix, + "444:3", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} not present".format(router.name, prefix) + + logger.info( + "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format( + router.name + ) + ) + vpnv4_checks = { + prefix: "r1", + } + check_show_bgp_vpn_ok(router, vpnv4_checks) + + router = tgen.gears["r3"] + logger.info( + "{}, restoring the redistribute connected prefixes from r3".format(router.name) + ) + router.vtysh_cmd("configure terminal\ninterface r3-eth1 vrf vrf1\nno shutdown\n") + router.vtysh_cmd("configure terminal\ninterface r3-eth2 vrf vrf1\nno shutdown\n") + router = tgen.gears["r2"] + for prefix in ("172.31.1.0/24", "172.31.2.0/24"): + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + router, + "ipv4", + prefix, + "444:3", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} not present".format(router.name, prefix) + + # diagnostic + logger.info("Dumping mplsvpn nexthop table") + tgen.gears["r2"].vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + + +def test_unconfigure_nexthop_change_nexthop_self(): + """ + Get the list of labels advertised from r2 to r1 + On r2, disable next-hop-self for 192.0.2.100 neighbor + Check that the list of labels are not present in 'show mpls table' + Check that r1 received the prefixes with the original (next-hop,label) + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + vpnv4_checks = { + "172.31.1.0/24": "r1", + "172.31.2.0/24": "r1", + "172.31.3.0/24": "r1", + } + logger.info( + "{}, Get the list of labels allocated for prefixes from r3".format(router.name) + ) + label_ip_entries = check_show_bgp_vpn_ok(router, vpnv4_checks) + + logger.info( + "{}, disable next-hop-self for 192.0.2.100 neighbor".format(router.name) + ) + router = tgen.gears["r2"] + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nno neighbor 192.0.2.100 next-hop-self\n" + ) + + for prefix, label in label_ip_entries.items(): + logger.info( + "{}, check mpls entry for {} with in_label {} is not present'".format( + router.name, prefix, label + ) + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_not_found, router, label + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry for {} with in_label {} still present".format( + prefix, label + ) + + router = tgen.gears["r1"] + for prefix, label in label_ip_entries.items(): + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + router, + "ipv4", + prefix, + "444:3", + label=label, + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, mpls vpn update {} label {} is present".format( + router.name, prefix, label + ) + for prefix, label in label_ip_entries.items(): + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + router, + "ipv4", + prefix, + "444:3", + nexthop="192.168.1.3", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, mpls vpn update {} label {} is present".format( + router.name, prefix, label + ) + + # diagnostic + logger.info("Dumping mplsvpn nexthop table") + tgen.gears["r2"].vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + + +def test_reconfigure_nexthop_change_nexthop_self(): + """ + Get the list of labels advertised from r2 to r1 + On r2, enable next-hop-self for 192.0.2.100 neighbor + Check that the list of labels are present in 'show mpls table' + Check that r1 received the prefixes with the original (next-hop,label) + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + logger.info("{}, enable next-hop-self for 192.0.2.100 neighbor".format(router.name)) + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nneighbor 192.0.2.100 next-hop-self\n" + ) + vpnv4_checks = { + "172.31.1.0/24": "r1", + "172.31.2.0/24": "r1", + "172.31.3.0/24": "r1", + } + logger.info( + "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format( + router.name + ) + ) + check_show_bgp_vpn_ok(router, vpnv4_checks) + + logger.info("h1, check that ping from h1 to (h2,h3) is ok") + check_ping("h1", "172.31.1.10", True, 20, 0.5) + check_ping("h1", "172.31.2.10", True, 20, 0.5) + # diagnostic + logger.info("Dumping mplsvpn nexthop table") + router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + + +def test_declare_vpn_network_with_different_label(): + """ + declare a vpnv4 network on r3. + check that a new VPNv4 entry is received on r2. + Check that the list of labels are present in 'show mpls table' + Check that r1 received the prefixes with the new (next-hop,label) + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r3"] + logger.info( + "{}, declare static 33.33.33.33/32 network rd 33:33 label 33".format( + router.name + ) + ) + router.vtysh_cmd( + "configure terminal\nrouter bgp 65501\nno bgp network import-check\n" + ) + router.vtysh_cmd( + "configure terminal\nrouter bgp 65501\naddress-family ipv4 vpn\nnetwork 33.33.33.33/32 rd 444:3 label 33\n" + ) + + router = tgen.gears["r2"] + vpnv4_entries = { + "172.31.1.0/24": None, + "172.31.2.0/24": None, + "172.31.3.0/24": None, + "33.33.33.33/32": 33, + } + + for prefix, label in vpnv4_entries.items(): + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + router, + "ipv4", + prefix, + "444:3", + label=label, + nexthop="192.168.1.3", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {}, label {} not present".format( + router.name, prefix, label + ) + + vpnv4_checks = { + "172.31.1.0/24": "r1", + "172.31.2.0/24": "r1", + "172.31.3.0/24": "r1", + "33.33.33.33/32": "r1", + } + logger.info( + "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format( + router.name + ) + ) + check_show_bgp_vpn_ok(router, vpnv4_checks) + + +def test_filter_vpn_network_from_r1(): + """ + Get the list of labels in 'show mpls table' + filter network from r1 + check that the vpnv4 entry on r2 is not present + Check that the associated mpls entry is not present + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + vpnv4_checks = { + "172.31.0.0/24": "r3", + } + logger.info( + "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r3".format( + router.name + ) + ) + label_ip_entries = check_show_bgp_vpn_ok(router, vpnv4_checks) + + for prefix, label in label_ip_entries.items(): + logger.info("{}, filter prefix {} from r1".format(router.name, prefix)) + router.vtysh_cmd( + "configure terminal\nroute-map rmap deny 1\nmatch ip next-hop address 192.0.2.1\n" + ) + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nneighbor 192.0.2.100 route-map rmap in\n" + ) + logger.info( + "{}, check that prefix {} is not present".format(router.name, prefix) + ) + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + router, + "ipv4", + "172.31.0.0/24", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {}, is still present".format( + router.name, prefix + ) + + # diagnostic + logger.info("Dumping mplsvpn nexthop table") + router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + + logger.info( + "{}, check that show mpls table {} is not present".format( + router.name, label + ) + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_not_found, router, int(label) + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry for {} with in_label {} still present".format( + prefix, label + ) + + +def test_unfilter_vpn_network_from_r1(): + """ + unfilter network from r1 + check that the vpnv4 entry on r2 is present + Check that the list of labels are present in 'show mpls table' + Check that r3 received the prefixes with the new (next-hop,label) + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + prefix = "172.31.0.0/24" + + logger.info("{}, filter prefix {} from r1".format(router.name, prefix)) + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nno neighbor 192.0.2.100 route-map rmap in\n" + ) + + logger.info("{}, check that prefix {} is present".format(router.name, prefix)) + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, router, "ipv4", prefix, "444:1" + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {}, is not present".format(router.name, prefix) + + vpnv4_checks = { + "172.31.0.0/24": "r3", + } + logger.info( + "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on all devices".format( + router.name + ) + ) + check_show_bgp_vpn_ok(router, vpnv4_checks) + + # diagnostic + logger.info("Dumping mplsvpn nexthop table") + router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vpnv4_ebgp/__init__.py b/tests/topotests/bgp_vpnv4_ebgp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_vpnv4_ebgp/r1/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_ebgp/r1/bgp_ipv4_routes.json new file mode 100644 index 0000000..184ab31 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r1/bgp_ipv4_routes.json @@ -0,0 +1,49 @@ +{ + "vrfName": "vrf1", + "localAS": 65500, + "routes": + { + "172.31.0.10/32": [ + { + "prefix": "172.31.0.10", + "prefixLen": 32, + "network": "172.31.0.10\/32", + "nhVrfName": "default", + "nexthops": [ + { + "ip": "192.168.0.3", + "afi": "ipv4", + "used": true + } + ] + }, + { + "prefix": "172.31.0.10", + "prefixLen": 32, + "network": "172.31.0.10\/32", + "nhVrfName": "default", + "nexthops": [ + { + "ip": "192.168.0.2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.1/32": [ + { + "prefix": "172.31.0.1", + "prefixLen": 32, + "network": "172.31.0.1\/32", + "nexthops": [ + { + "ip": "0.0.0.0", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf new file mode 100644 index 0000000..0249279 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf @@ -0,0 +1,28 @@ +router bgp 65500 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + neighbor 192.168.0.2 remote-as 65501 + neighbor 192.168.0.3 remote-as 65501 + address-family ipv4 unicast + no neighbor 192.168.0.3 activate + no neighbor 192.168.0.2 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.168.0.2 activate + neighbor 192.168.0.3 activate + exit-address-family +! +router bgp 65500 vrf vrf1 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + redistribute connected + label vpn export 101 + rd vpn export 444:1 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +interface r1-eth0 + mpls bgp forwarding +! \ No newline at end of file diff --git a/tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json b/tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json new file mode 100644 index 0000000..79b020a --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json @@ -0,0 +1,62 @@ +{ + "172.31.0.10/32": [ + { + "prefix": "172.31.0.10/32", + "prefixLen": 32, + "protocol": "bgp", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "192.168.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "vrf": "default", + "active": true, + "labels":[ + 102 + ] + }, + { + "flags": 3, + "fib": true, + "ip": "192.168.0.3", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "vrf": "default", + "active": true, + "labels":[ + 102 + ] + } + ] + } + ], + "172.31.0.1/32": [ + { + "prefix": "172.31.0.1/32", + "prefixLen": 32, + "protocol": "connected", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "nexthops":[ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf b/tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf new file mode 100644 index 0000000..f626e44 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf @@ -0,0 +1,7 @@ +log stdout +interface r1-eth1 vrf vrf1 + ip address 172.31.0.1/32 +! +interface r1-eth0 + ip address 192.168.0.1/24 +! diff --git a/tests/topotests/bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json new file mode 100644 index 0000000..1fc3a4b --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json @@ -0,0 +1,38 @@ +{ + "vrfName": "vrf1", + "localAS": 65501, + "routes": + { + "172.31.0.1/32": [ + { + "prefix": "172.31.0.1", + "prefixLen": 32, + "network": "172.31.0.1\/32", + "nhVrfName": "default", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.10/32": [ + { + "valid": true, + "bestpath": true, + "prefix": "172.31.0.10", + "prefixLen": 32, + "network": "172.31.0.10\/32", + "nexthops": [ + { + "ip": "0.0.0.0", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf new file mode 100644 index 0000000..e873469 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf @@ -0,0 +1,25 @@ +router bgp 65501 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + neighbor 192.168.0.1 remote-as 65500 + address-family ipv4 unicast + no neighbor 192.168.0.1 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.168.0.1 activate + exit-address-family +! +router bgp 65501 vrf vrf1 + bgp router-id 192.0.2.2 + address-family ipv4 unicast + redistribute connected + label vpn export 102 + rd vpn export 444:2 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +interface r2-eth0 + mpls bgp forwarding +! diff --git a/tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf b/tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf new file mode 100644 index 0000000..bbc5240 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf @@ -0,0 +1,7 @@ +log stdout +interface r2-eth1 vrf vrf1 + ip address 172.31.0.10/32 +! +interface r2-eth0 + ip address 192.168.0.2/24 +! diff --git a/tests/topotests/bgp_vpnv4_ebgp/r3/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_ebgp/r3/bgp_ipv4_routes.json new file mode 100644 index 0000000..19797dd --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r3/bgp_ipv4_routes.json @@ -0,0 +1,38 @@ +{ + "vrfName": "vrf1", + "localAS": 65501, + "routes": + { + "10.201.0.0/24": [ + { + "prefix": "10.201.0.0", + "prefixLen": 24, + "network": "10.201.0.0\/24", + "nhVrfName": "default", + "nexthops": [ + { + "ip": "10.125.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.200.0.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "10.200.0.0", + "prefixLen": 24, + "network": "10.200.0.0\/24", + "nexthops": [ + { + "ip": "0.0.0.0", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vpnv4_ebgp/r3/bgpd.conf b/tests/topotests/bgp_vpnv4_ebgp/r3/bgpd.conf new file mode 100644 index 0000000..a327638 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r3/bgpd.conf @@ -0,0 +1,25 @@ +router bgp 65501 + bgp router-id 192.0.2.3 + no bgp ebgp-requires-policy + neighbor 192.168.0.1 remote-as 65500 + address-family ipv4 unicast + no neighbor 192.168.0.1 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.168.0.1 activate + exit-address-family +! +router bgp 65502 vrf vrf1 + bgp router-id 192.0.2.3 + address-family ipv4 unicast + redistribute connected + label vpn export 102 + rd vpn export 444:3 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +interface r3-eth0 + mpls bgp forwarding +! diff --git a/tests/topotests/bgp_vpnv4_ebgp/r3/zebra.conf b/tests/topotests/bgp_vpnv4_ebgp/r3/zebra.conf new file mode 100644 index 0000000..4412c04 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r3/zebra.conf @@ -0,0 +1,7 @@ +log stdout +interface r3-eth1 vrf vrf1 + ip address 172.31.0.10/32 +! +interface r3-eth0 + ip address 192.168.0.3/24 +! diff --git a/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py b/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py new file mode 100644 index 0000000..61e1163 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_vpnv4_ebgp.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2022 by 6WIND +# + +""" + test_bgp_vpnv4_ebgp.py: Test the FRR BGP daemon with EBGP direct connection +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 2 routers. + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r3") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r3"]) + + +def _populate_iface(): + tgen = get_topogen() + cmds_list = [ + "ip link add vrf1 type vrf table 10", + "echo 100000 > /proc/sys/net/mpls/platform_labels", + "ip link set dev vrf1 up", + "ip link set dev {0}-eth1 master vrf1", + "echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input", + ] + + for cmd in cmds_list: + input = cmd.format("r1") + logger.info("input: " + cmd) + output = tgen.net["r1"].cmd(cmd.format("r1")) + logger.info("output: " + output) + + for cmd in cmds_list: + input = cmd.format("r2") + logger.info("input: " + cmd) + output = tgen.net["r2"].cmd(cmd.format("r2")) + logger.info("output: " + output) + + for cmd in cmds_list: + input = cmd.format("r3") + logger.info("input: " + cmd) + output = tgen.net["r3"].cmd(cmd.format("r3")) + logger.info("output: " + output) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + _populate_iface() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + logger.info("Dump some context for r1") + router.vtysh_cmd("show bgp ipv4 vpn") + router.vtysh_cmd("show bgp summary") + router.vtysh_cmd("show bgp vrf vrf1 ipv4") + router.vtysh_cmd("show running-config") + router = tgen.gears["r2"] + logger.info("Dump some context for r2") + router.vtysh_cmd("show bgp ipv4 vpn") + router.vtysh_cmd("show bgp summary") + router.vtysh_cmd("show bgp vrf vrf1 ipv4") + router.vtysh_cmd("show running-config") + + # Check IPv4 routing tables on r1 + logger.info("Checking IPv4 routes for convergence on r1") + router = tgen.gears["r1"] + json_file = "{}/{}/ipv4_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + assert 0, "ipv4_routes.json file not found" + return + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip route vrf vrf1 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check BGP IPv4 routing tables on r1 + logger.info("Checking BGP IPv4 routes for convergence on r1") + router = tgen.gears["r1"] + json_file = "{}/{}/bgp_ipv4_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + assert 0, "bgp_ipv4_routes.json file not found" + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp vrf vrf1 ipv4 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check BGP IPv4 imported entry is not detected as local + # "selectionReason": "Locally configured route" + donna = tgen.gears["r1"].vtysh_cmd( + "show bgp vrf vrf1 ipv4 172.31.0.10/32 json", isjson=True + ) + routes = donna["paths"] + selectionReasonFound = False + for route in routes: + if "bestpath" not in route.keys(): + continue + if "selectionReason" not in route["bestpath"].keys(): + continue + + if "Locally configured route" == route["bestpath"]["selectionReason"]: + assert 0, "imported prefix has wrong reason detected" + + selectionReasonFound = True + + if not selectionReasonFound: + assertmsg = '"{}" imported prefix has wrong reason detected'.format(router.name) + assert False, assertmsg + + # Check BGP IPv4 routing tables on r2 not installed + logger.info("Checking BGP IPv4 routes for convergence on r2") + router = tgen.gears["r2"] + json_file = "{}/{}/bgp_ipv4_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + assert 0, "bgp_ipv4_routes.json file not found" + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp vrf vrf1 ipv4 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vpnv4_gre/__init__.py b/tests/topotests/bgp_vpnv4_gre/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf new file mode 100644 index 0000000..295811b --- /dev/null +++ b/tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf @@ -0,0 +1,27 @@ +router bgp 65500 + bgp router-id 192.0.2.1 + neighbor 192.0.2.2 remote-as 65500 + neighbor 192.0.2.2 update-source 192.0.2.1 + address-family ipv4 unicast + no neighbor 192.0.2.2 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.0.2.2 activate + neighbor 192.0.2.2 route-map rmap in + exit-address-family +! +router bgp 65500 vrf vrf1 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + redistribute connected + distance bgp 21 201 41 + label vpn export 101 + rd vpn export 444:1 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +route-map rmap permit 1 + set l3vpn next-hop encapsulation gre +! diff --git a/tests/topotests/bgp_vpnv4_gre/r1/ipv4_routes.json b/tests/topotests/bgp_vpnv4_gre/r1/ipv4_routes.json new file mode 100644 index 0000000..e57e21b --- /dev/null +++ b/tests/topotests/bgp_vpnv4_gre/r1/ipv4_routes.json @@ -0,0 +1,50 @@ +{ + "10.200.0.0/24": [ + { + "prefix": "10.200.0.0/24", + "prefixLen": 24, + "protocol": "bgp", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 201, + "metric": 0, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "192.168.0.2", + "afi": "ipv4", + "interfaceName": "r1-gre0", + "vrf": "default", + "active": true, + "labels":[ + 102 + ] + } + ] + } + ], + "10.201.0.0/24": [ + { + "prefix": "10.201.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "nexthops":[ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vpnv4_gre/r1/zebra.conf b/tests/topotests/bgp_vpnv4_gre/r1/zebra.conf new file mode 100644 index 0000000..11780a8 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_gre/r1/zebra.conf @@ -0,0 +1,14 @@ +log stdout +ip route 192.0.2.2/32 192.168.0.2 +interface lo + ip address 192.0.2.1/32 +! +interface r1-gre0 + ip address 192.168.0.1/24 +! +interface r1-eth1 vrf vrf1 + ip address 10.201.0.1/24 +! +interface r1-eth0 + ip address 10.125.0.1/24 +! diff --git a/tests/topotests/bgp_vpnv4_gre/r2/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_gre/r2/bgp_ipv4_routes.json new file mode 100644 index 0000000..e50d5dd --- /dev/null +++ b/tests/topotests/bgp_vpnv4_gre/r2/bgp_ipv4_routes.json @@ -0,0 +1,38 @@ +{ + "vrfName": "vrf1", + "localAS": 65500, + "routes": + { + "10.201.0.0/24": [ + { + "prefix": "10.201.0.0", + "prefixLen": 24, + "network": "10.201.0.0\/24", + "nhVrfName": "default", + "nexthops": [ + { + "ip": "192.0.2.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.200.0.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "10.200.0.0", + "prefixLen": 24, + "network": "10.200.0.0\/24", + "nexthops": [ + { + "ip": "0.0.0.0", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vpnv4_gre/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_gre/r2/bgpd.conf new file mode 100644 index 0000000..bf05866 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_gre/r2/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 65500 + bgp router-id 192.0.2.2 + neighbor 192.0.2.1 remote-as 65500 + neighbor 192.0.2.1 update-source 192.0.2.2 + address-family ipv4 unicast + no neighbor 192.0.2.1 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.0.2.1 activate + exit-address-family +! +router bgp 65500 vrf vrf1 + bgp router-id 192.0.2.2 + address-family ipv4 unicast + redistribute connected + label vpn export 102 + rd vpn export 444:2 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! diff --git a/tests/topotests/bgp_vpnv4_gre/r2/zebra.conf b/tests/topotests/bgp_vpnv4_gre/r2/zebra.conf new file mode 100644 index 0000000..de88a4b --- /dev/null +++ b/tests/topotests/bgp_vpnv4_gre/r2/zebra.conf @@ -0,0 +1,14 @@ +log stdout +ip route 192.0.2.1/32 192.168.0.1 +interface lo + ip address 192.0.2.2/32 +! +interface r2-gre0 + ip address 192.168.0.2/24 +! +interface r2-eth1 vrf vrf1 + ip address 10.200.0.2/24 +! +interface r2-eth0 + ip address 10.125.0.2/24 +! diff --git a/tests/topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py b/tests/topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py new file mode 100644 index 0000000..6f313be --- /dev/null +++ b/tests/topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_vpnv4_gre.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 by 6WIND +# + +""" + test_bgp_vpnv4_gre.py: Test the FRR BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 2 routers. + tgen.add_router("r1") + tgen.add_router("r2") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + +def _populate_iface(): + tgen = get_topogen() + cmds_list = [ + 'ip link add vrf1 type vrf table 10', + 'echo 10 > /proc/sys/net/mpls/platform_labels', + 'ip link set dev vrf1 up', + 'ip link set dev {0}-eth1 master vrf1', + 'echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input', + 'ip tunnel add {0}-gre0 mode gre ttl 64 dev {0}-eth0 local 10.125.0.{1} remote 10.125.0.{2}', + 'ip link set dev {0}-gre0 up', + 'echo 1 > /proc/sys/net/mpls/conf/{0}-gre0/input', + ] + + for cmd in cmds_list: + input = cmd.format('r1', '1', '2') + logger.info('input: ' + cmd) + output = tgen.net['r1'].cmd(cmd.format('r1', '1', '2')) + logger.info('output: ' + output) + + for cmd in cmds_list: + input = cmd.format('r2', '2', '1') + logger.info('input: ' + cmd) + output = tgen.net['r2'].cmd(cmd.format('r2', '2', '1')) + logger.info('output: ' + output) + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + _populate_iface() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears['r1'] + logger.info("Dump some context for r1") + router.vtysh_cmd("show bgp ipv4 vpn") + router.vtysh_cmd("show bgp summary") + router.vtysh_cmd("show bgp vrf vrf1 ipv4") + router.vtysh_cmd("show running-config") + router = tgen.gears['r2'] + logger.info("Dump some context for r2") + router.vtysh_cmd("show bgp ipv4 vpn") + router.vtysh_cmd("show bgp summary") + router.vtysh_cmd("show bgp vrf vrf1 ipv4") + router.vtysh_cmd("show running-config") + + # Check IPv4 routing tables on r1 + logger.info("Checking IPv4 routes for convergence on r1") + router = tgen.gears['r1'] + json_file = "{}/{}/ipv4_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + assert 0, 'ipv4_routes.json file not found' + return + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip route vrf vrf1 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check BGP IPv4 routing tables on r2 not installed + logger.info("Checking BGP IPv4 routes for convergence on r2") + router = tgen.gears['r2'] + json_file = "{}/{}/bgp_ipv4_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + assert 0, 'bgp_ipv4_routes.json file not found' + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp vrf vrf1 ipv4 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vpnv4_noretain/__init__.py b/tests/topotests/bgp_vpnv4_noretain/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_noretain/r1/bgpd.conf new file mode 100644 index 0000000..0709e43 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r1/bgpd.conf @@ -0,0 +1,46 @@ +router bgp 65500 + bgp router-id 192.0.2.1 + neighbor 10.125.0.2 remote-as 65500 + address-family ipv4 unicast + no neighbor 10.125.0.2 activate + label vpn export 100 + rd vpn export 192.0.2.1:0 + rt vpn import 192.0.2.2:400 + import vpn + exit-address-family + address-family ipv4 vpn + neighbor 10.125.0.2 activate + no bgp retain route-target all + exit-address-family +! +router bgp 65500 vrf vrf1 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + redistribute connected + label vpn export 101 + rd vpn export 192.0.2.1:1 + rt vpn import 192.0.2.2:100 + rt vpn export 192.0.2.1:100 + export vpn + import vpn + exit-address-family +! +router bgp 65500 vrf vrf3 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + redistribute connected + label vpn export 103 + rd vpn export 192.0.2.1:3 + rt vpn export 192.0.2.1:300 + export vpn + exit-address-family +! +router bgp 65500 vrf vrf4 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + label vpn export 104 + rd vpn export 192.0.2.1:4 + rt vpn import 192.0.2.1:300 + import vpn + exit-address-family +! diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_all.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_all.json new file mode 100644 index 0000000..648bf85 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_all.json @@ -0,0 +1,175 @@ +{ + "vrfId":0, + "vrfName":"default", + "routerId":"192.0.2.1", + "defaultLocPrf":100, + "localAS":65500, + "routes":{ + "routeDistinguishers":{ + "192.0.2.1:1":{ + "10.101.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.101.0.0", + "prefixLen":24, + "network":"10.101.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"incomplete", + "announceNexthopSelf":true, + "nhVrfName":"vrf1", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.1:3":{ + "10.103.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.103.0.0", + "prefixLen":24, + "network":"10.103.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"incomplete", + "announceNexthopSelf":true, + "nhVrfName":"vrf3", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:1":{ + "10.201.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.201.0.0", + "prefixLen":24, + "network":"10.201.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:2":{ + "10.202.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.202.0.0", + "prefixLen":24, + "network":"10.202.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:3":{ + "10.203.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.203.0.0", + "prefixLen":24, + "network":"10.203.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:4":{ + "10.204.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.204.0.0", + "prefixLen":24, + "network":"10.204.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init.json new file mode 100644 index 0000000..f01607a --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init.json @@ -0,0 +1,121 @@ +{ + "vrfId":0, + "vrfName":"default", + "routerId":"192.0.2.1", + "defaultLocPrf":100, + "localAS":65500, + "routes":{ + "routeDistinguishers":{ + "192.0.2.1:1":{ + "10.101.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.101.0.0", + "prefixLen":24, + "network":"10.101.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"incomplete", + "announceNexthopSelf":true, + "nhVrfName":"vrf1", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.1:3":{ + "10.103.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.103.0.0", + "prefixLen":24, + "network":"10.103.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"incomplete", + "announceNexthopSelf":true, + "nhVrfName":"vrf3", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:1":{ + "10.201.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.201.0.0", + "prefixLen":24, + "network":"10.201.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:4":{ + "10.204.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.204.0.0", + "prefixLen":24, + "network":"10.204.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json new file mode 100644 index 0000000..6df6c69 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json @@ -0,0 +1,148 @@ +{ + "vrfId":0, + "vrfName":"default", + "routerId":"192.0.2.1", + "defaultLocPrf":100, + "localAS":65500, + "routes":{ + "routeDistinguishers":{ + "192.0.2.1:1":{ + "10.101.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.101.0.0", + "prefixLen":24, + "network":"10.101.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"incomplete", + "announceNexthopSelf":true, + "nhVrfName":"vrf1", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.1:3":{ + "10.103.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.103.0.0", + "prefixLen":24, + "network":"10.103.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"incomplete", + "announceNexthopSelf":true, + "nhVrfName":"vrf3", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:1":{ + "10.201.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.201.0.0", + "prefixLen":24, + "network":"10.201.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:2":{ + "10.202.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.202.0.0", + "prefixLen":24, + "network":"10.202.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:4":{ + "10.204.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.204.0.0", + "prefixLen":24, + "network":"10.204.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json new file mode 100644 index 0000000..7a17ff0 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json @@ -0,0 +1,148 @@ +{ + "vrfId":0, + "vrfName":"default", + "routerId":"192.0.2.1", + "defaultLocPrf":100, + "localAS":65500, + "routes":{ + "routeDistinguishers":{ + "192.0.2.1:1":{ + "10.101.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.101.0.0", + "prefixLen":24, + "network":"10.101.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"incomplete", + "announceNexthopSelf":true, + "nhVrfName":"vrf1", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.1:3":{ + "10.103.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.103.0.0", + "prefixLen":24, + "network":"10.103.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"incomplete", + "announceNexthopSelf":true, + "nhVrfName":"vrf3", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:1":{ + "10.201.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.201.0.0", + "prefixLen":24, + "network":"10.201.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:3":{ + "10.203.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.203.0.0", + "prefixLen":24, + "network":"10.203.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:4":{ + "10.204.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.204.0.0", + "prefixLen":24, + "network":"10.204.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_init.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_init.json new file mode 100644 index 0000000..2769c6e --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_init.json @@ -0,0 +1,156 @@ +{ + "default": { + "vrfName": "default", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.204.0.0/24": [ + { + "pathFrom": "external", + "prefix": "10.204.0.0", + "prefixLen": 24, + "network": "10.204.0.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "default", + "nexthops": [ + { + "ip": "10.125.0.2", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "vrf1": { + "vrfName": "vrf1", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.101.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.101.0.0", + "prefixLen": 24, + "network": "10.101.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.201.0.0/24": [ + { + "pathFrom": "external", + "prefix": "10.201.0.0", + "prefixLen": 24, + "network": "10.201.0.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "default", + "nexthops": [ + { + "ip": "10.125.0.2", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "vrf3": { + "vrfName": "vrf3", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.103.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.103.0.0", + "prefixLen": 24, + "network": "10.103.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "vrf4": { + "vrfName": "vrf4", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.103.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.103.0.0", + "prefixLen": 24, + "network": "10.103.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf3", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r1_vrf1.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r1_vrf1.json new file mode 100644 index 0000000..488dc4a --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r1_vrf1.json @@ -0,0 +1,190 @@ +{ + "default": { + "vrfName": "default", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.204.0.0/24": [ + { + "pathFrom": "external", + "prefix": "10.204.0.0", + "prefixLen": 24, + "network": "10.204.0.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "default", + "nexthops": [ + { + "ip": "10.125.0.2", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "vrf1": { + "vrfName": "vrf1", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.101.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.101.0.0", + "prefixLen": 24, + "network": "10.101.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.201.0.0/24": [ + { + "pathFrom": "external", + "prefix": "10.201.0.0", + "prefixLen": 24, + "network": "10.201.0.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "default", + "nexthops": [ + { + "ip": "10.125.0.2", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "vrf3": { + "vrfName": "vrf3", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.103.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.103.0.0", + "prefixLen": 24, + "network": "10.103.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "vrf4": { + "vrfName": "vrf4", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.103.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.103.0.0", + "prefixLen": 24, + "network": "10.103.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf3", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "vrf2": { + "vrfName": "vrf2", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.101.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.101.0.0", + "prefixLen": 24, + "network": "10.101.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf1", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf2.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf2.json new file mode 100644 index 0000000..b751756 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf2.json @@ -0,0 +1,188 @@ +{ + "default": { + "vrfName": "default", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.204.0.0/24": [ + { + "pathFrom": "external", + "prefix": "10.204.0.0", + "prefixLen": 24, + "network": "10.204.0.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "default", + "nexthops": [ + { + "ip": "10.125.0.2", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "vrf1": { + "vrfName": "vrf1", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.101.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.101.0.0", + "prefixLen": 24, + "network": "10.101.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.201.0.0/24": [ + { + "pathFrom": "external", + "prefix": "10.201.0.0", + "prefixLen": 24, + "network": "10.201.0.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "default", + "nexthops": [ + { + "ip": "10.125.0.2", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "vrf3": { + "vrfName": "vrf3", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.103.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.103.0.0", + "prefixLen": 24, + "network": "10.103.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "vrf4": { + "vrfName": "vrf4", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.103.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.103.0.0", + "prefixLen": 24, + "network": "10.103.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf3", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "vrf2": { + "vrfName": "vrf2", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.202.0.0/24": [ + { + "pathFrom": "external", + "prefix": "10.202.0.0", + "prefixLen": 24, + "network": "10.202.0.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "default", + "nexthops": [ + { + "ip": "10.125.0.2", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf3.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf3.json new file mode 100644 index 0000000..49d4066 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf3.json @@ -0,0 +1,188 @@ +{ + "default": { + "vrfName": "default", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.204.0.0/24": [ + { + "pathFrom": "external", + "prefix": "10.204.0.0", + "prefixLen": 24, + "network": "10.204.0.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "default", + "nexthops": [ + { + "ip": "10.125.0.2", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "vrf1": { + "vrfName": "vrf1", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.101.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.101.0.0", + "prefixLen": 24, + "network": "10.101.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.201.0.0/24": [ + { + "pathFrom": "external", + "prefix": "10.201.0.0", + "prefixLen": 24, + "network": "10.201.0.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "default", + "nexthops": [ + { + "ip": "10.125.0.2", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "vrf3": { + "vrfName": "vrf3", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.103.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.103.0.0", + "prefixLen": 24, + "network": "10.103.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "vrf4": { + "vrfName": "vrf4", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.103.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.103.0.0", + "prefixLen": 24, + "network": "10.103.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf3", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "vrf2": { + "vrfName": "vrf2", + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "10.203.0.0/24": [ + { + "pathFrom": "external", + "prefix": "10.203.0.0", + "prefixLen": 24, + "network": "10.203.0.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "default", + "nexthops": [ + { + "ip": "10.125.0.2", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/zebra.conf b/tests/topotests/bgp_vpnv4_noretain/r1/zebra.conf new file mode 100644 index 0000000..f99cfaf --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r1/zebra.conf @@ -0,0 +1,16 @@ +log stdout +interface lo + ip address 192.0.2.1/32 +! +interface r1-gre0 + ip address 192.168.0.1/24 +! +interface r1-eth0 + ip address 10.125.0.1/24 +! +interface r1-eth1 vrf vrf1 + ip address 10.101.0.1/24 +! +interface r1-eth3 vrf vrf3 + ip address 10.103.0.1/24 +! \ No newline at end of file diff --git a/tests/topotests/bgp_vpnv4_noretain/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_noretain/r2/bgpd.conf new file mode 100644 index 0000000..729daef --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r2/bgpd.conf @@ -0,0 +1,54 @@ +router bgp 65500 + bgp router-id 192.0.2.2 + neighbor 10.125.0.1 remote-as 65500 + address-family ipv4 unicast + no neighbor 10.125.0.1 activate + exit-address-family + address-family ipv4 vpn + neighbor 10.125.0.1 activate + exit-address-family +! +router bgp 65500 vrf vrf1 + bgp router-id 192.0.2.2 + address-family ipv4 unicast + redistribute connected + label vpn export 201 + rd vpn export 192.0.2.2:1 + rt vpn import 192.0.2.1:100 192.0.2.2:100 192.0.2.2:200 + rt vpn export 192.0.2.2:100 + export vpn + import vpn + exit-address-family +! +router bgp 65500 vrf vrf2 + bgp router-id 192.0.2.2 + address-family ipv4 unicast + redistribute connected + label vpn export 202 + rd vpn export 192.0.2.2:2 + rt vpn import 192.0.2.1:100 192.0.2.2:100 192.0.2.2:200 + rt vpn export 192.0.2.2:200 + export vpn + import vpn + exit-address-family +! +router bgp 65500 vrf vrf3 + bgp router-id 192.0.2.2 + address-family ipv4 unicast + redistribute connected + label vpn export 203 + rd vpn export 192.0.2.2:3 + rt vpn export 192.0.2.2:300 + export vpn + exit-address-family +! +router bgp 65500 vrf vrf4 + bgp router-id 192.0.2.2 + address-family ipv4 unicast + redistribute connected + label vpn export 204 + rd vpn export 192.0.2.2:4 + rt vpn export 192.0.2.2:400 + export vpn + exit-address-family +! diff --git a/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_routes_all.json b/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_routes_all.json new file mode 100644 index 0000000..d8b8e88 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_routes_all.json @@ -0,0 +1,177 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId":"192.0.2.2", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "routeDistinguishers": { + "192.0.2.1:1": { + "10.101.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "internal", + "prefix": "10.101.0.0", + "prefixLen": 24, + "network": "10.101.0.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "10.125.0.1", + "path": "", + "origin": "incomplete", + "nexthops": [ + { + "ip": "10.125.0.1", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + }, + "192.0.2.1:3": { + "10.103.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "internal", + "prefix": "10.103.0.0", + "prefixLen": 24, + "network": "10.103.0.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "10.125.0.1", + "path": "", + "origin": "incomplete", + "nexthops": [ + { + "ip": "10.125.0.1", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + }, + "192.0.2.2:1": { + "10.201.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "network": "10.201.0.0/24", + "prefixLen": 24, + "prefix": "10.201.0.0", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf1", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + }, + "192.0.2.2:2": { + "10.202.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "network": "10.202.0.0/24", + "prefixLen": 24, + "prefix": "10.202.0.0", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf2", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + }, + "192.0.2.2:3": { + "10.203.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.203.0.0", + "prefixLen": 24, + "network": "10.203.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf3", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + }, + "192.0.2.2:4": { + "10.204.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.204.0.0", + "prefixLen": 24, + "network": "10.204.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf4", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_summary.json b/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_summary.json new file mode 100644 index 0000000..a4408f1 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_summary.json @@ -0,0 +1,17 @@ +{ + "routerId":"192.0.2.2", + "as":65500, + "vrfId":0, + "vrfName":"default", + "peerCount":1, + "peers":{ + "10.125.0.1":{ + "remoteAs":65500, + "localAs":65500, + "version":4, + "state":"Established", + "peerState":"OK" + } + }, + "totalPeers":1 +} diff --git a/tests/topotests/bgp_vpnv4_noretain/r2/zebra.conf b/tests/topotests/bgp_vpnv4_noretain/r2/zebra.conf new file mode 100644 index 0000000..f19ad9d --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r2/zebra.conf @@ -0,0 +1,22 @@ +log stdout +interface lo + ip address 192.0.2.2/32 +! +interface r2-gre0 + ip address 192.168.0.2/24 +! +interface r2-eth0 + ip address 10.125.0.2/24 +! +interface r2-eth1 vrf vrf1 + ip address 10.201.0.2/24 +! +interface r2-eth2 vrf vrf2 + ip address 10.202.0.2/24 +! +interface r2-eth3 vrf vrf3 + ip address 10.203.0.1/24 +! +interface r2-eth4 vrf vrf4 + ip address 10.204.0.1/24 +! diff --git a/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py b/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py new file mode 100644 index 0000000..037dd40 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py @@ -0,0 +1,567 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_vpnv4_noretain.py +# Part of NetDEF Topology Tests +# +# Copyright 2022 6WIND S.A. +# + +""" + test_bgp_vpnv4_noretain.py: Do not keep the VPNvx entries when no + VRF matches incoming VPNVx entries +""" + +import os +import sys +import json +from functools import partial +from copy import deepcopy +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + tgen.add_router("r1") + tgen.add_router("r2") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s8") + switch.add_link(tgen.gears["r2"]) + + +def _populate_iface(): + tgen = get_topogen() + cmds_list = [ + "modprobe mpls_router", + "echo 100000 > /proc/sys/net/mpls/platform_labels", + "ip link add vrf1 type vrf table 10", + "ip link set dev vrf1 up", + "ip link set dev {0}-eth1 master vrf1", + "echo 1 > /proc/sys/net/mpls/conf/vrf1/input", + "ip link add vrf2 type vrf table 20", + "ip link set dev vrf2 up", + "ip link set dev {0}-eth2 master vrf2", + "echo 1 > /proc/sys/net/mpls/conf/vrf2/input", + "ip link add vrf3 type vrf table 30", + "ip link set dev vrf3 up", + "ip link set dev {0}-eth3 master vrf3", + "echo 1 > /proc/sys/net/mpls/conf/vrf3/input", + "ip link add vrf4 type vrf table 40", + "ip link set dev vrf4 up", + "ip link set dev {0}-eth4 master vrf4", + "echo 1 > /proc/sys/net/mpls/conf/vrf4/input", + ] + + for cmd in cmds_list: + input = cmd.format("r1", "1", "2") + logger.info("input: " + cmd) + output = tgen.net["r1"].cmd(cmd.format("r1", "1", "2")) + logger.info("output: " + output) + + for cmd in cmds_list: + input = cmd.format("r2", "2", "1") + logger.info("input: " + cmd) + output = tgen.net["r2"].cmd(cmd.format("r2", "2", "1")) + logger.info("output: " + output) + + +def setup_module(mod): + "Sets up the pytest environment" + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + _populate_iface() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def router_json_cmp_exact_filter(router, cmd, expected): + output = router.vtysh_cmd(cmd) + logger.info("{}: {}\n{}".format(router.name, cmd, output)) + + json_output = json.loads(output) + + # filter out tableVersion, version and nhVrfID + json_output.pop("tableVersion") + for rd, data in json_output["routes"]["routeDistinguishers"].items(): + for prefix, attrs in data.items(): + for attr in attrs: + if "nhVrfId" in attr: + attr.pop("nhVrfId") + if "version" in attr: + attr.pop("version") + + # filter out RD with no data (e.g. "444:3": {}) + json_tmp = deepcopy(json_output) + for rd, data in json_tmp["routes"]["routeDistinguishers"].items(): + if len(data.keys()) == 0: + json_output["routes"]["routeDistinguishers"].pop(rd) + + return topotest.json_cmp(json_output, expected, exact=True) + + +def router_vrf_json_cmp_exact_filter(router, cmd, expected): + output = router.vtysh_cmd(cmd) + logger.info("{}: {}\n{}".format(router.name, cmd, output)) + + json_output = json.loads(output) + + # filter out tableVersion, version, nhVrfId and vrfId + for vrf, data in json_output.items(): + if "vrfId" in data: + data.pop("vrfId") + if "tableVersion" in data: + data.pop("tableVersion") + if "routes" not in data: + continue + for route, attrs in data["routes"].items(): + for attr in attrs: + if "nhVrfId" in attr: + attr.pop("nhVrfId") + if "version" in attr: + attr.pop("version") + + # filter out VRF with no routes + json_tmp = deepcopy(json_output) + for vrf, data in json_tmp.items(): + if "routes" not in data or len(data["routes"].keys()) == 0: + json_output.pop(vrf) + + return topotest.json_cmp(json_output, expected, exact=True) + + +def check_show_bgp_ipv4_vpn(rname, json_file): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears[rname] + + logger.info("Checking VPNv4 routes for convergence on {}".format(rname)) + + json_file = "{}/{}/{}".format(CWD, router.name, json_file) + expected = json.loads(open(json_file).read()) + test_func = partial( + router_json_cmp_exact_filter, + router, + "show bgp ipv4 vpn json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def check_show_bgp_vrf_ipv4(rname, json_file): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears[rname] + + logger.info("Checking VRF IPv4 routes for convergence on {}".format(rname)) + + json_file = "{}/{}/{}".format(CWD, router.name, json_file) + expected = json.loads(open(json_file).read()) + test_func = partial( + router_vrf_json_cmp_exact_filter, + router, + "show bgp vrf all ipv4 unicast json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_protocols_convergence_step0(): + """ + Assert that all protocols have converged + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # check that r2 peerings are ok + logger.info("Checking BGP ipv4 vpn summary for r2") + router = tgen.gears["r2"] + json_file = "{}/{}/ipv4_vpn_summary.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp ipv4 vpn summary json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_bgp_no_retain_step1(): + """ + Check bgp no retain route-target all on r1 + """ + + rname = "r1" + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.json") + + +def test_bgp_retain_step2(): + """ + Apply and check bgp retain route-target all on r1 + """ + rname = "r1" + cfg = """ +configure +router bgp 65500 + address-family ipv4 vpn + bgp retain route-target all +""" + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_all.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.json") + + +def test_bgp_no_retain_step3(): + """ + Apply and check no bgp retain route-target all on r1 + """ + rname = "r1" + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + router.vtysh_cmd( + "configure\nrouter bgp 65500\naddress-family ipv4 vpn\nno bgp retain route-target all\n" + ) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.json") + + +def test_bgp_no_retain_add_vrf2_step4(): + """ + Add vrf2 on r1 and check bgp tables + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 vrf vrf2 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + redistribute connected + label vpn export 102 + rd vpn export 192.0.2.1:200 + rt vpn import 192.0.2.2:200 + import vpn + exit-address-family +! +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_plus_r2_vrf2.json") + + +def test_bgp_no_retain_unimport_vrf2_step5(): + """ + Unimport to vrf2 on r1 and check bgp tables + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 vrf vrf2 + address-family ipv4 unicast + no import vpn + exit-address-family +! +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.json") + + +def test_bgp_no_retain_import_vrf2_step6(): + """ + Re-import to vrf2 on r1 and check bgp tables + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 vrf vrf2 + address-family ipv4 unicast + import vpn + exit-address-family +! +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_plus_r2_vrf2.json") + + +def test_bgp_no_retain_import_vrf1_step7(): + """ + Import r1 vrf1 into r1 vrf2 and check bgp tables + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 vrf vrf2 + address-family ipv4 unicast + rt vpn import 192.0.2.1:100 + exit-address-family +! +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_plus_r1_vrf1.json") + + +def test_bgp_no_retain_import_vrf3_step8(): + """ + Import r2 vrf3 into r1 vrf2 and check bgp tables + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 vrf vrf2 + address-family ipv4 unicast + rt vpn import 192.0.2.2:300 + exit-address-family +! +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_plus_r2_vrf3.json") + + +def test_bgp_no_retain_unimport_vrf3_step9(): + """ + Un-import r2 vrf3 into r1 vrf2 and check bgp tables + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 vrf vrf2 + address-family ipv4 unicast + no rt vpn import 192.0.2.2:300 + exit-address-family +! +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.json") + + +def test_bgp_no_retain_import_vrf3_step10(): + """ + Import r2 vrf3 into r1 vrf2 and check bgp tables + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 vrf vrf2 + address-family ipv4 unicast + rt vpn import 192.0.2.2:300 + exit-address-family +! +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_plus_r2_vrf3.json") + + +def test_bgp_no_retain_remove_vrf2_step11(): + """ + Remove BGP vrf2 on r1 and check bgp tables + """ + + rname = "r1" + cfg = """ +configure +no router bgp 65500 vrf vrf2 +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.json") + + +def test_bgp_retain_step12(): + """ + Configure retain and check bgp tables + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 + address-family ipv4 vpn + bgp retain route-target all +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_all.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.json") + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/__init__.py b/tests/topotests/bgp_vpnv4_per_nexthop_label/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgp_ipv4_routes_vrf1.json b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgp_ipv4_routes_vrf1.json new file mode 100644 index 0000000..31a1f3d --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgp_ipv4_routes_vrf1.json @@ -0,0 +1,143 @@ +{ + "vrfName": "vrf1", + "localAS": 65500, + "routes": + { + "10.200.0.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "10.200.0.0", + "prefixLen": 24, + "network": "10.200.0.0\/24", + "nexthops": [ + { + "ip": "192.168.0.2", + "afi": "ipv4", + "used": true + } + ] + } + + ], + "172.31.0.11/32": [ + { + "valid":true, + "bestpath":true, + "prefix":"172.31.0.11", + "prefixLen":32, + "network":"172.31.0.11/32", + "peerId":"192.0.2.100", + "nexthops":[ + { + "ip":"192.0.2.11", + "afi":"ipv4", + "used":true + } + ] + } + ], + "172.31.0.12/32": [ + { + "valid":true, + "bestpath":true, + "prefix":"172.31.0.12", + "prefixLen":32, + "network":"172.31.0.12/32", + "peerId":"192.0.2.100", + "nexthops":[ + { + "ip":"192.0.2.12", + "afi":"ipv4", + "used":true + } + ] + } + ], + "172.31.0.13/32": [ + { + "valid":true, + "bestpath":true, + "prefix":"172.31.0.13", + "prefixLen":32, + "network":"172.31.0.13/32", + "peerId":"192.168.255.13", + "nexthops":[ + { + "ip":"192.168.255.13", + "afi":"ipv4", + "used":true + } + ] + } + ], + "172.31.0.14/32": [ + { + "valid":true, + "bestpath":true, + "prefix":"172.31.0.14", + "prefixLen":32, + "network":"172.31.0.14/32", + "peerId":"(unspec)", + "nexthops":[ + { + "ip":"192.0.2.14", + "afi":"ipv4", + "used":true + } + ] + } + ], + "172.31.0.15/32": [ + { + "valid":true, + "bestpath":true, + "prefix":"172.31.0.15", + "prefixLen":32, + "network":"172.31.0.15/32", + "peerId":"(unspec)", + "nexthops":[ + { + "ip":"192.0.2.12", + "afi":"ipv4", + "used":true + } + ] + } + ], + "172.31.0.20/32": [ + { + "valid":true, + "bestpath":true, + "prefix":"172.31.0.20", + "prefixLen":32, + "network":"172.31.0.20/32", + "peerId":"192.0.2.100", + "nexthops":[ + { + "ip":"192.0.2.11", + "afi":"ipv4", + "used":true + } + ] + } + ], + "172.31.0.111/32": [ + { + "valid":true, + "bestpath":true, + "prefix":"172.31.0.111", + "prefixLen":32, + "network":"172.31.0.111/32", + "peerId":"192.0.2.100", + "nexthops":[ + { + "ip":"192.0.2.11", + "afi":"ipv4", + "used":true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgpd.conf new file mode 100644 index 0000000..35fb2ec --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgpd.conf @@ -0,0 +1,30 @@ +router bgp 65500 + bgp router-id 192.168.0.1 + no bgp ebgp-requires-policy + neighbor 192.168.0.2 remote-as 65501 + address-family ipv4 unicast + no neighbor 192.168.0.2 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.168.0.2 activate + neighbor 192.168.0.2 soft-reconfiguration inbound + exit-address-family +! +router bgp 65500 vrf vrf1 + bgp router-id 192.168.0.1 + neighbor 192.0.2.100 remote-as 65500 + neighbor 192.168.255.13 remote-as 65500 + address-family ipv4 unicast + redistribute connected + redistribute static + label vpn export allocation-mode per-nexthop + label vpn export auto + rd vpn export 444:1 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +interface r1-eth0 + mpls bgp forwarding +! diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/ipv4_routes.json b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/ipv4_routes.json new file mode 100644 index 0000000..da7d281 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/ipv4_routes.json @@ -0,0 +1,50 @@ +{ + "10.200.0.0/24": [ + { + "prefix": "10.200.0.0/24", + "prefixLen": 24, + "protocol": "bgp", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.125.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "vrf": "default", + "active": true, + "labels":[ + 102 + ] + } + ] + } + ], + "10.201.0.0/24": [ + { + "prefix": "10.201.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "nexthops":[ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/zebra.conf new file mode 100644 index 0000000..2618595 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/zebra.conf @@ -0,0 +1,18 @@ +log stdout +debug zebra nht +!debug zebra kernel msgdump recv +!debug zebra dplane detailed +!debug zebra packet recv +interface r1-eth1 vrf vrf1 + ip address 192.0.2.1/24 +! +interface r1-eth2 vrf vrf1 + ip address 192.168.255.1/24 +! +interface r1-eth0 + ip address 192.168.0.1/24 +! +vrf vrf1 + ip route 172.31.0.14/32 192.0.2.14 + ip route 172.31.0.15/32 192.0.2.12 +exit-vrf diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/bgpd.conf new file mode 100644 index 0000000..5da9151 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 65500 + bgp router-id 192.0.2.11 + no bgp network import-check + neighbor 192.0.2.100 remote-as 65500 + address-family ipv4 unicast + network 172.31.0.11/32 + network 172.31.0.111/32 + network 172.31.0.20/32 + exit-address-family +! + diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/zebra.conf new file mode 100644 index 0000000..a080757 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface r11-eth0 + ip address 192.0.2.11/24 +! diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/bgpd.conf new file mode 100644 index 0000000..d3889f5 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65500 + bgp router-id 192.0.2.12 + no bgp network import-check + neighbor 192.0.2.100 remote-as 65500 + address-family ipv4 unicast + network 172.31.0.12/32 + exit-address-family +! + diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/zebra.conf new file mode 100644 index 0000000..9ce3aba --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface r12-eth0 + ip address 192.0.2.12/24 +! diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/bgpd.conf new file mode 100644 index 0000000..21dbb58 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65500 + bgp router-id 192.168.255.13 + no bgp network import-check + address-family ipv4 unicast + neighbor 192.168.255.1 remote-as 65500 + network 172.31.0.13/32 + exit-address-family +! + diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/zebra.conf new file mode 100644 index 0000000..4d78b5f --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface r13-eth0 + ip address 192.168.255.13/24 +! diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_ipv4_routes.json new file mode 100644 index 0000000..3407925 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_ipv4_routes.json @@ -0,0 +1,38 @@ +{ + "vrfName": "vrf1", + "localAS": 65501, + "routes": + { + "10.201.0.0/24": [ + { + "prefix": "10.201.0.0", + "prefixLen": 24, + "network": "10.201.0.0\/24", + "nhVrfName": "default", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.200.0.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "10.200.0.0", + "prefixLen": 24, + "network": "10.200.0.0\/24", + "nexthops": [ + { + "ip": "0.0.0.0", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_vpnv4_routes.json b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_vpnv4_routes.json new file mode 100644 index 0000000..46f4a18 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_vpnv4_routes.json @@ -0,0 +1,187 @@ +{ + "vrfName": "default", + "localAS": 65501, + "routes": + { + "routeDistinguishers": + { + "444:1": + { + "172.31.0.11/32": [ + { + "valid": true, + "bestpath": true, + "prefix": "172.31.0.11", + "prefixLen": 32, + "network": "172.31.0.11\/32", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.12/32": [ + { + "valid": true, + "bestpath": true, + "prefix": "172.31.0.12", + "prefixLen": 32, + "network": "172.31.0.12\/32", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.13/32": [ + { + "valid": true, + "bestpath": true, + "prefix": "172.31.0.13", + "prefixLen": 32, + "network": "172.31.0.13\/32", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.14/32": [ + { + "valid": true, + "bestpath": true, + "prefix": "172.31.0.14", + "prefixLen": 32, + "network": "172.31.0.14\/32", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.15/32": [ + { + "valid": true, + "bestpath": true, + "prefix": "172.31.0.15", + "prefixLen": 32, + "network": "172.31.0.15\/32", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.20/32": [ + { + "valid": true, + "bestpath": true, + "prefix": "172.31.0.20", + "prefixLen": 32, + "network": "172.31.0.20\/32", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.111/32": [ + { + "valid": true, + "bestpath": true, + "prefix": "172.31.0.111", + "prefixLen": 32, + "network": "172.31.0.111\/32", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "192.0.2.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "192.0.2.0", + "prefixLen": 24, + "network": "192.0.2.0\/24", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "192.168.255.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "192.168.255.0", + "prefixLen": 24, + "network": "192.168.255.0\/24", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ] + }, + "444:2": + { + "10.200.0.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "10.200.0.0", + "prefixLen": 24, + "network": "10.200.0.0\/24", + "peerId": "(unspec)", + "nhVrfName": "vrf1", + "nexthops": [ + { + "ip": "0.0.0.0", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgpd.conf new file mode 100644 index 0000000..5fb7902 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgpd.conf @@ -0,0 +1,25 @@ +router bgp 65501 + bgp router-id 192.168.0.2 + no bgp ebgp-requires-policy + neighbor 192.168.0.1 remote-as 65500 + address-family ipv4 unicast + no neighbor 192.168.0.1 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.168.0.1 activate + exit-address-family +! +router bgp 65501 vrf vrf1 + bgp router-id 192.168.0.2 + address-family ipv4 unicast + redistribute connected + label vpn export 102 + rd vpn export 444:2 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +interface r2-eth0 + mpls bgp forwarding +! diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/zebra.conf new file mode 100644 index 0000000..b7283a3 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/zebra.conf @@ -0,0 +1,7 @@ +log stdout +interface r2-eth1 vrf vrf1 + ip address 10.200.0.2/24 +! +interface r2-eth0 + ip address 192.168.0.2/24 +! diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/bgpd.conf new file mode 100644 index 0000000..ff32314 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 65500 + bgp router-id 100.100.100.100 + no bgp network import-check + neighbor 192.0.2.1 remote-as 65500 + neighbor 192.0.2.11 remote-as 65500 + neighbor 192.0.2.12 remote-as 65500 + address-family ipv4 unicast + neighbor 192.0.2.1 route-reflector-client + neighbor 192.0.2.11 route-reflector-client + neighbor 192.0.2.12 route-reflector-client + exit-address-family +! + diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/zebra.conf new file mode 100644 index 0000000..315c22a --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface rr-eth0 + ip address 192.0.2.100/24 +! diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py b/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py new file mode 100644 index 0000000..ce278ed --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py @@ -0,0 +1,806 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# test_bgp_vpnv4_per_nexthop_label.py +# +# Copyright 2023 6WIND S.A. +# + +""" + test_bgp_vpnv4_per_nexthop_label.py: Test the FRR BGP daemon using EBGP peering + Let us exchange VPNv4 updates between both devices + Updates from r1 will originate from the same RD, but will have separate + label values. + + +----------+ + | r11 | + |192.0.2.11+---+ + | | | +----+--------+ +----------+ + +----------+ | 192.0.2.1 |vrf | r1 |192.168.0.0/24| r2 | + +-------------------+ | 1+--------------+ | + +----------+ | |VRF1|AS65500 | | AS65501 | + | r12 | | +-------------+ | VPNV4| |VPNV4 | + |192.0.2.12+---+ |192.168.255.1+-+--+--------+ +----------+ + | | | + +----------+ | + | + +----------+ | + | r13 | | + |192.168. +---------+ + | 255.13 | + +----------+ +""" + +import os +import sys +import json +from functools import partial +import pytest +import functools + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + + +pytestmark = [pytest.mark.bgpd] + +PREFIXES_R11 = ["172.31.0.11/32", "172.31.0.20/32", "172.31.0.111/32"] +PREFIXES_R12 = ["172.31.0.12/32", "172.31.0.15/32"] +PREFIXES_R13 = ["172.31.0.13/32"] +PREFIXES_REDIST = ["172.31.0.14/32"] +PREFIXES_CONNECTED = ["192.168.255.0/24", "192.0.2.0/24"] + + +def build_topo(tgen): + "Build function" + + # Create 2 routers. + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r11") + tgen.add_router("r12") + tgen.add_router("r13") + tgen.add_router("r14") + tgen.add_router("rr") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r11"]) + switch.add_link(tgen.gears["r12"]) + switch.add_link(tgen.gears["rr"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r13"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r14"]) + + +def _populate_iface(): + tgen = get_topogen() + cmds_list = [ + "ip link add vrf1 type vrf table 10", + "echo 100000 > /proc/sys/net/mpls/platform_labels", + "ip link set dev vrf1 up", + "ip link set dev {0}-eth1 master vrf1", + "echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input", + ] + cmds_list_plus = [ + "ip link set dev {0}-eth2 master vrf1", + ] + + for cmd in cmds_list: + input = cmd.format("r1") + logger.info("input: " + cmd) + output = tgen.net["r1"].cmd(cmd.format("r1")) + logger.info("output: " + output) + + for cmd in cmds_list_plus: + input = cmd.format("r1") + logger.info("input: " + cmd) + output = tgen.net["r1"].cmd(cmd.format("r1")) + logger.info("output: " + output) + + for cmd in cmds_list: + input = cmd.format("r2") + logger.info("input: " + cmd) + output = tgen.net["r2"].cmd(cmd.format("r2")) + logger.info("output: " + output) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + _populate_iface() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def bgp_vpnv4_table_check(router, group, label_list=None, label_value_expected=None): + """ + Dump and check that vpnv4 entries have the same MPLS label value + * 'router': the router to check + * 'group': the list of prefixes to check. a single label value for the group has to be found + * 'label_list': check that the label values are not present in the vpnv4 entries + * that list is updated with the present label value + * 'label_value_expected': check that the mpls label read is the same as that value + """ + + stored_label_inited = False + for prefix in group: + dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True) + assert dump, "{0}, {1}, route distinguisher not present".format( + router.name, prefix + ) + for rd, pathes in dump.items(): + for path in pathes["paths"]: + assert ( + "remoteLabel" in path.keys() + ), "{0}, {1}, remoteLabel not present".format(router.name, prefix) + logger.info( + "{0}, {1}, label value is {2}".format( + router.name, prefix, path["remoteLabel"] + ) + ) + if stored_label_inited: + assert ( + path["remoteLabel"] == stored_label + ), "{0}, {1}, label value not expected one (expected {2}, observed {3}".format( + router.name, prefix, stored_label, path["remoteLabel"] + ) + else: + stored_label = path["remoteLabel"] + stored_label_inited = True + if label_list is not None: + assert ( + stored_label not in label_list + ), "{0}, {1}, label already detected in a previous prefix".format( + router.name, prefix + ) + label_list.add(stored_label) + + if label_value_expected: + assert ( + path["remoteLabel"] == label_value_expected + ), "{0}, {1}, label value not expected (expected {2}, observed {3}".format( + router.name, prefix, label_value_expected, path["remoteLabel"] + ) + + +def bgp_vpnv4_table_check_all(router, label_list=None, same=False): + """ + Dump and check that vpnv4 entries are correctly configured with specific label values + * 'router': the router to check + * 'label_list': check that the label values are not present in the vpnv4 entries + * that list is updated with the present label value found. + * 'same': by default, set to False. Addresses groups are classified by addresses. + * if set to True, all entries of all groups should have a unique label value + """ + if same: + bgp_vpnv4_table_check( + router, + group=PREFIXES_R11 + + PREFIXES_R12 + + PREFIXES_R13 + + PREFIXES_REDIST + + PREFIXES_CONNECTED, + label_list=label_list, + ) + else: + for group in ( + PREFIXES_R11, + PREFIXES_R12, + PREFIXES_R13, + PREFIXES_REDIST, + PREFIXES_CONNECTED, + ): + bgp_vpnv4_table_check(router, group=group, label_list=label_list) + + +def check_show_mpls_table(router, blacklist=None, label_list=None, whitelist=None): + nexthop_list = [] + if blacklist: + nexthop_list.append(blacklist) + + dump = router.vtysh_cmd("show mpls table json", isjson=True) + for in_label, label_info in dump.items(): + if label_list is not None: + label_list.add(in_label) + for nh in label_info["nexthops"]: + if "installed" not in nh.keys(): + return "{} {} is not installed yet on {}".format( + in_label, label_info, router.name + ) + if nh["installed"] != True or nh["type"] != "BGP": + return "{}, show mpls table, nexthop is not installed".format( + router.name + ) + if "nexthop" in nh.keys(): + if nh["nexthop"] in nexthop_list: + return "{}, show mpls table, duplicated or blacklisted nexthop address".format( + router.name + ) + nexthop_list.append(nh["nexthop"]) + elif "interface" in nh.keys(): + if nh["interface"] in nexthop_list: + return "{}, show mpls table, duplicated or blacklisted nexthop interface".format( + router.name + ) + nexthop_list.append(nh["interface"]) + else: + return "{}, show mpls table, entry with neither nexthop nor interface".format( + router.name + ) + + if whitelist: + for entry in whitelist: + if entry not in nexthop_list: + return "{}, show mpls table, entry with nexthop {} not present in nexthop list".format( + router.name, entry + ) + return None + + +def mpls_table_check(router, blacklist=None, label_list=None, whitelist=None): + """ + Dump and check 'show mpls table json' output. An assert is triggered in case test fails + * 'router': the router to check + * 'blacklist': the list of nexthops (IP or interface) that should not be on output + * 'label_list': the list of labels that should be in inLabel value + * 'whitelist': the list of nexthops (IP or interface) that should be on output + """ + logger.info("Checking MPLS labels on {}".format(router.name)) + # Check r2 removed 172.31.0.30 vpnv4 update + test_func = functools.partial( + check_show_mpls_table, router, blacklist, label_list, whitelist + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, MPLS labels check fail: {}".format(router.name, result) + + +def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None): + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + if label: + expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} + else: + expected = {rd: {"prefix": prefix}} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + +def check_show_bgp_vpn_prefix_found(router, ipversion, prefix, rd): + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + expected = {rd: {"prefix": prefix}} + return topotest.json_cmp(output, expected) + + +def check_show_mpls_table_entry_label_found(router, inlabel, interface): + output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) + expected = { + "inLabel": inlabel, + "installed": True, + "nexthops": [{"interface": interface}], + } + return topotest.json_cmp(output, expected) + + +def check_show_mpls_table_entry_label_not_found(router, inlabel): + output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) + expected = {"inlabel": inlabel, "installed": True} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + +def mpls_entry_get_interface(router, label): + """ + Assert that the label is in MPLS table + Assert an outgoing interface is programmed + return the outgoing interface + """ + outgoing_interface = None + + logger.info("Checking MPLS labels on {}".format(router.name)) + dump = router.vtysh_cmd("show mpls table {} json".format(label), isjson=True) + assert dump, "{0}, label {1} not present".format(router.name, label) + + for nh in dump["nexthops"]: + assert ( + "interface" in nh.keys() + ), "{}, show mpls table, nexthop interface not present for MPLS entry {}".format( + router.name, label + ) + + outgoing_interface = nh["interface"] + + return outgoing_interface + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check BGP IPv4 routing tables on VRF1 of r1 + logger.info("Checking BGP IPv4 routes for convergence on r1 VRF1") + router = tgen.gears["r1"] + json_file = "{}/{}/bgp_ipv4_routes_vrf1.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp vrf vrf1 ipv4 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + logger.info("Checking BGP VPNv4 routes for convergence on r2") + router = tgen.gears["r2"] + json_file = "{}/{}/bgp_vpnv4_routes.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp ipv4 vpn json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check BGP labels received on r2 + logger.info("Checking BGP VPNv4 labels on r2") + label_list = set() + bgp_vpnv4_table_check_all(tgen.gears["r2"], label_list) + + # Check MPLS labels received on r1 + mpls_table_check(tgen.gears["r1"], label_list) + + +def test_flapping_bgp_vrf_down(): + """ + Turn down a remote BGP session + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("Unpeering BGP on r11") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp 65500\nno neighbor 192.0.2.100\n", + isjson=False, + ) + + def _bgp_prefix_not_found(router, vrf, ipversion, prefix): + output = json.loads( + router.vtysh_cmd( + "show bgp vrf {} {} {} json".format(vrf, ipversion, prefix) + ) + ) + expected = {"prefix": prefix} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + # Check prefix from r11 is not present + test_func = functools.partial( + _bgp_prefix_not_found, tgen.gears["r1"], "vrf1", "ipv4", "172.31.0.11/32" + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert ( + success + ), "r1, prefix 172.31.0.11/32 from r11 did not disappear. r11 still connected to rr ?" + + # Check BGP updated received on r2 are not from r11 + logger.info("Checking BGP VPNv4 labels on r2") + for entry in PREFIXES_R11: + dump = tgen.gears["r2"].vtysh_cmd( + "show bgp ipv4 vpn {} json".format(entry), isjson=True + ) + for rd in dump: + assert False, "r2, {}, route distinguisher {} present".format(entry, rd) + + mpls_table_check(tgen.gears["r1"], blacklist=["192.0.2.11"]) + + +def test_flapping_bgp_vrf_up(): + """ + Turn up a remote BGP session + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("Peering BGP on r11") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp 65500\nneighbor 192.0.2.100 remote-as 65500\n", + isjson=False, + ) + + # Check r2 gets prefix 172.31.0.11/128 + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + tgen.gears["r2"], + "ipv4", + "172.31.0.11/32", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert ( + success + ), "r2, prefix 172.31.0.11/32 from r11 not present. r11 still disconnected from rr ?" + bgp_vpnv4_table_check_all(tgen.gears["r2"]) + + +def test_recursive_route(): + """ + Test static recursive route redistributed over BGP + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Enabling recursive static route") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nvrf vrf1\nip route 172.31.0.30/32 172.31.0.20\n", + isjson=False, + ) + logger.info("Checking BGP VPNv4 labels on r2") + + # Check r2 received vpnv4 update with 172.31.0.30 + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + tgen.gears["r2"], + "ipv4", + "172.31.0.30/32", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, vpnv4 update 172.31.0.30 not found" + + bgp_vpnv4_table_check(tgen.gears["r2"], group=PREFIXES_R11 + ["172.31.0.30/32"]) + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + logger.info("Dumping nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 nexthop detail", isjson=False) + + logger.info("Disabling recursive static route") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nvrf vrf1\nno ip route 172.31.0.30/32 172.31.0.20\n", + isjson=False, + ) + logger.info("Checking BGP VPNv4 labels on r2") + + # Check r2 removed 172.31.0.30 vpnv4 update + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + tgen.gears["r2"], + "ipv4", + "172.31.0.30/32", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, vpnv4 update 172.31.0.30 still present" + + +def test_prefix_changes_interface(): + """ + Test BGP update for a given prefix learnt on different interface + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Enabling a 172.31.0.50/32 prefix for r11") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv4 unicast\nnetwork 172.31.0.50/32", + isjson=False, + ) + + # Check r2 received vpnv4 update with 172.31.0.50 + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + tgen.gears["r2"], + "ipv4", + "172.31.0.50/32", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, vpnv4 update 172.31.0.50 not found" + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + + label_list = set() + bgp_vpnv4_table_check( + tgen.gears["r2"], + group=["172.31.0.11/32", "172.31.0.111/32", "172.31.0.50/32"], + label_list=label_list, + ) + + assert ( + len(label_list) == 1 + ), "Multiple Label values found for updates from r11 found" + + oldlabel = label_list.pop() + logger.info("r1, getting the outgoing interface used by label {}".format(oldlabel)) + old_outgoing_interface = mpls_entry_get_interface(tgen.gears["r1"], oldlabel) + logger.info( + "r1, outgoing interface used by label {} is {}".format( + oldlabel, old_outgoing_interface + ) + ) + + logger.info("Moving the 172.31.0.50/32 prefix from r11 to r13") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv4 unicast\nno network 172.31.0.50/32", + isjson=False, + ) + tgen.gears["r13"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv4 unicast\nnetwork 172.31.0.50/32", + isjson=False, + ) + + # Check r2 removed 172.31.0.50 vpnv4 update with old label + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + tgen.gears["r2"], + "ipv4", + "172.31.0.50/32", + "444:1", + label=oldlabel, + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert ( + success + ), "r2, vpnv4 update 172.31.0.50 with old label {0} still present".format(oldlabel) + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + + # Check r2 received new 172.31.0.50 vpnv4 update + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + tgen.gears["r2"], + "ipv4", + "172.31.0.50/32", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, vpnv4 update 172.31.0.50 not found" + + label_list = set() + bgp_vpnv4_table_check( + tgen.gears["r2"], + group=PREFIXES_R13 + ["172.31.0.50/32"], + label_list=label_list, + ) + assert ( + len(label_list) == 1 + ), "Multiple Label values found for updates from r13 found" + + newlabel = label_list.pop() + logger.info("r1, getting the outgoing interface used by label {}".format(newlabel)) + new_outgoing_interface = mpls_entry_get_interface(tgen.gears["r1"], newlabel) + logger.info( + "r1, outgoing interface used by label {} is {}".format( + newlabel, new_outgoing_interface + ) + ) + if old_outgoing_interface == new_outgoing_interface: + assert 0, "r1, outgoing interface did not change whereas BGP update moved" + + logger.info("Restoring state by removing the 172.31.0.50/32 prefix from r13") + tgen.gears["r13"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv4 unicast\nno network 172.31.0.50/32", + isjson=False, + ) + + +def test_changing_default_label_value(): + """ + Change the MPLS default value + Check that r1 VPNv4 entries have the 222 label value + Check that MPLS entry with old label value is no more present + Check that MPLS entry for local traffic has inLabel set to 222 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + + # counting the number of labels used in the VPNv4 table + label_list = set() + logger.info("r1, vpnv4 table, check the number of labels used before modification") + bgp_vpnv4_table_check_all(router, label_list) + old_len = len(label_list) + assert ( + old_len != 1 + ), "r1, number of labels used should be greater than 1, oberved {} ".format(old_len) + + logger.info("r1, vrf1, changing the default MPLS label value to export to 222") + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nlabel vpn export 222\n", + isjson=False, + ) + + # Check r1 updated the MPLS entry with the 222 label value + logger.info( + "r1, mpls table, check that MPLS entry with inLabel set to 222 has vrf1 interface" + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_found, router, 222, "vrf1" + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry with label 222 not found" + + # check label repartition is ok + logger.info("r1, vpnv4 table, check the number of labels used after modification") + label_list = set() + bgp_vpnv4_table_check_all(router, label_list) + new_len = len(label_list) + assert ( + old_len == new_len + ), "r1, number of labels after modification differ from previous, observed {}, expected {} ".format( + new_len, old_len + ) + + logger.info( + "r1, vpnv4 table, check that prefixes that were using the vrf label have refreshed the label value to 222" + ) + bgp_vpnv4_table_check( + router, group=["192.168.255.0/24", "192.0.2.0/24"], label_value_expected=222 + ) + + +def test_unconfigure_allocation_mode_nexthop(): + """ + Test unconfiguring allocation mode per nexthop + Check that show mpls table has no entry with label 17 (previously used) + Check that all VPN updates on r1 should have label value moved to 222 + Check that show mpls table will only have 222 label value + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Unconfiguring allocation mode per nexthop") + router = tgen.gears["r1"] + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nno label vpn export allocation-mode per-nexthop\n", + isjson=False, + ) + + # Check r1 updated the MPLS entry with the 222 label value + logger.info( + "r1, mpls table, check that MPLS entry with inLabel set to 17 is not present" + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_not_found, router, 17 + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry with label 17 still present" + + # Check vpnv4 routes from r1 + logger.info("Checking vpnv4 routes on r1") + label_list = set() + bgp_vpnv4_table_check_all(router, label_list=label_list, same=True) + assert len(label_list) == 1, "r1, multiple Label values found for vpnv4 updates" + + new_label = label_list.pop() + assert ( + new_label == 222 + ), "r1, wrong label value in VPNv4 table, expected 222, observed {}".format( + new_label + ) + + # Check mpls table with 222 value + logger.info("Checking MPLS values on show mpls table of r1") + label_list = set() + label_list.add(222) + mpls_table_check(router, label_list=label_list) + + +def test_reconfigure_allocation_mode_nexthop(): + """ + Test re-configuring allocation mode per nexthop + Check that show mpls table has no entry with label 17 + Check that all VPN updates on r1 should have multiple label values and not only 222 + Check that show mpls table will have multiple label values and not only 222 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Reconfiguring allocation mode per nexthop") + router = tgen.gears["r1"] + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nlabel vpn export allocation-mode per-nexthop\n", + isjson=False, + ) + + # Check that show mpls table has no entry with label 17 + logger.info( + "r1, mpls table, check that MPLS entry with inLabel set to 17 is present" + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_not_found, router, 17 + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry with label 17 still present" + + # Check vpnv4 routes from r1 + logger.info("Checking vpnv4 routes on r1") + label_list = set() + bgp_vpnv4_table_check_all(router, label_list=label_list) + assert len(label_list) != 1, "r1, only 1 label values found for vpnv4 updates" + + # Check mpls table with all values + logger.info("Checking MPLS values on show mpls table of r1") + mpls_table_check(router, label_list=label_list) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/__init__.py b/tests/topotests/bgp_vpnv6_per_nexthop_label/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json new file mode 100644 index 0000000..39ba7dd --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json @@ -0,0 +1,186 @@ +{ + "vrfName": "vrf1", + "localAS": 65500, + "routes": + { + "10:200::/64": [ + { + "valid": true, + "bestpath": true, + "prefix": "10:200::", + "prefixLen": 64, + "network": "10:200::/64", + "nexthops": [ + { + "ip": "192:168::2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "172:31::11/128": [ + { + "valid":true, + "bestpath":true, + "prefix":"172:31::11", + "prefixLen":128, + "network":"172:31::11/128", + "peerId":"192:2::100", + "nexthops":[ + { + "ip":"192:2::11", + "afi":"ipv6", + "scope":"global" + } + ] + } + ], + "172:31::12/128": [ + { + "valid":true, + "bestpath":true, + "prefix":"172:31::12", + "prefixLen":128, + "network":"172:31::12/128", + "peerId":"192:2::100", + "nexthops":[ + { + "ip":"192:2::12", + "afi":"ipv6", + "scope":"global", + "used":true + }, + { + "scope": "link-local" + } + ] + } + ], + "172:31::13/128": [ + { + "valid":true, + "bestpath":true, + "prefix":"172:31::13", + "prefixLen":128, + "network":"172:31::13/128", + "peerId":"192:168::255:13", + "nexthops":[ + { + "ip":"192:168::255:13", + "afi":"ipv6", + "scope": "global", + "used":true + }, + { + "scope": "link-local" + } + ] + } + ], + "172:31::14/128": [ + { + "valid":true, + "bestpath":true, + "prefix":"172:31::14", + "prefixLen":128, + "network":"172:31::14/128", + "peerId":"(unspec)", + "nexthops":[ + { + "ip":"192:2::14", + "afi":"ipv6", + "used":true + } + ] + } + ], + "172:31::15/128": [ + { + "valid":true, + "bestpath":true, + "prefix":"172:31::15", + "prefixLen":128, + "network":"172:31::15/128", + "peerId":"(unspec)", + "nexthops":[ + { + "ip":"192:2::12", + "afi":"ipv6", + "used":true + } + ] + } + ], + "172:31::20/128": [ + { + "valid":true, + "bestpath":true, + "prefix":"172:31::20", + "prefixLen":128, + "network":"172:31::20/128", + "peerId":"192:2::100", + "nexthops":[ + { + "ip":"192:2::11", + "afi":"ipv6", + "scope":"global", + "used":true + } + ] + } + ], + "172:31::111/128": [ + { + "valid":true, + "bestpath":true, + "prefix":"172:31::111", + "prefixLen":128, + "network":"172:31::111/128", + "peerId":"192:2::100", + "nexthops":[ + { + "ip":"192:2::11", + "afi":"ipv6", + "scope":"global", + "used":true + } + ] + } + ], + "192:2::/64": [ + { + "valid":true, + "bestpath":true, + "prefix":"192:2::", + "prefixLen":64, + "network":"192:2::/64", + "peerId":"(unspec)", + "nexthops":[ + { + "ip":"::", + "afi":"ipv6", + "used":true + } + ] + } + ], + "192:168::255:0/112": [ + { + "valid":true, + "bestpath":true, + "prefix":"192:168::255:0", + "prefixLen":112, + "network":"192:168::255:0/112", + "peerId":"(unspec)", + "nexthops":[ + { + "ip":"::", + "afi":"ipv6", + "used":true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf new file mode 100644 index 0000000..6bb0a88 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf @@ -0,0 +1,45 @@ +debug bgp vpn leak-from-vrf +debug bgp vpn label +debug bgp nht +debug bgp updates out +router bgp 65500 + bgp router-id 192.168.0.1 + no bgp ebgp-requires-policy + neighbor 192:168::2 remote-as 65501 + address-family ipv4 unicast + no neighbor 192:168::2 activate + exit-address-family + address-family ipv6 vpn + neighbor 192:168::2 activate + neighbor 192:168::2 soft-reconfiguration inbound + exit-address-family +! +router bgp 65500 vrf vrf1 + bgp router-id 192.168.0.1 + neighbor 192:2::100 remote-as 65500 + neighbor 192:168::255:13 remote-as 65500 + address-family ipv6 unicast + neighbor 192:2::100 activate + neighbor 192:2::100 route-map rmap in + neighbor 192:168::255:13 activate + neighbor 192:168::255:13 route-map rmap in + redistribute connected + redistribute static route-map rmap + label vpn export allocation-mode per-nexthop + label vpn export auto + rd vpn export 444:1 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +interface r1-eth0 + mpls bgp forwarding +! +bgp community-list 1 seq 5 permit 10:10 +! +route-map rmap permit 1 + set ipv6 next-hop prefer-global +! +route-map rmap permit 2 +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/zebra.conf new file mode 100644 index 0000000..bdad9ee --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/zebra.conf @@ -0,0 +1,18 @@ +log stdout +debug zebra nht +!debug zebra kernel msgdump recv +!debug zebra dplane detailed +!debug zebra packet recv +interface r1-eth1 vrf vrf1 + ipv6 address 192:2::1/64 +! +interface r1-eth2 vrf vrf1 + ipv6 address 192:168::255:1/112 +! +interface r1-eth0 + ip address 192:168::1/112 +! +vrf vrf1 + ipv6 route 172:31::14/128 192:2::14 + ipv6 route 172:31::15/128 192:2::12 +exit-vrf diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf new file mode 100644 index 0000000..cb653d6 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 65500 + bgp router-id 11.11.11.11 + no bgp network import-check + neighbor 192:2::100 remote-as 65500 + address-family ipv4 unicast + no neighbor 192:2::100 activate + ! + address-family ipv6 unicast + neighbor 192:2::100 activate + network 172:31::11/128 + network 172:31::111/128 + network 172:31::20/128 + exit-address-family +! + diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/zebra.conf new file mode 100644 index 0000000..a76080d --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface r11-eth0 + ipv6 address 192:2::11/64 +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/bgpd.conf new file mode 100644 index 0000000..d41fb18 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 65500 + bgp router-id 12.12.12.12 + no bgp network import-check + neighbor 192:2::100 remote-as 65500 + address-family ipv4 unicast + no neighbor 192:2::100 activate + ! + address-family ipv6 unicast + neighbor 192:2::100 activate + network 172:31::12/128 + exit-address-family +! + diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/zebra.conf new file mode 100644 index 0000000..df9cae4 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface r12-eth0 + ipv6 address 192:2::12/64 +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf new file mode 100644 index 0000000..0437882 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65500 + bgp router-id 13.13.13.13 + no bgp network import-check + neighbor 192:168::255:1 remote-as 65500 + address-family ipv4 unicast + no neighbor 192:168::255:1 activate + exit-address-family + address-family ipv6 unicast + neighbor 192:168::255:1 activate + network 172:31::0:13/128 + exit-address-family +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/zebra.conf new file mode 100644 index 0000000..dfe5994 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface r13-eth0 + ipv6 address 192:168::255:13/112 +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgp_vpnv6_routes.json b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgp_vpnv6_routes.json new file mode 100644 index 0000000..bb7d5c0 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgp_vpnv6_routes.json @@ -0,0 +1,187 @@ +{ + "vrfName": "default", + "localAS": 65501, + "routes": + { + "routeDistinguishers": + { + "444:1": + { + "172:31::11/128": [ + { + "valid": true, + "bestpath": true, + "prefix": "172:31::11", + "prefixLen": 128, + "network": "172:31::11/128", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "172:31::12/128": [ + { + "valid": true, + "bestpath": true, + "prefix": "172:31::12", + "prefixLen": 128, + "network": "172:31::12/128", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "172:31::13/128": [ + { + "valid": true, + "bestpath": true, + "prefix": "172:31::13", + "prefixLen": 128, + "network": "172:31::13/128", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "172:31::14/128": [ + { + "valid": true, + "bestpath": true, + "prefix": "172:31::14", + "prefixLen": 128, + "network": "172:31::14/128", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "172:31::15/128": [ + { + "valid": true, + "bestpath": true, + "prefix": "172:31::15", + "prefixLen": 128, + "network": "172:31::15/128", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "172:31::20/128": [ + { + "valid": true, + "bestpath": true, + "prefix": "172:31::20", + "prefixLen": 128, + "network": "172:31::20/128", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "172:31::111/128": [ + { + "valid": true, + "bestpath": true, + "prefix": "172:31::111", + "prefixLen": 128, + "network": "172:31::111/128", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192:2::/64": [ + { + "valid": true, + "bestpath": true, + "prefix": "192:2::", + "prefixLen": 64, + "network": "192:2::/64", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192:168::255:0/112": [ + { + "valid": true, + "bestpath": true, + "prefix": "192:168::255:0", + "prefixLen": 112, + "network": "192:168::255:0/112", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "444:2": + { + "10:200::/64": [ + { + "valid": true, + "bestpath": true, + "prefix": "10:200::", + "prefixLen": 64, + "network": "10:200::/64", + "peerId": "(unspec)", + "nhVrfName": "vrf1", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgpd.conf new file mode 100644 index 0000000..30e9959 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgpd.conf @@ -0,0 +1,25 @@ +router bgp 65501 + bgp router-id 192.168.0.2 + no bgp ebgp-requires-policy + neighbor 192:168::1 remote-as 65500 + address-family ipv4 unicast + no neighbor 192:168::1 activate + exit-address-family + address-family ipv6 vpn + neighbor 192:168::1 activate + exit-address-family +! +router bgp 65501 vrf vrf1 + bgp router-id 192.168.0.2 + address-family ipv6 unicast + redistribute connected + label vpn export 102 + rd vpn export 444:2 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +interface r2-eth0 + mpls bgp forwarding +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/zebra.conf new file mode 100644 index 0000000..47cee95 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/zebra.conf @@ -0,0 +1,7 @@ +log stdout +interface r2-eth1 vrf vrf1 + ipv6 address 10:200::2/64 +! +interface r2-eth0 + ipv6 address 192:168::2/112 +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/bgpd.conf new file mode 100644 index 0000000..8c7664b --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/bgpd.conf @@ -0,0 +1,24 @@ +router bgp 65500 + bgp router-id 100.100.100.100 + no bgp network import-check + neighbor 192:2::1 remote-as 65500 + neighbor 192:2::11 remote-as 65500 + neighbor 192:2::12 remote-as 65500 + address-family ipv4 unicast + no neighbor 192:2::1 activate + no neighbor 192:2::11 activate + no neighbor 192:2::12 activate + ! + address-family ipv6 unicast + neighbor 192:2::1 activate + neighbor 192:2::1 route-reflector-client + neighbor 192:2::1 nexthop-local unchanged + neighbor 192:2::11 activate + neighbor 192:2::11 route-reflector-client + neighbor 192:2::11 nexthop-local unchanged + neighbor 192:2::12 activate + neighbor 192:2::12 route-reflector-client + neighbor 192:2::12 nexthop-local unchanged + exit-address-family +! + diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/zebra.conf new file mode 100644 index 0000000..94b82dc --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface rr-eth0 + ipv6 address 192:2::100/64 +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py new file mode 100644 index 0000000..e936ccc --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py @@ -0,0 +1,808 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# test_bgp_vpnv6_per_nexthop_label.py +# +# Copyright 2023 6WIND S.A. +# + +""" + test_bgp_vpnv6_per_nexthop_label.py: Test the FRR BGP daemon using EBGP peering + Let us exchange VPNv6 updates between both devices + Updates from r1 will originate from the same RD, but will have separate + label values. + + +----------+ + | r11 | + |192::2:11 +---+ + | | | +----+--------+ +----------+ + +----------+ | 192::2::1 |vrf | r1 |192:168::/112 | r2 | + +-------------------+ | 1+--------------+ | + +----------+ | |VRF1|AS65500 | | AS65501 | + | r12 | | +--------------+ | VPNV4| |VPNV4 | + |192::2:12 +---+ |192:168::255:1+-+--+--------+ +----------+ + | | | + +----------+ | + | + +----------+ | + | r13 | | + |192:168:: +--------+ + | 255:13 | + +----------+ +""" + +import os +import sys +import json +from functools import partial +import pytest +import functools + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + + +pytestmark = [pytest.mark.bgpd] + +PREFIXES_R11 = ["172:31::11/128", "172:31::20/128", "172:31::111/128"] +PREFIXES_R12 = ["172:31::12/128", "172:31::15/128"] +PREFIXES_REDIST_R14 = ["172:31::14/128"] +PREFIXES_CONNECTED = ["192:168::255/112", "192:2::/64"] + + +def build_topo(tgen): + "Build function" + + # Create 2 routers. + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r11") + tgen.add_router("r12") + tgen.add_router("r13") + tgen.add_router("r14") + tgen.add_router("rr") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r11"]) + switch.add_link(tgen.gears["r12"]) + switch.add_link(tgen.gears["rr"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r13"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r14"]) + + +def _populate_iface(): + tgen = get_topogen() + cmds_list = [ + "ip link add vrf1 type vrf table 10", + "echo 100000 > /proc/sys/net/mpls/platform_labels", + "ip link set dev vrf1 up", + "ip link set dev {0}-eth1 master vrf1", + "echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input", + ] + cmds_list_plus = [ + "ip link set dev {0}-eth2 master vrf1", + ] + + for cmd in cmds_list: + input = cmd.format("r1") + logger.info("input: " + cmd) + output = tgen.net["r1"].cmd(cmd.format("r1")) + logger.info("output: " + output) + + for cmd in cmds_list_plus: + input = cmd.format("r1") + logger.info("input: " + cmd) + output = tgen.net["r1"].cmd(cmd.format("r1")) + logger.info("output: " + output) + + for cmd in cmds_list: + input = cmd.format("r2") + logger.info("input: " + cmd) + output = tgen.net["r2"].cmd(cmd.format("r2")) + logger.info("output: " + output) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + _populate_iface() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def bgp_vpnv6_table_check(router, group, label_list=None, label_value_expected=None): + """ + Dump and check that vpnv6 entries have the same MPLS label value + * 'router': the router to check + * 'group': the list of prefixes to check. a single label value for the group has to be found + * 'label_list': check that the label values are not present in the vpnv6 entries + * that list is updated with the present label value + * 'label_value_expected': check that the mpls label read is the same as that value + """ + + stored_label_inited = False + for prefix in group: + dump = router.vtysh_cmd("show bgp ipv6 vpn {} json".format(prefix), isjson=True) + for rd, pathes in dump.items(): + for path in pathes["paths"]: + assert ( + "remoteLabel" in path.keys() + ), "{0}, {1}, remoteLabel not present".format(router.name, prefix) + logger.info( + "{0}, {1}, label value is {2}".format( + router.name, prefix, path["remoteLabel"] + ) + ) + if stored_label_inited: + assert ( + path["remoteLabel"] == stored_label + ), "{0}, {1}, label value not expected one (expected {2}, observed {3}".format( + router.name, prefix, stored_label, path["remoteLabel"] + ) + else: + stored_label = path["remoteLabel"] + stored_label_inited = True + if label_list is not None: + assert ( + stored_label not in label_list + ), "{0}, {1}, label already detected in a previous prefix".format( + router.name, prefix + ) + label_list.add(stored_label) + + if label_value_expected: + assert ( + path["remoteLabel"] == label_value_expected + ), "{0}, {1}, label value not expected (expected {2}, observed {3}".format( + router.name, prefix, label_value_expected, path["remoteLabel"] + ) + + +def bgp_vpnv6_table_check_all(router, label_list=None, same=False): + """ + Dump and check that vpnv6 entries are correctly configured with specific label values + * 'router': the router to check + * 'label_list': check that the label values are not present in the vpnv6 entries + * that list is updated with the present label value found. + * 'same': by default, set to False. Addresses groups are classified by addresses. + * if set to True, all entries of all groups should have a unique label value + """ + if same: + bgp_vpnv6_table_check( + router, + group=PREFIXES_R11 + + PREFIXES_R12 + + PREFIXES_REDIST_R14 + + PREFIXES_CONNECTED, + label_list=label_list, + ) + else: + for group in ( + PREFIXES_R11, + PREFIXES_R12, + PREFIXES_REDIST_R14, + PREFIXES_CONNECTED, + ): + bgp_vpnv6_table_check(router, group=group, label_list=label_list) + + +def check_show_mpls_table(router, blacklist=None, label_list=None, whitelist=None): + nexthop_list = [] + if blacklist: + nexthop_list.append(blacklist) + + dump = router.vtysh_cmd("show mpls table json", isjson=True) + for in_label, label_info in dump.items(): + if label_list is not None: + label_list.add(in_label) + for nh in label_info["nexthops"]: + if "installed" not in nh.keys(): + return "{} {} is not installed yet on {}".format(in_label, label_info, router.name) + if nh["installed"] != True or nh["type"] != "BGP": + return "{}, show mpls table, nexthop is not installed".format( + router.name + ) + if "nexthop" in nh.keys(): + if nh["nexthop"] in nexthop_list: + return "{}, show mpls table, duplicated or blacklisted nexthop address".format( + router.name + ) + nexthop_list.append(nh["nexthop"]) + elif "interface" in nh.keys(): + if nh["interface"] in nexthop_list: + return "{}, show mpls table, duplicated or blacklisted nexthop interface".format( + router.name + ) + nexthop_list.append(nh["interface"]) + else: + return "{}, show mpls table, entry with neither nexthop nor interface".format( + router.name + ) + + if whitelist: + for entry in whitelist: + if entry not in nexthop_list: + return "{}, show mpls table, entry with nexthop {} not present in nexthop list".format( + router.name, entry + ) + return None + + +def mpls_table_check(router, blacklist=None, label_list=None, whitelist=None): + """ + Dump and check 'show mpls table json' output. An assert is triggered in case test fails + * 'router': the router to check + * 'blacklist': the list of nexthops (IP or interface) that should not be on output + * 'label_list': the list of labels that should be in inLabel value + * 'whitelist': the list of nexthops (IP or interface) that should be on output + """ + logger.info("Checking MPLS labels on {}".format(router.name)) + logger.info("Checking MPLS labels on {}".format(router.name)) + # Check r2 removed 172.31.0.30 vpnv4 update + test_func = functools.partial( + check_show_mpls_table, router, blacklist, label_list, whitelist + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, MPLS labels check fail: {}".format(router.name, result) + + +def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None): + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + if label: + expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} + else: + expected = {rd: {"prefix": prefix}} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + +def check_show_bgp_vpn_prefix_found(router, ipversion, prefix, rd): + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + expected = {rd: {"prefix": prefix}} + return topotest.json_cmp(output, expected) + + +def check_show_mpls_table_entry_label_found(router, inlabel, interface): + output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) + expected = { + "inLabel": inlabel, + "installed": True, + "nexthops": [{"interface": interface}], + } + return topotest.json_cmp(output, expected) + + +def check_show_mpls_table_entry_label_not_found(router, inlabel): + output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) + expected = {"inlabel": inlabel, "installed": True} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + +def mpls_entry_get_interface(router, label): + """ + Assert that the label is in MPLS table + Assert an outgoing interface is programmed + return the outgoing interface + """ + outgoing_interface = None + + logger.info("Checking MPLS labels on {}".format(router.name)) + dump = router.vtysh_cmd("show mpls table {} json".format(label), isjson=True) + assert dump, "{}, show mpls table, inLabel {} not found".format(router.name, label) + + for nh in dump["nexthops"]: + assert ( + "interface" in nh.keys() + ), "{}, show mpls table, nexthop interface not present for MPLS entry {}".format( + router.name, label + ) + + outgoing_interface = nh["interface"] + + return outgoing_interface + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check BGP IPv6 routing tables on VRF1 of r1 + logger.info("Checking BGP IPv6 routes for convergence on r1 VRF1") + router = tgen.gears["r1"] + json_file = "{}/{}/bgp_ipv6_routes_vrf1.json".format(CWD, router.name) + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp vrf vrf1 ipv6 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + logger.info("Checking BGP VPNv6 routes for convergence on r2") + router = tgen.gears["r2"] + json_file = "{}/{}/bgp_vpnv6_routes.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp ipv6 vpn json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check BGP labels received on r2 + logger.info("Checking BGP VPNv6 labels on r2") + label_list = set() + bgp_vpnv6_table_check_all(tgen.gears["r2"], label_list) + + # Check MPLS labels received on r1 + mpls_table_check(tgen.gears["r1"], label_list) + + +def test_flapping_bgp_vrf_down(): + """ + Turn down a remote BGP session + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("Unpeering BGP on r11") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp 65500\nno neighbor 192:2::100\n", + isjson=False, + ) + + def _bgp_prefix_not_found(router, vrf, ipversion, prefix): + output = json.loads( + router.vtysh_cmd( + "show bgp vrf {} {} {} json".format(vrf, ipversion, prefix) + ) + ) + expected = {"prefix": prefix} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + # Check prefix from r11 is not present + test_func = functools.partial( + _bgp_prefix_not_found, tgen.gears["r1"], "vrf1", "ipv6", "172:31::11/128" + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert ( + success + ), "r1, prefix 172:31::11/128 from r11 did not disappear. r11 still connected to rr ?" + + # Check BGP updated received on r2 are not from r11 + logger.info("Checking BGP VPNv6 labels on r2") + for entry in PREFIXES_R11: + dump = tgen.gears["r2"].vtysh_cmd( + "show bgp ipv6 vpn {} json".format(entry), isjson=True + ) + for rd in dump: + assert False, "r2, {}, route distinguisher {} present".format(entry, rd) + + mpls_table_check(tgen.gears["r1"], blacklist=["192:2::11"]) + + +def test_flapping_bgp_vrf_up(): + """ + Turn up a remote BGP session + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("Peering BGP on r11") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp 65500\nneighbor 192:2::100 remote-as 65500\n", + isjson=False, + ) + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp 65500\naddress-family ipv6 unicast\nneighbor 192:2::100 activate\n", + isjson=False, + ) + + # Check r2 gets prefix 172:31::11/128 + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + tgen.gears["r2"], + "ipv6", + "172:31::11/128", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert ( + success + ), "r2, prefix 172:31::11/128 from r11 not present. r11 still disconnected from rr ?" + bgp_vpnv6_table_check_all(tgen.gears["r2"]) + + +def test_recursive_route(): + """ + Test static recursive route redistributed over BGP + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Enabling recursive static route") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nvrf vrf1\nipv6 route 172:31::30/128 172:31::20\n", + isjson=False, + ) + logger.info("Checking BGP VPNv6 labels on r2") + # that route should be sent along with label for 192.0.2.11 + + def _prefix30_not_found(router): + output = json.loads(router.vtysh_cmd("show bgp ipv6 vpn 172:31::30/128 json")) + expected = {"444:1": {"prefix": "172:31::30/128"}} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + def _prefix30_found(router): + output = json.loads(router.vtysh_cmd("show bgp ipv6 vpn 172:31::30/128 json")) + expected = {"444:1": {"prefix": "172:31::30/128"}} + return topotest.json_cmp(output, expected) + + # Check r2 received vpnv6 update with 172:31::30 + test_func = functools.partial(_prefix30_found, tgen.gears["r2"]) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, VPNv6 update 172:31::30 not found" + + # that route should be sent along with label for 192::2:11 + bgp_vpnv6_table_check( + tgen.gears["r2"], + group=PREFIXES_R11 + ["172:31::30/128"], + ) + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + logger.info("Dumping nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 nexthop detail", isjson=False) + + logger.info("Disabling recursive static route") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nvrf vrf1\nno ipv6 route 172:31::30/128 172:31::20\n", + isjson=False, + ) + + # Check r2 removed 172:31::30 vpnv6 update + test_func = functools.partial(_prefix30_not_found, tgen.gears["r2"]) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, VPNv6 update 172:31::30 still present" + + +def test_prefix_changes_interface(): + """ + Test BGP update for a given prefix learnt on different interface + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Enabling a 172:31::50/128 prefix for r11") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv6 unicast\nnetwork 172:31::50/128", + isjson=False, + ) + + # Check r2 received vpnv6 update with 172:31::50 + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + tgen.gears["r2"], + "ipv6", + "172:31::50/128", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, VPNv6 update 172:31::50 not found" + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + + label_list = set() + bgp_vpnv6_table_check( + tgen.gears["r2"], + group=PREFIXES_R11 + ["172:31::50/128"], + label_list=label_list, + ) + + assert ( + len(label_list) == 1 + ), "Multiple Label values found for updates from r11 found" + + oldlabel = label_list.pop() + logger.info("r1, getting the outgoing interface used by label {}".format(oldlabel)) + old_outgoing_interface = mpls_entry_get_interface(tgen.gears["r1"], oldlabel) + logger.info( + "r1, outgoing interface used by label {} is {}".format( + oldlabel, old_outgoing_interface + ) + ) + + logger.info("Moving the 172:31::50/128 prefix from r11 to r13") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv6 unicast\nno network 172:31::50/128", + isjson=False, + ) + tgen.gears["r13"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv6 unicast\nnetwork 172:31::50/128", + isjson=False, + ) + + # Check r2 removed 172:31::50 vpnv6 update with old label + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + tgen.gears["r2"], + "ipv6", + "172:31::50/128", + "444:1", + label=oldlabel, + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert ( + success + ), "r2, vpnv6 update 172:31::50 with old label {0} still present".format(oldlabel) + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + + # Check r2 received new 172:31::50 vpnv6 update + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + tgen.gears["r2"], + "ipv6", + "172:31::50/128", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, vpnv6 update 172:31::50 not found" + + label_list = set() + bgp_vpnv6_table_check( + tgen.gears["r2"], + group=["172:31::13/128", "172:31::50/128"], + label_list=label_list, + ) + assert ( + len(label_list) == 1 + ), "Multiple Label values found for updates from r13 found" + + newlabel = label_list.pop() + logger.info("r1, getting the outgoing interface used by label {}".format(newlabel)) + new_outgoing_interface = mpls_entry_get_interface(tgen.gears["r1"], newlabel) + logger.info( + "r1, outgoing interface used by label {} is {}".format( + newlabel, new_outgoing_interface + ) + ) + if old_outgoing_interface == new_outgoing_interface: + assert 0, "r1, outgoing interface did not change whereas BGP update moved" + + logger.info("Restoring state by removing the 172:31::50/128 prefix from r13") + tgen.gears["r13"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv6 unicast\nno network 172:31::50/128", + isjson=False, + ) + + +def test_changing_default_label_value(): + """ + Change the MPLS default value + Check that r1 VPNv6 entries have the 222 label value + Check that MPLS entry with old label value is no more present + Check that MPLS entry for local traffic has inLabel set to 222 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + + # counting the number of labels used in the VPNv6 table + label_list = set() + logger.info("r1, VPNv6 table, check the number of labels used before modification") + bgp_vpnv6_table_check_all(router, label_list) + old_len = len(label_list) + assert ( + old_len != 1 + ), "r1, number of labels used should be greater than 1, oberved {} ".format(old_len) + + logger.info("r1, vrf1, changing the default MPLS label value to export to 222") + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nlabel vpn export 222\n", + isjson=False, + ) + + # Check r1 updated the MPLS entry with the 222 label value + logger.info( + "r1, mpls table, check that MPLS entry with inLabel set to 222 has vrf1 interface" + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_found, router, 222, "vrf1" + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry with label 222 not found" + + # check label repartition is ok + logger.info("r1, VPNv6 table, check the number of labels used after modification") + label_list = set() + bgp_vpnv6_table_check_all(router, label_list) + new_len = len(label_list) + assert ( + old_len == new_len + ), "r1, number of labels after modification differ from previous, observed {}, expected {} ".format( + new_len, old_len + ) + + logger.info( + "r1, VPNv6 table, check that prefixes that were using the vrf label have refreshed the label value to 222" + ) + bgp_vpnv6_table_check(router, group=PREFIXES_CONNECTED, label_value_expected=222) + + +def test_unconfigure_allocation_mode_nexthop(): + """ + Test unconfiguring allocation mode per nexthop + Check on r2 that new MPLS label values have been propagated + Check that show mpls table has no entry with label 17 (previously used) + Check that all VPN updates on r1 should have label value moved to 222 + Check that show mpls table will only have 222 label value + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Unconfiguring allocation mode per nexthop") + router = tgen.gears["r1"] + dump = router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nno label vpn export allocation-mode per-nexthop\n", + isjson=False, + ) + + # Check r1 updated the MPLS entry with the 222 label value + logger.info( + "r1, mpls table, check that MPLS entry with inLabel set to 17 is not present" + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_not_found, router, 17 + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry with label 17 still present" + + # Check vpnv6 routes from r1 + logger.info("Checking VPNv6 routes on r1") + label_list = set() + bgp_vpnv6_table_check_all(router, label_list=label_list, same=True) + assert len(label_list) == 1, "r1, multiple Label values found for VPNv6 updates" + + new_label = label_list.pop() + assert ( + new_label == 222 + ), "r1, wrong label value in VPNv6 table, expected 222, observed {}".format( + new_label + ) + + # Check mpls table with 222 value + logger.info("Checking MPLS values on show mpls table of r1") + label_list = set() + label_list.add(222) + mpls_table_check(router, label_list=label_list) + + +def test_reconfigure_allocation_mode_nexthop(): + """ + Test re-configuring allocation mode per nexthop + Check that show mpls table has no entry with label 17 + Check that all VPN updates on r1 should have multiple label values and not only 222 + Check that show mpls table will have multiple label values and not only 222 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Reconfiguring allocation mode per nexthop") + router = tgen.gears["r1"] + dump = router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nlabel vpn export allocation-mode per-nexthop\n", + isjson=False, + ) + + # Check that show mpls table has no entry with label 17 + logger.info( + "r1, mpls table, check that MPLS entry with inLabel set to 17 is present" + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_not_found, router, 17 + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry with label 17 still present" + + # Check vpnv6 routes from r1 + logger.info("Checking VPNv6 routes on r1") + label_list = set() + bgp_vpnv6_table_check_all(router, label_list=label_list) + assert len(label_list) != 1, "r1, only 1 label values found for VPNv6 updates" + + # Check mpls table with all values + logger.info("Checking MPLS values on show mpls table of r1") + mpls_table_check(router, label_list=label_list) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json new file mode 100644 index 0000000..b1d7d09 --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json @@ -0,0 +1,563 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["11.11.11.1/32", "11.11.11.11/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["11:11::1/128", "11:11::11/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["10.10.10.10/32", "10.10.10.100/32"], + "next_hop":"Null0" + }, + { + "network": ["10:10::10/128", "10:10::100/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r2": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["22.22.22.2/32", "22.22.22.22/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["22:22::2/128", "22:22::22/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["20.20.20.20/32", "20.20.20.200/32"], + "next_hop":"Null0" + }, + { + "network": ["20:20::20/128", "20:20::200/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r3": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["30.30.30.3/32", "30.30.30.30/32"], + "next_hop":"Null0" + }, + { + "network": ["30:30::3/128", "30:30::30/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["40.40.40.4/32", "40.40.40.40/32"], + "next_hop":"Null0" + }, + { + "network": ["40:40::4/128", "40:40::40/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + } + } +} diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json new file mode 100644 index 0000000..b1d7d09 --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json @@ -0,0 +1,563 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["11.11.11.1/32", "11.11.11.11/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["11:11::1/128", "11:11::11/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["10.10.10.10/32", "10.10.10.100/32"], + "next_hop":"Null0" + }, + { + "network": ["10:10::10/128", "10:10::100/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r2": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["22.22.22.2/32", "22.22.22.22/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["22:22::2/128", "22:22::22/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["20.20.20.20/32", "20.20.20.200/32"], + "next_hop":"Null0" + }, + { + "network": ["20:20::20/128", "20:20::200/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r3": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["30.30.30.3/32", "30.30.30.30/32"], + "next_hop":"Null0" + }, + { + "network": ["30:30::3/128", "30:30::30/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["40.40.40.4/32", "40.40.40.40/32"], + "next_hop":"Null0" + }, + { + "network": ["40:40::4/128", "40:40::40/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + } + } +} diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py new file mode 100644 index 0000000..c34c3eb --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py @@ -0,0 +1,1903 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: + +1. Verify that dynamically imported routes are further advertised + to iBGP peers(peer in cluster). +2. Verify matching a prefix based on community attribute and + importing it by stripping off this value +3. Verify the route-map operation along with dynamic import command. +4. Verifying the JSON outputs for all supported commands +""" + +import os +import sys +import time +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + check_address_types, + write_test_footer, + step, + create_route_maps, + create_static_routes, + create_prefix_lists, + create_bgp_community_lists, + create_interface_in_kernel, + check_router_status, + verify_cli_json, + verify_fib_routes, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_community, + verify_bgp_rib, +) +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"} +NETWORK1_3 = {"ipv4": "10.10.10.10/32", "ipv6": "10:10::10/128"} +NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"} + +NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"} +NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"} +NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"} +NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"} + +NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"} +NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"} +NETWORK3_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"} +NETWORK3_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"} + +NETWORK4_1 = {"ipv4": "40.40.40.4/32", "ipv6": "40:40::4/128"} +NETWORK4_2 = {"ipv4": "40.40.40.40/32", "ipv6": "40:40::40/128"} +NETWORK4_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"} +NETWORK4_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"} + +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} +LOOPBACK_1 = { + "ipv4": "10.0.0.7/24", + "ipv6": "fd00:0:0:1::7/64", +} +LOOPBACK_2 = { + "ipv4": "10.0.0.16/24", + "ipv6": "fd00:0:0:3::5/64", +} +PREFERRED_NEXT_HOP = "global" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_vrf_dynamic_route_leak_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), "4.19") < 0: + error_msg = ( + "BGP vrf dynamic route leak tests will not run " + '(have kernel "{}", but it requires >= 4.19)'.format(platform.release()) + ) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Local APIs +# +##################################################### + + +def disable_route_map_to_prefer_global_next_hop(tgen, topo): + """ + This API is to remove prefer global route-map applied on neighbors + + Parameter: + ---------- + * `tgen` : Topogen object + * `topo` : Input JSON data + + Returns: + -------- + True/errormsg + + """ + + logger.info("Remove prefer-global rmap applied on neighbors") + input_dict = { + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + ] + }, + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + ] + }, + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + { + "local_as": "300", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + ] + }, + "r4": { + "bgp": [ + { + "local_as": "400", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + { + "local_as": "400", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase :Failed \n Error: {}".format(result) + + return True + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_dynamic_imported_routes_advertised_to_iBGP_peer_p0(request): + """ + TC5_FUNC_5: + 1.5.5. Verify that dynamically imported routes are further advertised + to iBGP peers(peer in cluster). + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step( + "Redistribute configured static routes into BGP process" " on R1 and R3/R4" + ) + + input_dict_1 = {} + DUT = ["r1", "r3", "r4"] + VRFS = ["default", "default", "default"] + AS_NUM = [100, 300, 400] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step("Verify that R1 receives BGP routes from R3 and R4 in " "vrf default.") + + input_routes_r3 = { + "r3": { + "static_routes": [ + { + "network": [ + NETWORK3_1[addr_type], + NETWORK3_2[addr_type], + NETWORK3_3[addr_type], + NETWORK3_4[addr_type], + ] + } + ] + } + } + + input_routes_r4 = { + "r4": { + "static_routes": [ + { + "network": [ + NETWORK4_1[addr_type], + NETWORK4_2[addr_type], + NETWORK4_3[addr_type], + NETWORK4_4[addr_type], + ] + } + ] + } + } + + DUT = ["r1", "r2"] + INPUT_DICT = [input_routes_r3, input_routes_r4] + + for dut, routes in zip(DUT, INPUT_DICT): + result = verify_bgp_rib(tgen, addr_type, dut, routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_fib_routes(tgen, addr_type, dut, routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step("Import from default vrf into vrf ISR on R1") + + input_dict_isr = {} + DUT = ["r1", "r2"] + VRFS = ["ISR", "ISR"] + AS_NUM = [100, 100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: {"unicast": {"import": {"vrf": "default"}}} + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step( + "Verify that default vrf's imported routes are installed " + "in RIB/FIB of vrf ISR on R1:" + ) + + input_routes_r3 = { + "r3": { + "static_routes": [ + { + "network": [ + NETWORK3_1[addr_type], + NETWORK3_2[addr_type], + NETWORK3_3[addr_type], + NETWORK3_4[addr_type], + ], + "vrf": "ISR", + } + ] + } + } + + input_routes_r4 = { + "r4": { + "static_routes": [ + { + "network": [ + NETWORK4_1[addr_type], + NETWORK4_2[addr_type], + NETWORK4_3[addr_type], + NETWORK4_4[addr_type], + ], + "vrf": "ISR", + } + ] + } + } + + INPUT_DICT_VRF = [input_routes_r3, input_routes_r4] + + for routes in INPUT_DICT_VRF: + result = verify_bgp_rib(tgen, addr_type, "r1", routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_fib_routes(tgen, addr_type, "r1", routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + intf_r2_r1 = topo["routers"]["r2"]["links"]["r1-link1"] + for addr_type in ADDR_TYPES: + + step( + "Create a loopback10 interface on R1 with below IP address and " + "associate with vrf ISR:" + ) + + create_interface_in_kernel( + tgen, + "r1", + "loopback2", + LOOPBACK_2[addr_type], + "ISR", + ) + + for addr_type in ADDR_TYPES: + + step( + "On router R1 Change the next-hop of static routes in vrf " + "ISR to LOOPBACK_2" + ) + + input_routes_r1 = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]], + "next_hop": "Null0", + "delete": True, + } + ] + } + } + + result = create_static_routes(tgen, input_routes_r1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + input_routes_r1 = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]], + "next_hop": (intf_r2_r1[addr_type]).split("/")[0], + } + ] + } + } + + result = create_static_routes(tgen, input_routes_r1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step( + "Verify that, though R1 originating BGP routes with next-hop" + " 24.1.1.2/24::1:2, which is local to R2(but in default vrf)" + ", R2 must receives and install all routes from R1 in vrf ISR." + ) + step( + "Verify on R2, that it now rejects 10.10.10.x routes originated " + "from R1. As next-hop IP is local to R2's vrf ISR." + ) + + input_routes_r1 = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]], + "vrf": "ISR", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step("On router R1 delete static routes in vrf ISR to LOOPBACK_1") + + input_routes_r1 = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]], + "next_hop": (intf_r2_r1[addr_type]).split("/")[0], + "delete": True, + } + ] + } + } + + result = create_static_routes(tgen, input_routes_r1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_dynamic_imported_matching_prefix_based_on_community_list_p0(request): + """ + TC7_FUNC_7: + 1.5.7. Verify matching a prefix based on community attribute and + importing it by stripping off this value + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step( + "Configure route-map to set community attribute for a specific" + "prefix on R1 in vrf ISR" + ) + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100", + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pflist_ABC_{}".format(addr_type) + } + }, + "set": {"community": {"num": "100:100"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step( + "Apply this route-map on R1 to vrf ISR while redistributing the" + " prefixes into BGP" + ) + + input_dict_1 = {} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": { + "route-map": "rmap_XYZ_{}".format(addr_type) + }, + } + ] + } + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step( + "Configure another route-map for filtering the prefixes based on" + " community attribute while importing into default vrf" + ) + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [ + { + "action": "permit", + "match": {"community_list": {"id": "COMM"}}, + "set": {"community": {"num": "none"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step( + "Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:" + ) + + input_dict_isr = {} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: {"unicast": {"import": {"vrf": "ISR"}}} + }, + } + ) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step( + "Verify on R1 that only prefixes with community value 100:100" + "in vrf ISR are imported to vrf default. While importing, the" + " community value has been stripped off:" + ) + + input_routes_r1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "default"} + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict_comm = {"community": "100:100"} + + result = verify_bgp_community( + tgen, + addr_type, + dut, + [NETWORK1_1[addr_type]], + input_dict_comm, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: Commnunity is not stipped off, {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step("Remove/re-add route-map XYZ from redistribution.") + + input_dict_1 = {} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": { + "route-map": "rmap_XYZ_{}".format(addr_type) + }, + "delete": True, + } + ] + } + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step( + "Verify that all the routes disappear from vrf default when " + "route-map is removed from redistribution, and appear again " + "when route-map is re-added to redistribution in vrf ISR." + ) + + input_routes_r1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "default"} + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error : Routes are still present \n {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + input_dict_1 = {} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": { + "route-map": "rmap_XYZ_{}".format(addr_type) + }, + } + ] + } + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + input_routes_r1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "default"} + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step("Remove/re-add route-map IMP form import statement.") + + input_dict_isr = {} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: {"unicast": {"import": {"vrf": "ISR"}}} + }, + } + ) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type), + "delete": True, + } + } + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step( + "Verify that when route-map IMP is removed all the prefixes of" + " vrf ISR are imported to vrf default. However when route-map " + "IMP is re-added only 11.11.11.1 and 11:11::1 (with community " + "value) are imported." + ) + + input_routes_r1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "default"} + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + input_dict_isr = {} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: {"unicast": {"import": {"vrf": "ISR"}}} + }, + } + ) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + input_routes_r1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "default"} + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step("Delete/Re-add prefix-list ABC.") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + "delete": True, + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_routes_r1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "default"} + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error : Routes are still present \n {}".format( + tc_name, result + ) + + input_dict_pf["r1"]["prefix_lists"][addr_type][ + "pflist_ABC_{}".format(addr_type) + ][0]["delete"] = False + + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Delete/Re-add community-list COMM.") + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100", + "delete": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error : Routes are still present \n {}".format( + tc_name, result + ) + + input_dict_cl["r1"]["bgp_community_lists"][0]["delete"] = False + + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Delete/Re-add route-map XYZ.") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pflist_ABC_{}".format(addr_type) + } + }, + "set": {"community": {"num": "100:100"}}, + "delete": True, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error : Routes are still present \n {}".format( + tc_name, result + ) + + input_dict_rm["r1"]["route_maps"]["rmap_XYZ_{}".format(addr_type)][0][ + "delete" + ] = False + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Delete/Re-add route-map IMP.") + + input_dict_rm2 = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [ + { + "action": "permit", + "match": {"community_list": {"id": "COMM"}}, + "set": {"community": {"num": "none"}}, + "delete": True, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_rm2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error : Routes are still present \n {}".format( + tc_name, result + ) + + input_dict_rm2["r1"]["route_maps"]["rmap_IMP_{}".format(addr_type)][0][ + "delete" + ] = False + + result = create_route_maps(tgen, input_dict_rm2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_routemap_operatons_with_dynamic_import_p0(request): + """ + TC8_FUNC_8: + 1.5.8. Verify the route-map operation along with dynamic import command. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step( + "Configure route-map to set community attribute for a specific" + "prefix on R1 in vrf ISR" + ) + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100", + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pflist_ABC_{}".format(addr_type) + } + }, + "set": {"community": {"num": "100:100"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step( + "Apply this route-map on R1 to vrf ISR while redistributing the" + " prefixes into BGP" + ) + + input_dict_1 = {} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": { + "route-map": "rmap_XYZ_{}".format(addr_type) + }, + } + ] + } + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step( + "Configure another route-map for filtering the prefixes based on" + " community attribute while importing into default vrf" + ) + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [ + { + "action": "permit", + "match": {"community_list": {"id": "COMM"}}, + "set": {"community": {"num": "500:500"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step( + "Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:" + ) + + input_dict_isr = {} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: {"unicast": {"import": {"vrf": "ISR"}}} + }, + } + ) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step( + "Verify on R1 that only prefixes with community value 100:100" + "in vrf ISR are imported to vrf default. While importing, the" + " community value has been stripped off:" + ) + + input_routes_r1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "default"} + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step("Applying route-map first followed by import VRF command.") + step( + "Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:" + ) + + input_dict_isr = {} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": {"import": {"vrf": "ISR", "delete": True}} + } + }, + } + ) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step( + "Verify that until 'import VRF command' is not configured, " + "routes are not imported. After configuring 'import VRF command'" + " repeat step-4 for verification" + ) + + input_routes_r1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "default"} + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error : Routes are still present \n {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + input_dict_isr = {} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: {"unicast": {"import": {"vrf": "ISR"}}} + }, + } + ) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + input_routes_r1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "default"} + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step("Delete/re-add import vrf ISR command multiple times in default" "vrf.") + + input_dict_isr = {} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": {"import": {"vrf": "ISR", "delete": True}} + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that when import vrf ISR command is deleted, " + "all routes of vrf ISR disappear from default vrf and " + "when it's re-configured, repeat step-4 for verification." + ) + + input_routes_r1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "default"} + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Routes are still present, Error {}".format( + tc_name, result + ) + + input_dict_isr["r1"]["bgp"][0]["address_family"][addr_type]["unicast"][ + "import" + ]["delete"] = False + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + + step( + "Delete and re-configure route-map IMP from global config when " + "import and route-maps are applied in a ISR vrf." + ) + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [ + { + "action": "permit", + "match": {"community_list": {"id": "COMM"}}, + "set": {"community": {"num": "500:500"}}, + "delete": True, + } + ] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_routes_r1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "default"} + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Routes are still present, Error {}".format( + tc_name, result + ) + + input_dict_rm["r1"]["route_maps"]["rmap_IMP_{}".format(addr_type)][0][ + "delete" + ] = False + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_comm = {"community": "500:500"} + + result = verify_bgp_community( + tgen, addr_type, dut, [NETWORK1_1[addr_type]], input_dict_comm + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_cli_json_p1(request): + """ + TC8_FUNC_9: + 1.5.9. Verifying the JSON outputs for all supported commands: + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + input_dict = { + "r1": { + "cli": [ + "show bgp vrf default ipv4 summary", + "show bgp vrf all ipv6 summary", + "show bgp neighbors", + ] + } + } + + result = verify_cli_json(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py new file mode 100644 index 0000000..32643c2 --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py @@ -0,0 +1,890 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: + +1. Verify that Changing route-map configurations(match/set clauses) on + the fly it takes immediate effect. +2. Verify BGP best path selection algorithm works fine when + routes are imported from ISR to default vrf and vice versa. +""" + +import os +import sys +import json +import time +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + check_address_types, + write_test_footer, + step, + create_route_maps, + create_prefix_lists, + create_bgp_community_lists, + check_router_status, + get_frr_ipv6_linklocal, + shutdown_bringup_interface, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_community, + verify_bgp_attributes, + verify_best_path_as_per_bgp_attribute, + verify_bgp_rib, +) +from lib.topojson import build_topo_from_json, build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK3_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"} +NETWORK3_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"} + +PREFERRED_NEXT_HOP = "global" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_vrf_dynamic_route_leak_topo2.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), "4.19") < 0: + error_msg = ( + "BGP vrf dynamic route leak tests will not run " + '(have kernel "{}", but it requires >= 4.19)'.format(platform.release()) + ) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_bgp_best_path_with_dynamic_import_p0(request): + """ + TC6_FUNC_6: + 1.5.6. Verify BGP best path selection algorithm works fine when + routes are imported from ISR to default vrf and vice versa. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + step( + "Redistribute configured static routes into BGP process" " on R1/R2 and R3" + ) + + input_dict_1 = {} + DUT = ["r1", "r2", "r3", "r4"] + VRFS = ["ISR", "ISR", "default", "default"] + AS_NUM = [100, 100, 300, 400] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step("Import from default vrf into vrf ISR on R1 and R2 as below") + + input_dict_vrf = {} + DUT = ["r1", "r2"] + VRFS = ["ISR", "ISR"] + AS_NUM = [100, 100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_vrf.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: {"unicast": {"import": {"vrf": "default"}}} + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_vrf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_default = {} + DUT = ["r1", "r2"] + VRFS = ["default", "default"] + AS_NUM = [100, 100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_default.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: {"unicast": {"import": {"vrf": "ISR"}}} + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_default) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify ECMP/Next-hop/Imported routes Vs Locally originated " + "routes/eBGP routes vs iBGP routes --already covered in almost" + " all tests" + ) + + for addr_type in ADDR_TYPES: + step("Verify Pre-emption") + + input_routes_r3 = { + "r3": {"static_routes": [{"network": [NETWORK3_3[addr_type]]}]} + } + + intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"] + + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1) + nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1) + else: + nh_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[ + 0 + ] + nh_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"][addr_type].split("/")[ + 0 + ] + + result = verify_bgp_rib( + tgen, addr_type, "r1", input_routes_r3, next_hop=[nh_r4_r1] + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Shutdown interface connected to r1 from r4:") + shutdown_bringup_interface(tgen, "r4", intf_r4_r1, False) + + for addr_type in ADDR_TYPES: + input_routes_r3 = { + "r3": {"static_routes": [{"network": [NETWORK3_3[addr_type]]}]} + } + + intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"] + + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1) + nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1) + else: + nh_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[ + 0 + ] + nh_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"][addr_type].split("/")[ + 0 + ] + + step("Verify next-hop is changed") + result = verify_bgp_rib( + tgen, addr_type, "r1", input_routes_r3, next_hop=[nh_r3_r1] + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Bringup interface connected to r1 from r4:") + shutdown_bringup_interface(tgen, "r4", intf_r4_r1, True) + + for addr_type in ADDR_TYPES: + input_routes_r3 = { + "r3": {"static_routes": [{"network": [NETWORK3_3[addr_type]]}]} + } + + intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"] + + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1) + nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1) + else: + nh_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[ + 0 + ] + nh_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"][addr_type].split("/")[ + 0 + ] + + step("Verify next-hop is not chnaged aftr shutdown:") + result = verify_bgp_rib( + tgen, addr_type, "r1", input_routes_r3, next_hop=[nh_r3_r1] + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Active-Standby scenario(as-path prepend and Local pref)") + + for addr_type in ADDR_TYPES: + step("Create prefix-list") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pf_ls_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK3_4[addr_type], + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step("Create route-map to match prefix-list and set localpref 500") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 10, + "match": { + addr_type: { + "prefix_lists": "pf_ls_{}".format(addr_type) + } + }, + "set": {"locPrf": 500}, + } + ] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Create route-map to match prefix-list and set localpref 600") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH2_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 20, + "match": { + addr_type: { + "prefix_lists": "pf_ls_{}".format(addr_type) + } + }, + "set": {"locPrf": 600}, + } + ] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_rma = { + "r1": { + "bgp": [ + { + "local_as": "100", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap_PATH1_{}".format( + addr_type + ), + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap_PATH2_{}".format( + addr_type + ), + "direction": "in", + } + ] + } + } + }, + } + } + } + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rma) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r1" + attribute = "locPrf" + + for addr_type in ADDR_TYPES: + step("Verify bestpath is installed as per highest localpref") + + input_routes_r3 = { + "r3": {"static_routes": [{"network": [NETWORK3_4[addr_type]]}]} + } + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_routes_r3, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step("Create route-map to match prefix-list and set localpref 700") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 10, + "match": { + addr_type: { + "prefix_lists": "pf_ls_{}".format(addr_type) + } + }, + "set": {"locPrf": 700}, + } + ] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step("Verify bestpath is changed as per highest localpref") + + input_routes_r3 = { + "r3": {"static_routes": [{"network": [NETWORK3_4[addr_type]]}]} + } + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_routes_r3, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step("Create route-map to match prefix-list and set as-path prepend") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH2_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 20, + "match": { + addr_type: { + "prefix_lists": "pf_ls_{}".format(addr_type) + } + }, + "set": { + "localpref": 700, + "path": {"as_num": "111", "as_action": "prepend"}, + }, + } + ] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + attribute = "path" + + for addr_type in ADDR_TYPES: + step("Verify bestpath is changed as per shortest as-path") + + input_routes_r3 = { + "r3": {"static_routes": [{"network": [NETWORK3_4[addr_type]]}]} + } + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_routes_r3, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_modify_route_map_match_set_clauses_p1(request): + """ + TC13_CHAOS_4: + 1.5.13. Verify that Changing route-map configurations(match/set clauses) on + the fly it takes immediate effect. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + step( + "Configure route-map to set community attribute for a specific" + "prefix on R1 in vrf ISR" + ) + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100", + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pflist_ABC_{}".format(addr_type) + } + }, + "set": {"community": {"num": "100:100"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step( + "Apply this route-map on R1 to vrf ISR while redistributing the" + " prefixes into BGP" + ) + + input_dict_1 = {} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": { + "route-map": "rmap_XYZ_{}".format(addr_type) + }, + } + ] + } + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step( + "Configure another route-map for filtering the prefixes based on" + " community attribute while importing into default vrf" + ) + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 10, + "match": {"community_list": {"id": "COMM"}}, + "set": {"community": {"num": "none"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step( + "Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:" + ) + + input_dict_isr = {} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: {"unicast": {"import": {"vrf": "ISR"}}} + }, + } + ) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step( + "Verify on R1 that only prefixes with community value 100:100" + "in vrf ISR are imported to vrf default. While importing, the" + " community value has been stripped off:" + ) + + input_routes_r1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "default"} + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step("Add set clause in route-map IMP:") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 10, + "match": {"community_list": {"id": "COMM"}}, + "set": { + "large_community": {"num": "100:100:100"}, + "locPrf": 500, + "path": {"as_num": "100 100", "as_action": "prepend"}, + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step( + "Verify that as we continue adding different attributes " + "step-by-step in route-map IMP those attributes gets " + "attached to prefixes:" + ) + + input_routes_r1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "default"} + ] + } + } + + input_dict_comm = {"largeCommunity": "100:100:100"} + + result = verify_bgp_community( + tgen, addr_type, dut, [NETWORK1_1[addr_type]], input_dict_comm + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_rmap = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{"set": {"locPrf": 500}}] + } + } + } + + result = verify_bgp_attributes( + tgen, + addr_type, + "r1", + [NETWORK1_1[addr_type]], + rmap_name="rmap_IMP_{}".format(addr_type), + input_dict=input_rmap, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Change community-list to match a different value then " "100:100.") + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100", + "delete": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_routes_r1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "default"} + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error : Routes are still " "present {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak_topo3/bgp_vrf_dynamic_route_leak_topo3.json b/tests/topotests/bgp_vrf_dynamic_route_leak_topo3/bgp_vrf_dynamic_route_leak_topo3.json new file mode 100644 index 0000000..9c73baf --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo3/bgp_vrf_dynamic_route_leak_topo3.json @@ -0,0 +1,1088 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r3-link1": {"ipv4": "13.1.1.1/24", "ipv6": "13::1:1/120", "vrf": "RED"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r3-link3": {"ipv4": "13.1.1.1/24", "ipv6": "13::1:1/120", "vrf": "GREEN"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + {"name": "RED", "id": "1"}, + {"name": "BLUE", "id": "2"}, + {"name": "GREEN", "id": "3"} + ], + "bgp": + [ + { + "local_as": "1", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "1", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "1", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "1", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r2": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + {"name": "RED", "id": "1"}, + {"name": "BLUE", "id": "2"}, + {"name": "GREEN", "id": "3"} + ], + "bgp": + [ + { + "local_as": "2", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "2", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "2", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "2", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r3": { + "links": { + "r1-link1": {"ipv4": "13.1.1.2/24", "ipv6": "13::1:2/120", "vrf": "RED"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r1-link3": {"ipv4": "13.1.1.2/24", "ipv6": "13::1:2/120", "vrf": "GREEN"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r4-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r4-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r5-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r5-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r5-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r5-link4": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + {"name": "RED", "id": "1"}, + {"name": "BLUE", "id": "2"}, + {"name": "GREEN", "id": "3"} + ], + "bgp": + [ + { + "local_as": "3", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": {} + } + }, + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r5": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r5": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "3", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link2": {} + } + }, + "r2": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r5": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r2": { + "dest_link": { + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r5": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "3", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r5": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r2": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r5": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "3", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r5": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r2": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r5": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + {"name": "RED", "id": "1"}, + {"name": "BLUE", "id": "2"}, + {"name": "GREEN", "id": "3"} + ], + "bgp": + [ + { + "local_as": "4", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "4", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "4", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "4", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ] + }, + "r5": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + {"name": "RED", "id": "1"}, + {"name": "BLUE", "id": "2"}, + {"name": "GREEN", "id": "3"} + ], + "bgp": + [ + { + "local_as": "3", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "3", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "3", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link3": {} + } + } + } + } + } + } + }, + { + "local_as": "3", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ] + } + } +} diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak_topo3/test_bgp_vrf_dynamic_route_leak_topo3.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo3/test_bgp_vrf_dynamic_route_leak_topo3.py new file mode 100644 index 0000000..1787021 --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo3/test_bgp_vrf_dynamic_route_leak_topo3.py @@ -0,0 +1,1790 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: +1. Verify that with multiple tenant VRFs, dynamically imported routes are + further advertised to eBGP peers. +2. Verify the route-map operations along with dynamic import command +3. Verify that deleting static routes from originating VRF also deletes + routes from other VRFs and peers. +4. Verify that deleting and adding "import" command multiple times shows + consistent results. +""" + +import os +import sys +import time +import pytest +import platform +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + create_route_maps, + create_static_routes, + create_prefix_lists, + create_bgp_community_lists, + get_frr_ipv6_linklocal, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_community, + verify_bgp_rib, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"} +NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"} +NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"} +NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"} +NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"} + +NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"} +NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"} +NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"} +NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"} +NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"} +NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"} + +NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"} +NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"} + +PREFIX_LIST = { + "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"], + "ipv6": ["11:11::1", "22:22::2", "22:22::22"], +} +PREFERRED_NEXT_HOP = "global" +VRF_LIST = ["RED", "BLUE", "GREEN"] +COMM_VAL_1 = "100:100" +COMM_VAL_2 = "500:500" +COMM_VAL_3 = "600:600" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_vrf_dynamic_route_leak_topo3.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), "4.19") < 0: + error_msg = ( + "BGP vrf dynamic route leak tests will not run " + '(have kernel "{}", but it requires >= 4.19)'.format(platform.release()) + ) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_dynamic_import_routes_advertised_to_ebgp_peers_p0(request): + """ + Verify that with multiple tenant VRFs, dynamically imported routes are + further advertised to eBGP peers. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Configure static routes on R2 and R3 and redistribute in BGP for " + "BLUE and RED vrf instances" + ) + for dut, network in zip( + ["r2", "r3"], [[NETWORK1_1, NETWORK1_2], [NETWORK2_1, NETWORK2_2]] + ): + for vrf_name, network_vrf in zip(["RED", "BLUE"], network): + step("Configure static route for VRF : {} on {}".format(vrf_name, dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [network_vrf[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for dut, as_num in zip(["r2", "r3"], ["2", "3"]): + for vrf_name in ["RED", "BLUE"]: + step("Redistribute static route on BGP VRF : {}".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + ) + + redist_dict = { + dut: { + "bgp": [ + {"vrf": vrf_name, "local_as": as_num, "address_family": temp} + ] + } + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that R2 and R3 has installed redistributed routes in BLUE " + "and RED vrfs" + ) + for dut, network in zip( + ["r2", "r3"], [[NETWORK2_1, NETWORK2_2], [NETWORK1_1, NETWORK1_2]] + ): + for vrf_name, network_vrf in zip(["RED", "BLUE"], network): + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [network_vrf[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Import BLUE vrf's route in tenant vrf RED on R2 and then import " + "vrf RED's routes into BLUE vrf on R3" + ) + + for dut, as_num, vrf_name, vrf_import in zip( + ["r2", "r3"], ["2", "3"], ["RED", "BLUE"], ["BLUE", "RED"] + ): + step("Import vrf {} int vrf {}, on router {}".format(vrf_import, vrf_name, dut)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_import}}}}) + + import_dict = { + dut: { + "bgp": [{"vrf": vrf_name, "local_as": as_num, "address_family": temp}] + } + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that R2's vrf RED and R3's vrf BLUE has installed 4 set of " + "prefixes. Routes imported from BLUE vrf (originated R2's & received " + "from R3). Vrf RED's local routes (originated by R2's & received " + "from R3)" + ) + step( + "Verify that R2 and R3 has installed redistributed routes in BLUE " + "and RED vrfs" + ) + + for dut, vrf_name in zip(["r2", "r3"], ["RED", "BLUE"]): + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK1_2[addr_type], + NETWORK2_1[addr_type], + NETWORK2_2[addr_type], + ], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Additionally, R2 receives R3's BLUE vrf's prefixes and then import " + "into vrf RED. These imported routes are advertised back to " + "(originator)R3 but now in vrf RED, however R3 doesn't install these " + "in vrf RED. Denied due to own AS" + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK1_2[addr_type], + NETWORK2_1[addr_type], + NETWORK2_2[addr_type], + ], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, result, static_routes["r3"]["static_routes"][0]["network"] + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Remove import vrf BLUE from vrf RED's instance on R2.") + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"import": {"vrf": "BLUE", "delete": True}}}} + ) + + import_dict = { + "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + step( + "Verify on R3 that, there is no change in FIB of vrf BLUE and R2's " + "BLUE vrf originated routes are removed from vrf RED on R3." + ) + for vrf_name in ["RED", "BLUE"]: + for addr_type in ADDR_TYPES: + if vrf_name == "RED": + network_vrf = [NETWORK1_1[addr_type], NETWORK2_1[addr_type]] + elif vrf_name == "BLUE": + network_vrf = [ + NETWORK1_1[addr_type], + NETWORK1_2[addr_type], + NETWORK2_1[addr_type], + NETWORK2_2[addr_type], + ] + static_routes = { + "r3": { + "static_routes": [ + { + "network": network_vrf, + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Remove import vrf BLUE from vrf RED's instance on R2.") + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": "BLUE"}}}}) + + import_dict = { + "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "All the routes described in earlier step should be added, once " + "import command on R2 is re-added." + ) + for dut, vrf_name in zip(["r2", "r3"], ["RED", "BLUE"]): + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK1_2[addr_type], + NETWORK2_1[addr_type], + NETWORK2_2[addr_type], + ], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Remove import vrf RED from BLUE vrf on R3") + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"import": {"vrf": "RED", "delete": True}}}} + ) + + import_dict = { + "r3": {"bgp": [{"vrf": "BLUE", "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R2 that, there is no change in FIB of vrf RED and R3's " + "vrf RED's originated routes are removed from vrf BLUE on R2." + ) + for vrf_name in ["RED", "BLUE"]: + for addr_type in ADDR_TYPES: + if vrf_name == "BLUE": + network_vrf = [NETWORK1_2[addr_type], NETWORK2_2[addr_type]] + elif vrf_name == "RED": + network_vrf = [ + NETWORK1_1[addr_type], + NETWORK1_2[addr_type], + NETWORK2_1[addr_type], + NETWORK2_2[addr_type], + ] + static_routes = { + "r2": { + "static_routes": [ + { + "network": network_vrf, + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Add import vrf RED from BLUE vrf on R3") + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": "RED"}}}}) + + import_dict = { + "r3": {"bgp": [{"vrf": "BLUE", "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "All the routes described in earlier step should be added, once " + "import command on R2 is re-added." + ) + for dut, vrf_name in zip(["r2", "r3"], ["RED", "BLUE"]): + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK1_2[addr_type], + NETWORK2_1[addr_type], + NETWORK2_2[addr_type], + ], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_dynamic_imported_matching_prefix_based_on_community_list_p0(request): + """ + Verify the route-map operations along with dynamic import command + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Configure static routes on R3 for vrf RED and redistribute in BGP " "instance" + ) + for vrf_name, networks in zip( + ["RED", "BLUE"], [[NETWORK1_1, NETWORK1_2], [NETWORK2_1, NETWORK2_2]] + ): + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [networks[0][addr_type], networks[1][addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure route-map to set community attribute for a specific " "prefix on R3" + ) + for addr_type in ADDR_TYPES: + input_dict_pf = { + "r3": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_cl = { + "r3": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": COMM_VAL_1, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_rm = { + "r3": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pflist_ABC_{}".format(addr_type) + } + }, + "set": {"community": {"num": COMM_VAL_1}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Apply this route-map on R3 to set community under vrf RED/BLUE " + "while redistributing the prefixes into BGP" + ) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": { + "route-map": "rmap_XYZ_{}".format(addr_type) + }, + } + ] + } + } + } + ) + + for vrf_name in ["RED", "BLUE"]: + redist_dict = { + "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that specific prefixes matched in route-map have community " + "attribute value 100:100 tagged" + ) + input_dict_comm = {"community": COMM_VAL_1} + for addr_type in ADDR_TYPES: + result = verify_bgp_community( + tgen, addr_type, "r3", [NETWORK1_1[addr_type]], input_dict_comm, vrf="RED" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Configure a route-map for filtering the prefixes based on community " + "attribute while importing into default vrf" + ) + for addr_type in ADDR_TYPES: + input_dict_rm = { + "r3": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 10, + "match": {"community_list": {"id": "COMM"}}, + "set": {"community": {"num": COMM_VAL_2}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Apply the route-map while Importing vrf RED/BLUE's prefixes into " + "GREEN vrf on router R3" + ) + temp = {} + for vrf_name in ["RED", "BLUE"]: + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) + + inport_dict = { + "r3": {"bgp": [{"vrf": "GREEN", "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, inport_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "import": {"vrf": "route-map rmap_IMP_{}".format(addr_type)} + } + } + } + ) + + inport_dict = { + "r3": {"bgp": [{"vrf": "GREEN", "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, inport_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict_comm = {"community": COMM_VAL_2} + step( + "Verify on R3 that only prefixes with community value {} in vrf RED " + "are imported to vrf GREEN. While importing, the community value " + "has been changed to {}".format(COMM_VAL_1, COMM_VAL_2) + ) + + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "GREEN"}] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + static_routes = { + "r3": { + "static_routes": [ + { + "network": [ + NETWORK2_1[addr_type], + NETWORK2_2[addr_type], + NETWORK1_2[addr_type], + ], + "vrf": "GREEN", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, result, static_routes["r3"]["static_routes"][0]["network"] + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, result, static_routes["r3"]["static_routes"][0]["network"] + ) + + result = verify_bgp_community( + tgen, addr_type, "r3", [NETWORK1_1[addr_type]], input_dict_comm, vrf="GREEN" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value in zip(["Delete", "Add"], [True, False]): + step("{} import vrf RED/BLUE command one by one from vrf GREEN".format(action)) + temp = {} + for vrf_name in ["RED", "BLUE"]: + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": {"import": {"vrf": vrf_name, "delete": value}} + } + } + ) + + inport_dict = { + "r3": {"bgp": [{"vrf": "GREEN", "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, inport_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that when import vrf RED/BLUE is {} one by one, all " + "routes of respective vrf disappear from vrf GREEN without " + "affecting (BLUE/RED) routes".format(action.lower()) + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "GREEN"} + ] + } + } + + if value: + result = verify_bgp_rib( + tgen, addr_type, "r3", static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes["r3"]["static_routes"][0]["network"], + ) + + result = verify_rib( + tgen, addr_type, "r3", static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, + result, + static_routes["r3"]["static_routes"][0]["network"], + ) + else: + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value in zip(["Delete", "Re-add"], [True, False]): + step( + "{} route-map IMP from global config when import and route-maps " + "are applied in vrf GREEN".format(action) + ) + for addr_type in ADDR_TYPES: + input_dict_rm = { + "r3": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 10, + "match": {"community_list": {"id": "COMM"}}, + "set": {"community": {"num": COMM_VAL_2}}, + "delete": value, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that when import vrf RED/BLUE is {} one by one, all " + "routes of respective vrf disappear from vrf GREEN without " + "affecting (BLUE/RED) routes".format(action.lower()) + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "GREEN"} + ] + } + } + + if value: + result = verify_bgp_rib( + tgen, addr_type, "r3", static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes["r3"]["static_routes"][0]["network"], + ) + + result = verify_rib( + tgen, addr_type, "r3", static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, + result, + static_routes["r3"]["static_routes"][0]["network"], + ) + else: + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_dynamic_import_routes_delete_static_route_p1(request): + """ + Verify that deleting static routes from originating VRF also deletes + routes from other VRFs and peers. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Configure static routes on R3 for each tenant vrf and redistribute " + "in respective BGP instance" + ) + vrf_list = VRF_LIST + ["default"] + for vrf_name, network in zip( + vrf_list, [NETWORK1_1, NETWORK2_1, NETWORK3_1, NETWORK1_2] + ): + step("Configure static route for VRF : {}".format(vrf_name)) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static route on BGP VRF : {}".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} + ) + + redist_dict = { + "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for vrf_name, network in zip(vrf_list, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + step( + "Verify that R3 has installed redistributed routes in respective " + "vrfs: {}".format(vrf_name) + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Import routes among vrfs as mentioned below on router R3") + + for vrf_name, vrf_import in zip( + ["GREEN", "BLUE", "default"], ["RED", "GREEN", "BLUE"] + ): + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_import}}}}) + + import_dict = { + "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for vrf_name, vrf_import, installed, not_installed in zip( + ["BLUE", "default"], + ["GREEN", "BLUE"], + [NETWORK3_1, NETWORK2_1], + [NETWORK1_1, NETWORK3_1], + ): + step( + "Verify that only locally originated routes of vrf {} are " + "advertised to vrf {}".format(vrf_import, vrf_name) + ) + + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + {"network": [installed[addr_type]], "vrf": vrf_name} + ] + } + } + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Verify that non local originated routes {} of vrf {} are " + "not advertised to vrf {}".format( + not_installed[addr_type], vrf_import, vrf_name + ) + ) + + static_routes = { + "r3": { + "static_routes": [ + {"network": [not_installed[addr_type]], "vrf": vrf_name} + ] + } + } + result = verify_bgp_rib( + tgen, addr_type, "r2", static_routes, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in BGP table".format( + tc_name, result, static_routes["r2"]["static_routes"][0]["network"] + ) + ) + + result = verify_rib(tgen, addr_type, "r2", static_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, result, static_routes["r2"]["static_routes"][0]["network"] + ) + + step("Delete static routes from vrf RED") + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": "blackhole", + "vrf": "RED", + "delete": True, + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + step( + "Verify on R2 and R3, that only vrf RED and GREEN's RIB/FIB withdraw " + "deleted routes" + ) + for dut in ["r2", "r3"]: + step( + "Verify on {}, that only vrf RED and GREEN's RIB/FIB withdraw " + "deleted routes".format(dut) + ) + for vrf_name in ["RED", "GREEN"]: + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": vrf_name} + ] + } + } + result = verify_bgp_rib( + tgen, addr_type, "r2", static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes["r2"]["static_routes"][0]["network"], + ) + + result = verify_rib( + tgen, addr_type, "r2", static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + + step("Delete static routes from vrf BLUE") + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": "blackhole", + "vrf": "BLUE", + "delete": True, + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r2", "r3"]: + step( + "Verify on {}, that only default and BLUE vrf's RIB/FIB " + "withdraw deleted routes".format(dut) + ) + for vrf_name in ["BLUE", "default"]: + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + {"network": [NETWORK2_1[addr_type]], "vrf": vrf_name} + ] + } + } + result = verify_bgp_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + + result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + + step("Delete static routes from vrf default") + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_2[addr_type]], + "next_hop": "blackhole", + "delete": True, + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r2", "r3"]: + step( + "Verify on {}, that only default vrf RIB/FIB withdraw deleted " + "routes".format(dut) + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + {"network": [NETWORK1_2[addr_type]], "vrf": vrf_name} + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in BGP table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + ) + + result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Add back all the routes that were deleted") + for vrf_name, network in zip( + vrf_list, [NETWORK1_1, NETWORK2_1, NETWORK3_1, NETWORK1_2] + ): + step("Configure static route for VRF : {}".format(vrf_name)) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static route on BGP VRF : {}".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} + ) + + redist_dict = { + "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for vrf_name, network in zip(vrf_list, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + step( + "Verify that R3 has installed redistributed routes in respective " + "vrfs: {}".format(vrf_name) + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_dynamic_import_routes_add_delete_import_command_p1(request): + """ + Verify that deleting and adding "import" command multiple times shows + consistent results. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Configure static routes on R2 for vrf RED and redistribute in " + "respective BGP instance" + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static route on BGP VRF RED") + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} + ) + + redist_dict = { + "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify that R2 has installed redistributed routes in respective " "vrfs only") + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "RED"}] + } + } + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Import vrf RED's routes into vrf GREEN on R2") + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": "RED"}}}}) + + import_dict = { + "r2": {"bgp": [{"vrf": "GREEN", "local_as": 2, "address_family": temp}]} + } + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R2, that it installs imported routes from vrf RED to vrf " + "GREEN's RIB/FIB" + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"}] + } + } + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("On R3 import routes from vrfs GREEN to default") + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": "GREEN"}}}}) + + import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that R2's vrf RED routes are now imported into vrf default " + "of R3, next-hop pointing to vrf GREEN" + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]} + } + + next_hop_1 = topo["routers"]["r2"]["links"]["r3-link3"][addr_type].split("/")[0] + result = verify_bgp_rib( + tgen, addr_type, "r3", static_routes, next_hop=next_hop_1 + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes, next_hop=next_hop_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Delete import command from R3's default vrf instance for both " + "address-families 1 by 1 (ipv4/ipv6)" + ) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"import": {"vrf": "GREEN", "delete": True}}}} + ) + + import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that R2's vrf RED routes are now removed from vrf " + "default on R3, however vrf GREEN still retains those" + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, result, static_routes["r3"]["static_routes"][0]["network"] + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Delete import command from R2's vrf GREEN instance for both " + "address-families 1 by 1 (ipv4/ipv6)" + ) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"import": {"vrf": "RED", "delete": True}}}} + ) + + import_dict = { + "r2": {"bgp": [{"vrf": "GREEN", "local_as": 2, "address_family": temp}]} + } + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + step( + "Verify that R2's vrf RED routes are now removed from vrf GREEN " + "on R2 & R3 as well" + ) + for dut in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [ + {"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"} + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in BGP table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + ) + + result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + + step( + "Add import command from R3's default vrf instance for both " + "address-families 1 by 1 (ipv4/ipv6)" + ) + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": "GREEN"}}}}) + + import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify that there are no routes installed on R3's vrf default " "RIB/FIB.") + for addr_type in ADDR_TYPES: + static_routes = { + "r3": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, result, static_routes["r3"]["static_routes"][0]["network"] + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Add import command from R2's vrf GREEN instance for both " + "address-families 1 by 1 (ipv4/ipv6)." + ) + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": "RED"}}}}) + + import_dict = { + "r2": {"bgp": [{"vrf": "GREEN", "local_as": 2, "address_family": temp}]} + } + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that R2's vrf RED routes are now imported into vrf " + "default of R3, next-hop pointing to vrf GREEN" + ) + for dut in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [ + {"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"} + ] + } + } + if dut == "r3": + next_hop_1 = topo["routers"]["r2"]["links"]["r3-link3"][ + addr_type + ].split("/")[0] + result = verify_bgp_rib( + tgen, addr_type, dut, static_routes, next_hop=next_hop_1 + ) + else: + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + if dut == "r3": + result = verify_rib( + tgen, addr_type, dut, static_routes, next_hop=next_hop_1 + ) + else: + result = verify_rib(tgen, addr_type, dut, static_routes) + + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Delete import command from R3's default vrf instance for both " + "address-families 1 by 1 (ipv4/ipv6)." + ) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"import": {"vrf": "GREEN", "delete": True}}}} + ) + + import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that R2's vrf RED routes are now removed from vrf " + "default on R3, however vrf GREEN still retains those." + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"}] + } + } + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + static_routes = { + "r2": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, result, static_routes["r3"]["static_routes"][0]["network"] + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Delete redistribute static from R2 for vrf RED") + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + } + } + ) + + redist_dict = { + "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that R2's vrf RED routes are now removed from vrf GREEN " + "on R2 & R3 as well." + ) + for dut in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [ + {"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"} + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in BGP table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + ) + + result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + + step( + "Add import command from R3's default vrf instance for both " + "address-families 1 by 1 (ipv4/ipv6)." + ) + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": "GREEN"}}}}) + + import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify that there are no routes installed on R3's vrf default " "RIB/FIB") + for addr_type in ADDR_TYPES: + static_routes = { + "r3": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, result, static_routes["r3"]["static_routes"][0]["network"] + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Add redistribute static from R2 for vrf RED") + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} + ) + + redist_dict = { + "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that R2's vrf RED routes are now imported into vrf " + "default of R3, next-hop pointing to vrf GREEN" + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]} + } + next_hop_1 = topo["routers"]["r2"]["links"]["r3-link3"][addr_type].split("/")[0] + result = verify_bgp_rib( + tgen, addr_type, "r3", static_routes, next_hop=next_hop_1 + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes, next_hop=next_hop_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/bgp_vrf_dynamic_route_leak_topo4.json b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/bgp_vrf_dynamic_route_leak_topo4.json new file mode 100644 index 0000000..9c73baf --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/bgp_vrf_dynamic_route_leak_topo4.json @@ -0,0 +1,1088 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r3-link1": {"ipv4": "13.1.1.1/24", "ipv6": "13::1:1/120", "vrf": "RED"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r3-link3": {"ipv4": "13.1.1.1/24", "ipv6": "13::1:1/120", "vrf": "GREEN"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + {"name": "RED", "id": "1"}, + {"name": "BLUE", "id": "2"}, + {"name": "GREEN", "id": "3"} + ], + "bgp": + [ + { + "local_as": "1", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "1", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "1", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "1", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r2": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + {"name": "RED", "id": "1"}, + {"name": "BLUE", "id": "2"}, + {"name": "GREEN", "id": "3"} + ], + "bgp": + [ + { + "local_as": "2", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "2", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "2", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "2", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r3": { + "links": { + "r1-link1": {"ipv4": "13.1.1.2/24", "ipv6": "13::1:2/120", "vrf": "RED"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r1-link3": {"ipv4": "13.1.1.2/24", "ipv6": "13::1:2/120", "vrf": "GREEN"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r4-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r4-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r5-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r5-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r5-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r5-link4": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + {"name": "RED", "id": "1"}, + {"name": "BLUE", "id": "2"}, + {"name": "GREEN", "id": "3"} + ], + "bgp": + [ + { + "local_as": "3", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": {} + } + }, + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r5": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r5": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "3", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link2": {} + } + }, + "r2": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r5": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r2": { + "dest_link": { + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r5": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "3", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r5": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r2": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r5": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "3", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r5": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r2": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r5": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + {"name": "RED", "id": "1"}, + {"name": "BLUE", "id": "2"}, + {"name": "GREEN", "id": "3"} + ], + "bgp": + [ + { + "local_as": "4", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "4", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "4", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "4", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ] + }, + "r5": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + {"name": "RED", "id": "1"}, + {"name": "BLUE", "id": "2"}, + {"name": "GREEN", "id": "3"} + ], + "bgp": + [ + { + "local_as": "3", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "3", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "3", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link3": {} + } + } + } + } + } + } + }, + { + "local_as": "3", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ] + } + } +} diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-1.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-1.py new file mode 100644 index 0000000..c7fbc01 --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-1.py @@ -0,0 +1,394 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: +1. Verify recursive import among Tenant VRFs. +2. Verify that dynamic import works fine between two different Tenant VRFs. + When next-hop IPs are same across all VRFs. + When next-hop IPs are different across all VRFs. +3. Verify that with multiple tenant VRFs, dynamic import works fine between + Tenant VRFs to default VRF. + When next-hop IPs and prefixes are same across all VRFs. + When next-hop IPs and prefixes are different across all VRFs. +""" + +import os +import sys +import time +import pytest +import platform +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + create_route_maps, + create_static_routes, + create_prefix_lists, + create_bgp_community_lists, + get_frr_ipv6_linklocal, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_community, + verify_bgp_rib, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"} +NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"} +NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"} +NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"} +NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"} + +NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"} +NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"} +NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"} +NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"} +NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"} +NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"} + +NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"} +NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"} + +PREFIX_LIST = { + "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"], + "ipv6": ["11:11::1", "22:22::2", "22:22::22"], +} +PREFERRED_NEXT_HOP = "global" +VRF_LIST = ["RED", "BLUE", "GREEN"] +COMM_VAL_1 = "100:100" +COMM_VAL_2 = "500:500" +COMM_VAL_3 = "600:600" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_vrf_dynamic_route_leak_topo4.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), "4.19") < 0: + error_msg = ( + "BGP vrf dynamic route leak tests will not run " + '(have kernel "{}", but it requires >= 4.19)'.format(platform.release()) + ) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_dynamic_import_recursive_import_tenant_vrf_p1(request): + """ + Verify recursive import among Tenant VRFs. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Configure static routes on R2 for vrf RED and redistribute in " + "respective BGP instance" + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static route on BGP VRF RED") + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} + ) + + redist_dict = { + "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify that R2 has installed redistributed routes in vrf RED only") + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "RED"}] + } + } + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Import vrf RED's routes into vrf GREEN on R2") + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": "RED"}}}}) + + import_dict = { + "r2": {"bgp": [{"vrf": "GREEN", "local_as": 2, "address_family": temp}]} + } + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R2, that it installs imported routes from vrf RED to vrf " + "GREEN's RIB/FIB pointing next-hop to vrf RED" + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"}] + } + } + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("On R3 import routes from vrf GREEN to vrf default") + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": "GREEN"}}}}) + + import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R3, that it installs imported routes from vrf GREEN to " + "vrf default RIB/FIB pointing next-hop to vrf GREEN. " + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r2": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("On R4 import routes from vrf default to vrf BLUE") + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": "default"}}}}) + + import_dict = { + "r4": {"bgp": [{"vrf": "BLUE", "local_as": 4, "address_family": temp}]} + } + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R4, that it installs imported routes from vrf default to " + "vrf BLUE RIB/FIB pointing next-hop to vrf default." + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r4": { + "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "BLUE"}] + } + } + result = verify_bgp_rib(tgen, addr_type, "r4", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r4", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for dut, vrf_name, vrf_import, as_num in zip( + ["r2", "r4"], ["GREEN", "BLUE"], ["RED", "default"], [2, 4] + ): + + for action, value in zip(["Delete", "Re-add"], [True, False]): + step("{} the import command on {} router".format(action, dut)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": {"import": {"vrf": vrf_import, "delete": value}} + } + } + ) + + import_dict = { + dut: { + "bgp": [ + {"vrf": vrf_name, "local_as": as_num, "address_family": temp} + ] + } + } + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + static_routes = { + "r4": { + "static_routes": [ + {"network": [NETWORK2_1[addr_type]], "vrf": "BLUE"} + ] + } + } + if value: + result = verify_bgp_rib( + tgen, addr_type, "r4", static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes["r4"]["static_routes"][0]["network"], + ) + + result = verify_rib( + tgen, addr_type, "r4", static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, + result, + static_routes["r4"]["static_routes"][0]["network"], + ) + else: + result = verify_bgp_rib(tgen, addr_type, "r4", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r4", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-2.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-2.py new file mode 100644 index 0000000..02950eb --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-2.py @@ -0,0 +1,919 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: +1. Verify recursive import among Tenant VRFs. +2. Verify that dynamic import works fine between two different Tenant VRFs. + When next-hop IPs are same across all VRFs. + When next-hop IPs are different across all VRFs. +3. Verify that with multiple tenant VRFs, dynamic import works fine between + Tenant VRFs to default VRF. + When next-hop IPs and prefixes are same across all VRFs. + When next-hop IPs and prefixes are different across all VRFs. +""" + +import os +import sys +import time +import pytest +import platform +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + create_route_maps, + create_static_routes, + create_prefix_lists, + create_bgp_community_lists, + get_frr_ipv6_linklocal, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_community, + verify_bgp_rib, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"} +NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"} +NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"} +NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"} +NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"} + +NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"} +NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"} +NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"} +NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"} +NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"} +NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"} + +NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"} +NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"} + +PREFIX_LIST = { + "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"], + "ipv6": ["11:11::1", "22:22::2", "22:22::22"], +} +PREFERRED_NEXT_HOP = "global" +VRF_LIST = ["RED", "BLUE", "GREEN"] +COMM_VAL_1 = "100:100" +COMM_VAL_2 = "500:500" +COMM_VAL_3 = "600:600" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_vrf_dynamic_route_leak_topo4.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), "4.19") < 0: + error_msg = ( + "BGP vrf dynamic route leak tests will not run " + '(have kernel "{}", but it requires >= 4.19)'.format(platform.release()) + ) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_dynamic_import_routes_between_two_tenant_vrf_p0(request): + """ + Verify that dynamic import works fine between two different Tenant VRFs. + + When next-hop IPs are same across all VRFs. + When next-hop IPs are different across all VRFs. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Configure static routes on R3 for each vrf and redistribute in " + "respective BGP instance" + ) + + for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + step("Configure static route for VRF : {}".format(vrf_name)) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static route on BGP VRF : {}".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} + ) + + redist_dict = { + "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + step( + "Verify that R3 has installed redistributed routes in respective " + "vrfs: {}".format(vrf_name) + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Import from vrf GREEN+BLUE into vrf RED on R3") + + for vrf_name in ["BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) + + import_dict = { + "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify on R1, that it installs all the routes(local+imported) in " + "vrf RED's RIB/FIB and doesn't get confuse with next-hop attribute, " + "as all vrfs on R1 are using same IP address for next-hop" + ) + + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + + next_hop_1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[0] + result = verify_bgp_rib( + tgen, addr_type, "r1", static_routes, next_hop=next_hop_1 + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", static_routes, next_hop=next_hop_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Remove import vrf GREEN/BLUE/Both command from vrf RED's instance on" " R3") + for vrf_name in ["BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}} + ) + + import_dict = { + "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that R1,R2 & R3 withdraw imported routes from vrf RED's RIB") + for dut in ["r1", "r2", "r3"]: + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in BGP table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + ) + + result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in Route table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + ) + + step("Add import vrf GREEN/BLUE/Both command from vrf RED's instance on " "R3") + for vrf_name in ["BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) + + import_dict = { + "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r1", "r2", "r3"]: + step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value in zip(["Shut", "No shut"], [True, False]): + step( + "{} the neighborship between R1-R3 and R1-R2 for vrf GREEN, BLUE " + "and default".format(action) + ) + bgp_disable = {"r3": {"bgp": []}} + for vrf_name in ["GREEN", "BLUE", "default"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": {"r3-link1": {"shutdown": value}} + }, + "r2": { + "dest_link": {"r3-link1": {"shutdown": value}} + }, + } + } + } + } + ) + + bgp_disable["r3"]["bgp"].append( + {"vrf": vrf_name, "local_as": 3, "address_family": temp} + ) + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers") + for dut in ["r1", "r2", "r3"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value, status in zip( + ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"] + ): + step("{} the neighborship between R1-R3 and R1-R2 for vrf RED".format(action)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3-link1": {"shutdown": value}}}, + "r2": {"dest_link": {"r3-link1": {"shutdown": value}}}, + } + } + } + } + ) + + bgp_disable = { + "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} + } + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that R1 and R2 {} all the routes from RED vrf's RIB and" + " FIB".format(status) + ) + for dut in ["r1", "r2"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + + if value: + result = verify_bgp_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + + result = verify_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in Route table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + else: + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Remove import command from router R3 and configure the same on R2") + for vrf_name in ["BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}} + ) + + import_dict = { + "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that once import commands are removed from R3, all imported " + "routes are withdrawn from RIB/FIB of vrf RED on R1/R2/R3" + ) + + for dut in ["r1", "r2", "r3"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in BGP table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + ) + + result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + + step( + "Configure static routes on R2 for each vrf and redistribute in " + "respective BGP instance" + ) + for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + step("Configure static route for VRF : {}".format(vrf_name)) + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static route on BGP VRF : {}".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} + ) + + redist_dict = { + "r2": {"bgp": [{"vrf": vrf_name, "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove redistribute static route on BGP VRF : {} on r3".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + } + } + ) + + redist_dict = { + "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for vrf_name in ["BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) + + import_dict = { + "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify after import commands are re-configured on R2's vrf RED, all " + "those routes are installed again in vrf RED of R1,R2,R3" + ) + for dut in ["r1", "r2", "r3"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Remove/add import vrf GREEN/BLUE/both command from vrf RED's " "instance on R2" + ) + for vrf_name in ["BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}} + ) + + redist_dict = { + "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that R1,R2 & R3 withdraw imported routes from vrf RED's RIB") + for dut in ["r1", "r2", "r3"]: + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in BGP table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + ) + + result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + + step("Add import vrf GREEN/BLUE/Both command from vrf RED's instance on " "R2") + for vrf_name in ["BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) + + redist_dict = { + "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r1", "r2", "r3"]: + step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value in zip(["Shut", "No shut"], [True, False]): + step( + "{} the neighborship between R2-R3 for vrf GREEN, BLUE and default".format( + action + ) + ) + bgp_disable = {"r2": {"bgp": []}} + for vrf_name in ["GREEN", "BLUE", "default"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": {"r2-link1": {"shutdown": value}} + } + } + } + } + } + ) + + bgp_disable["r2"]["bgp"].append( + {"vrf": vrf_name, "local_as": 2, "address_family": temp} + ) + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers") + for dut in ["r1", "r2", "r3"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value, status in zip( + ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"] + ): + step("{} the neighborship between R2-R3 for vrf RED".format(action)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3-link1": {"shutdown": value}}} + } + } + } + } + ) + + bgp_disable = { + "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} + } + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that R1 and R2 {} all the routes from RED vrf's RIB and" + " FIB".format(status) + ) + for dut in ["r1", "r3"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + + if value: + result = verify_bgp_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + + result = verify_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + else: + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-3.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-3.py new file mode 100644 index 0000000..4b18903 --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-3.py @@ -0,0 +1,919 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: +1. Verify recursive import among Tenant VRFs. +2. Verify that dynamic import works fine between two different Tenant VRFs. + When next-hop IPs are same across all VRFs. + When next-hop IPs are different across all VRFs. +3. Verify that with multiple tenant VRFs, dynamic import works fine between + Tenant VRFs to default VRF. + When next-hop IPs and prefixes are same across all VRFs. + When next-hop IPs and prefixes are different across all VRFs. +""" + +import os +import sys +import time +import pytest +import platform +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + create_route_maps, + create_static_routes, + create_prefix_lists, + create_bgp_community_lists, + get_frr_ipv6_linklocal, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_community, + verify_bgp_rib, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"} +NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"} +NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"} +NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"} +NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"} + +NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"} +NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"} +NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"} +NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"} +NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"} +NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"} + +NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"} +NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"} + +PREFIX_LIST = { + "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"], + "ipv6": ["11:11::1", "22:22::2", "22:22::22"], +} +PREFERRED_NEXT_HOP = "global" +VRF_LIST = ["RED", "BLUE", "GREEN"] +COMM_VAL_1 = "100:100" +COMM_VAL_2 = "500:500" +COMM_VAL_3 = "600:600" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_vrf_dynamic_route_leak_topo4.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), "4.19") < 0: + error_msg = ( + "BGP vrf dynamic route leak tests will not run " + '(have kernel "{}", but it requires >= 4.19)'.format(platform.release()) + ) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_dynamic_import_routes_between_tenant_to_default_vrf_p0(request): + """ + Verify that with multiple tenant VRFs, dynamic import works fine between + Tenant VRFs to default VRF. + + When next-hop IPs and prefixes are same across all VRFs. + When next-hop IPs and prefixes are different across all VRFs. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Configure static routes on R3 for each vrf and redistribute in " + "respective BGP instance" + ) + + for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + step("Configure static route for VRF : {}".format(vrf_name)) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static route on BGP VRF : {}".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} + ) + + redist_dict = { + "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + step( + "Verify that R3 has installed redistributed routes in respective " + "vrfs: {}".format(vrf_name) + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Import all tenant vrfs(GREEN+BLUE+RED) in default vrf on R3") + + for vrf_name in ["RED", "BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) + + redist_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify on R3 that it installs all the routes(imported from tenant " + "VRFs) in default vrf. Additionally, verify that R1 & R2 also " + "receive these routes from R3 and install in default vrf, next-hop " + "pointing to R3" + ) + + for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + } + ] + } + } + + for dut in ["r2", "r1"]: + next_hop_val = topo["routers"]["r3"]["links"]["{}-link4".format(dut)][ + addr_type + ].split("/")[0] + + result = verify_bgp_rib( + tgen, addr_type, dut, static_routes, next_hop=next_hop_val + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, dut, static_routes, next_hop=next_hop_val + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value, status in zip( + ["Remove", "Add"], [True, False], ["withdraw", "re-install"] + ): + step( + "{} import vrf GREEN/BLUE/RED/all command from default vrf " + "instance on R3".format(action) + ) + for vrf_name in ["RED", "BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": {"import": {"vrf": vrf_name, "delete": value}} + } + } + ) + + import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that R1,R2 & R3 {} imported routes from GREEN/BLUE/RED/all" + " in default vrf's RIB".format(status) + ) + for dut in ["r1", "r2", "r3"]: + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + + if value: + result = verify_bgp_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + + result = verify_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + else: + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value in zip(["Shut", "No shut"], [True, False]): + step( + "{} the neighborship between R1-R3 and R1-R2 for vrf RED, GREEN " + "and BLUE".format(action) + ) + bgp_disable = {"r3": {"bgp": []}} + for vrf_name in ["RED", "GREEN", "BLUE"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": {"r3-link4": {"shutdown": value}} + }, + "r2": { + "dest_link": {"r3-link4": {"shutdown": value}} + }, + } + } + } + } + ) + + bgp_disable["r3"]["bgp"].append( + {"vrf": vrf_name, "local_as": 3, "address_family": temp} + ) + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that when peering is shutdown for tenant vrfs, it " + "doesn't impact the RIB/FIB of default vrf on router R1 and R2" + ) + for dut in ["r1", "r2"]: + step("Verify RIB/FIB for default vrf on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value, status in zip( + ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"] + ): + step( + "{} the neighborship between R1-R3 and R2-R3 for default vrf".format(action) + ) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3-link4": {"shutdown": value}}}, + "r2": {"dest_link": {"r3-link4": {"shutdown": value}}}, + } + } + } + } + ) + + bgp_disable = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that R1 and R2 {} all the routes from default vrf's RIB" + " and FIB".format(status) + ) + for dut in ["r1", "r2"]: + step("Verify RIB/FIB for default vrf on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + + if value: + result = verify_bgp_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + + result = verify_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + else: + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Remove import command from router R3 and configure the same on R2") + temp = {} + for vrf_name in VRF_LIST: + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}} + ) + + import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that once import commands are removed from R3, all imported " + "routes are withdrawn from RIB/FIB of default vrf on R1/R2/R3" + ) + + for dut in ["r1", "r2", "r3"]: + step("Verify RIB/FIB for default vrf on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in BGP table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + ) + + result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + step("Configure static route for VRF : {} on r2".format(vrf_name)) + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static route on BGP VRF : {}".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} + ) + + redist_dict = { + "r2": {"bgp": [{"vrf": vrf_name, "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove redistribute static route on BGP VRF : {} on r3".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + } + } + ) + + redist_dict = { + "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for vrf_name in ["RED", "BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) + + import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}} + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify after import commands are re-configured on R2's vrf RED, all " + "those routes are installed again in default vrf of R1,R2,R3" + ) + for dut in ["r1", "r2", "r3"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Remove import vrf RED/GREEN/BLUE/all one by one from default vrf" " on R2") + for vrf_name in ["RED", "BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}} + ) + + import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}} + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that R1, R2 and R3 withdraws imported routes from default " + "vrf's RIB and FIB " + ) + for dut in ["r1", "r2", "r3"]: + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in BGP table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + ) + + result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Add import vrf RED/GREEN/BLUE/all one by one from default vrf on R2") + for vrf_name in ["RED", "BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) + + import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}} + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r1", "r2", "r3"]: + step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value in zip(["Shut", "No shut"], [True, False]): + step( + "{} the neighborship between R2-R3 for vrf GREEN, BLUE and RED".format( + action + ) + ) + bgp_disable = {"r2": {"bgp": []}} + for vrf_name in ["GREEN", "BLUE", "RED"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": {"r2-link4": {"shutdown": value}} + } + } + } + } + } + ) + + bgp_disable["r2"]["bgp"].append( + {"vrf": vrf_name, "local_as": 2, "address_family": temp} + ) + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers") + for dut in ["r1", "r2", "r3"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value, status in zip( + ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"] + ): + step("{} the neighborship between R2-R3 for default vrf".format(action)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r2-link4": {"shutdown": value}}} + } + } + } + } + ) + + bgp_disable = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}} + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that R1 and R2 {} all the routes from default vrfs RIB and" + " FIB".format(status) + ) + for dut in ["r1", "r3"]: + step("Verify RIB/FIB for default vrf on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + + if value: + result = verify_bgp_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + + result = verify_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + else: + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf new file mode 100644 index 0000000..66493f0 --- /dev/null +++ b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf @@ -0,0 +1,23 @@ +frr defaults traditional +! +hostname ce1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +router bgp 65002 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor eth0 interface + neighbor eth0 remote-as external + neighbor eth0 timers connect 1 + ! + address-family ipv4 unicast + neighbor eth0 activate + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/zebra.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/zebra.conf new file mode 100644 index 0000000..a163295 --- /dev/null +++ b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/zebra.conf @@ -0,0 +1,13 @@ +log file zebra.log +! +hostname ce1 +! +interface lo + ip address 172.16.0.1/32 +! +interface eth0 + ipv6 nd ra-interval 1 + no ipv6 nd suppress-ra +! +line vty +! diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf new file mode 100644 index 0000000..c5c9927 --- /dev/null +++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf @@ -0,0 +1,41 @@ +frr defaults traditional +! +hostname pe1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +router bgp 65001 + bgp router-id 192.0.2.1 + ! +! +router bgp 65001 vrf vrf10 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor eth0 interface + neighbor eth0 remote-as external + neighbor eth0 timers connect 1 + ! + address-family ipv4 unicast + neighbor eth0 activate + rd vpn export 65001:10 + rt vpn both 0:10 + import vpn + export vpn + exit-address-family + ! +! +router bgp 65001 vrf vrf20 + bgp router-id 192.0.2.1 + ! + address-family ipv4 unicast + rd vpn export 65001:20 + rt vpn both 0:10 + import vpn + export vpn + exit-address-family + ! +! diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/default_ipv4_vpn.json b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/default_ipv4_vpn.json new file mode 100644 index 0000000..9516016 --- /dev/null +++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/default_ipv4_vpn.json @@ -0,0 +1,32 @@ +{ + "vrfName": "default", + "routerId": "192.0.2.1", + "localAS": 65001, + "routes": { + "routeDistinguishers": { + "65001:10": { + "172.16.0.1/32": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "172.16.0.1", + "prefixLen": 32, + "network": "172.16.0.1\/32", + "path": "65002", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "hostname": "pe1", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf10_ipv4_unicast.json b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf10_ipv4_unicast.json new file mode 100644 index 0000000..768bffb --- /dev/null +++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf10_ipv4_unicast.json @@ -0,0 +1,32 @@ +{ + "vrfName": "vrf10", + "routerId": "192.0.2.1", + "localAS": 65001, + "routes": { + "172.16.0.1/32": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "172.16.0.1", + "prefixLen": 32, + "network": "172.16.0.1\/32", + "path": "65002", + "origin": "incomplete", + "nexthops": [ + { + "hostname": "ce1", + "afi": "ipv6", + "scope": "global", + "used": true + }, + { + "hostname": "ce1", + "afi": "ipv6", + "scope": "link-local" + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf20_ipv4_unicast.json b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf20_ipv4_unicast.json new file mode 100644 index 0000000..1e93715 --- /dev/null +++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf20_ipv4_unicast.json @@ -0,0 +1,34 @@ +{ + "vrfName": "vrf20", + "routerId": "192.0.2.1", + "localAS": 65001, + "routes": { + "172.16.0.1/32": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "172.16.0.1", + "prefixLen": 32, + "network": "172.16.0.1\/32", + "path": "65002", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "hostname": "pe1", + "afi": "ipv6", + "scope": "global", + "used": true + }, + { + "hostname": "pe1", + "afi": "ipv6", + "scope": "link-local" + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/zebra.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/zebra.conf new file mode 100644 index 0000000..d40041a --- /dev/null +++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/zebra.conf @@ -0,0 +1,10 @@ +log file zebra.log +! +hostname pe1 +! +interface eth0 vrf vrf10 + ipv6 nd ra-interval 1 + no ipv6 nd suppress-ra +! +line vty +! diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/test_bgp_vrf_leaking.py b/tests/topotests/bgp_vrf_leaking_5549_routes/test_bgp_vrf_leaking.py new file mode 100755 index 0000000..244db6c --- /dev/null +++ b/tests/topotests/bgp_vrf_leaking_5549_routes/test_bgp_vrf_leaking.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2022, LINE Corporation +# Authored by Ryoga Saito +# + +import os +import re +import sys +import json +import functools +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import required_linux_kernel_version + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("pe1") + tgen.add_router("ce1") + + tgen.add_link(tgen.gears["pe1"], tgen.gears["ce1"], "eth0", "eth0") + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + for rname, router in tgen.routers().items(): + router.load_config(TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname))) + router.load_config(TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname))) + + tgen.gears["pe1"].run("ip link add vrf10 type vrf table 10") + tgen.gears["pe1"].run("ip link set vrf10 up") + tgen.gears["pe1"].run("ip link add vrf20 type vrf table 20") + tgen.gears["pe1"].run("ip link set vrf20 up") + tgen.gears["pe1"].run("ip link set eth0 master vrf10") + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def open_json_file(path): + try: + with open(path, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(path) + + +def check_vrf10_rib(output): + expected = open_json_file("%s/pe1/results/vrf10_ipv4_unicast.json" % CWD) + actual = json.loads(output) + return topotest.json_cmp(actual, expected) + + +def check_default_vpn_rib(output): + expected = open_json_file("%s/pe1/results/default_ipv4_vpn.json" % CWD) + actual = json.loads(output) + return topotest.json_cmp(actual, expected) + + +def check_vrf20_rib(output): + expected = open_json_file("%s/pe1/results/vrf20_ipv4_unicast.json" % CWD) + actual = json.loads(output) + return topotest.json_cmp(actual, expected) + + +def check(name, command, checker): + tgen = get_topogen() + router = tgen.gears[name] + + def _check(): + try: + return checker(router.vtysh_cmd(command)) + except: + return False + + logger.info('[+] check {} "{}"'.format(name, command)) + _, result = topotest.run_and_expect(_check, None, count=10, wait=0.5) + assert result is None, "Failed" + + +def test_rib(): + check("pe1", "show bgp vrf vrf10 ipv4 unicast json", check_vrf10_rib) + check("pe1", "show bgp ipv4 vpn json", check_default_vpn_rib) + check("pe1", "show bgp vrf vrf20 ipv4 unicast json", check_vrf20_rib) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/__init__.py b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/bgpd.conf b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/bgpd.conf new file mode 100644 index 0000000..9e0a4a8 --- /dev/null +++ b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/bgpd.conf @@ -0,0 +1,31 @@ +! +router bgp 65500 +exit +! +router bgp 65500 vrf vrf1 + bgp router-id 10.0.0.1 + no bgp network import-check + address-family ipv4 unicast + network 192.168.100.100/32 route-map rm + rd vpn export 65500:10001 + rt vpn import 65500:10000 65500:10990 + rt vpn export 65500:10000 + export vpn + import vpn + exit-address-family +exit +! +router bgp 65500 vrf vrf2 + address-family ipv4 unicast + rd vpn export 65500:11001 + rt vpn import 65500:11000 65500:11990 + rt vpn export 65500:11000 + export vpn + import vpn + exit-address-family +exit +! +route-map rm permit 10 + set extcommunity rt 65500:10100 65500:11990 +exit +! diff --git a/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/zebra.conf b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/zebra.conf new file mode 100644 index 0000000..22a26ac --- /dev/null +++ b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 10.0.0.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/test_bgp_vrf_leaking_rt_change_route_maps.py b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/test_bgp_vrf_leaking_rt_change_route_maps.py new file mode 100644 index 0000000..42cbf1e --- /dev/null +++ b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/test_bgp_vrf_leaking_rt_change_route_maps.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# + +""" +If we overwrite import/export RT list via route-maps or even flush by using +`set extcommunity none`, then we must withdraw old paths from VRFs to avoid +stale paths. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("r1") + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router = tgen.gears["r1"] + router.cmd_raises("ip link add vrf1 type vrf table 10") + router.cmd_raises("ip link set up dev vrf1") + router.cmd_raises("ip link add vrf2 type vrf table 20") + router.cmd_raises("ip link set up dev vrf2") + router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf")) + router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf")) + router.start() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_vrf_leaking_rt_change_route_maps(): + tgen = get_topogen() + + router = tgen.gears["r1"] + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_check_path(): + output = json.loads(router.vtysh_cmd("show bgp vrf vrf2 ipv4 unicast json")) + expected = {"routes": {"192.168.100.100/32": [{"nhVrfName": "vrf1"}]}} + return topotest.json_cmp(output, expected) + + step("Initial converge") + test_func = functools.partial(_bgp_check_path) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't see 192.168.100.100/32 leaked from vrf1 into vrf2." + + step("Overwrite RT list (remove rt 65500:11990 from route-map)") + router.vtysh_cmd( + """ + config terminal + route-map rm permit 10 + set extcommunity rt 65500:10100 + exit + """ + ) + + step("Check if 192.168.100.100/32 was removed from vrf2") + test_func = functools.partial(_bgp_check_path) + _, result = topotest.run_and_expect(test_func, not None, count=20, wait=0.5) + assert result is not None, "192.168.100.100/32 still exists in vrf2 as stale." + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo1.json b/tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo1.json new file mode 100644 index 0000000..b1d7d09 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo1.json @@ -0,0 +1,563 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["11.11.11.1/32", "11.11.11.11/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["11:11::1/128", "11:11::11/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["10.10.10.10/32", "10.10.10.100/32"], + "next_hop":"Null0" + }, + { + "network": ["10:10::10/128", "10:10::100/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r2": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["22.22.22.2/32", "22.22.22.22/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["22:22::2/128", "22:22::22/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["20.20.20.20/32", "20.20.20.200/32"], + "next_hop":"Null0" + }, + { + "network": ["20:20::20/128", "20:20::200/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r3": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["30.30.30.3/32", "30.30.30.30/32"], + "next_hop":"Null0" + }, + { + "network": ["30:30::3/128", "30:30::30/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["40.40.40.4/32", "40.40.40.40/32"], + "next_hop":"Null0" + }, + { + "network": ["40:40::4/128", "40:40::40/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + } + } +} diff --git a/tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo2.json b/tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo2.json new file mode 100644 index 0000000..0b13882 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo2.json @@ -0,0 +1,1088 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r3-link1": {"ipv4": "192.168.1.1/24", "ipv6": "fd00:0:0:1::1/120", "vrf": "RED"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r3-link3": {"ipv4": "192.168.1.1/24", "ipv6": "fd00:0:0:1::1/120", "vrf": "GREEN"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + {"name": "RED", "id": "1"}, + {"name": "BLUE", "id": "2"}, + {"name": "GREEN", "id": "3"} + ], + "bgp": + [ + { + "local_as": "1", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "1", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "1", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "1", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r2": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + {"name": "RED", "id": "1"}, + {"name": "BLUE", "id": "2"}, + {"name": "GREEN", "id": "3"} + ], + "bgp": + [ + { + "local_as": "2", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "2", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "2", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "2", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r3": { + "links": { + "r1-link1": {"ipv4": "192.168.1.2/24", "ipv6": "fd00:0:0:1::2/120", "vrf": "RED"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r1-link3": {"ipv4": "192.168.1.2/24", "ipv6": "fd00:0:0:1::2/120", "vrf": "GREEN"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r4-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r4-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r5-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r5-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r5-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r5-link4": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + {"name": "RED", "id": "1"}, + {"name": "BLUE", "id": "2"}, + {"name": "GREEN", "id": "3"} + ], + "bgp": + [ + { + "local_as": "3", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": {} + } + }, + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r5": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r5": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "3", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link2": {} + } + }, + "r2": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r5": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r2": { + "dest_link": { + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r5": { + "dest_link": { + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "3", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r5": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r2": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r5": { + "dest_link": { + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "3", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r2": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r4": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "r5": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r2": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r5": { + "dest_link": { + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + {"name": "RED", "id": "1"}, + {"name": "BLUE", "id": "2"}, + {"name": "GREEN", "id": "3"} + ], + "bgp": + [ + { + "local_as": "4", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "4", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "4", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "4", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ] + }, + "r5": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + {"name": "RED", "id": "1"}, + {"name": "BLUE", "id": "2"}, + {"name": "GREEN", "id": "3"} + ], + "bgp": + [ + { + "local_as": "3", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "3", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "3", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link3": {} + } + } + } + } + } + } + }, + { + "local_as": "3", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5-link4": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ] + } + } +} diff --git a/tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo1.py b/tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo1.py new file mode 100644 index 0000000..f352196 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo1.py @@ -0,0 +1,879 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test BGP VRF Lite: + +1. Verify BGP best path selection algorithm works fine when +routes are imported from ISR to default vrf and vice versa. +""" + +import os +import sys +import time +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + check_address_types, + write_test_footer, + step, + create_route_maps, + create_prefix_lists, + check_router_status, + get_frr_ipv6_linklocal, + shutdown_bringup_interface, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_community, + verify_bgp_rib, + clear_bgp, + verify_best_path_as_per_bgp_attribute, +) +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"} +NETWORK1_3 = {"ipv4": "10.10.10.10/32", "ipv6": "10:10::10/128"} +NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"} + +NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"} +NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"} +NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"} +NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"} + +NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"} +NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"} +NETWORK3_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"} +NETWORK3_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"} + +NETWORK4_1 = {"ipv4": "40.40.40.4/32", "ipv6": "40:40::4/128"} +NETWORK4_2 = {"ipv4": "40.40.40.40/32", "ipv6": "40:40::40/128"} +NETWORK4_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"} +NETWORK4_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} +LOOPBACK_1 = { + "ipv4": "10.0.0.7/24", + "ipv6": "fd00:0:0:1::7/64", + "ipv4_mask": "255.255.255.0", + "ipv6_mask": None, +} +LOOPBACK_2 = { + "ipv4": "10.0.0.16/24", + "ipv6": "fd00:0:0:3::5/64", + "ipv4_mask": "255.255.255.0", + "ipv6_mask": None, +} +PREFERRED_NEXT_HOP = "global" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_vrf_lite_best_path_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), "4.19") < 0: + error_msg = ( + "BGP vrf dynamic route leak tests will not run " + '(have kernel "{}", but it requires >= 4.19)'.format(platform.release()) + ) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def disable_route_map_to_prefer_global_next_hop(tgen, topo): + """ + This API is to remove prefer global route-map applied on neighbors + + Parameter: + ---------- + * `tgen` : Topogen object + * `topo` : Input JSON data + + Returns: + -------- + True/errormsg + + """ + + logger.info("Remove prefer-global rmap applied on neighbors") + input_dict = { + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + ] + }, + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + ] + }, + "r3": { + "bgp": [ + { + "local_as": "300", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + { + "local_as": "300", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + ] + }, + "r4": { + "bgp": [ + { + "local_as": "400", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + { + "local_as": "400", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase :Failed \n Error: {}".format(result) + + return True + + +def test_bgp_best_path_with_dynamic_import_p0(request): + """ + 1.5.6. Verify BGP best path selection algorithm works fine when + routes are imported from ISR to default vrf and vice versa. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + step( + "Redistribute configured static routes into BGP process" " on R1/R2 and R3" + ) + + input_dict_1 = {} + DUT = ["r1", "r2", "r3", "r4"] + VRFS = ["ISR", "ISR", "default", "default"] + AS_NUM = [100, 100, 300, 400] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step("Import from default vrf into vrf ISR on R1 and R2 as below") + + input_dict_vrf = {} + DUT = ["r1", "r2"] + VRFS = ["ISR", "ISR"] + AS_NUM = [100, 100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_vrf.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: {"unicast": {"import": {"vrf": "default"}}} + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_vrf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_default = {} + DUT = ["r1", "r2"] + VRFS = ["default", "default"] + AS_NUM = [100, 100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_default.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: {"unicast": {"import": {"vrf": "ISR"}}} + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_default) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify ECMP/Next-hop/Imported routes Vs Locally originated " + "routes/eBGP routes vs iBGP routes --already covered in almost" + " all tests" + ) + + for addr_type in ADDR_TYPES: + step("Verify Pre-emption") + + input_routes_r3 = { + "r3": {"static_routes": [{"network": [NETWORK3_3[addr_type]]}]} + } + + intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"] + + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1) + nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1) + else: + nh_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[ + 0 + ] + nh_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"][addr_type].split("/")[ + 0 + ] + + result = verify_bgp_rib( + tgen, addr_type, "r1", input_routes_r3, next_hop=[nh_r4_r1] + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Shutdown interface connected to r1 from r4:") + shutdown_bringup_interface(tgen, "r4", intf_r4_r1, False) + + for addr_type in ADDR_TYPES: + input_routes_r3 = { + "r3": {"static_routes": [{"network": [NETWORK3_3[addr_type]]}]} + } + + intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"] + + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1) + nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1) + else: + nh_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[ + 0 + ] + nh_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"][addr_type].split("/")[ + 0 + ] + + step("Verify next-hop is changed") + result = verify_bgp_rib( + tgen, addr_type, "r1", input_routes_r3, next_hop=[nh_r3_r1] + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Bringup interface connected to r1 from r4:") + shutdown_bringup_interface(tgen, "r4", intf_r4_r1, True) + + for addr_type in ADDR_TYPES: + input_routes_r3 = { + "r3": {"static_routes": [{"network": [NETWORK3_3[addr_type]]}]} + } + + intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"] + + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1) + nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1) + else: + nh_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[ + 0 + ] + nh_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"][addr_type].split("/")[ + 0 + ] + + step("Verify next-hop is not chnaged aftr shutdown:") + result = verify_bgp_rib( + tgen, addr_type, "r1", input_routes_r3, next_hop=[nh_r3_r1] + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Active-Standby scenario(as-path prepend and Local pref)") + + for addr_type in ADDR_TYPES: + step("Create prefix-list") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pf_ls_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK3_4[addr_type], + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step("Create route-map to match prefix-list and set localpref 500") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 10, + "match": { + addr_type: { + "prefix_lists": "pf_ls_{}".format(addr_type) + } + }, + "set": {"locPrf": 500}, + } + ] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Create route-map to match prefix-list and set localpref 600") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH2_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 20, + "match": { + addr_type: { + "prefix_lists": "pf_ls_{}".format(addr_type) + } + }, + "set": {"locPrf": 600}, + } + ] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_rma = { + "r1": { + "bgp": [ + { + "local_as": "100", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap_PATH1_{}".format( + addr_type + ), + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap_PATH2_{}".format( + addr_type + ), + "direction": "in", + } + ] + } + } + }, + } + } + } + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rma) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r1" + attribute = "locPrf" + + for addr_type in ADDR_TYPES: + step("Verify bestpath is installed as per highest localpref") + + input_routes_r3 = { + "r3": {"static_routes": [{"network": [NETWORK3_4[addr_type]]}]} + } + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_routes_r3, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step("Create route-map to match prefix-list and set localpref 700") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 10, + "match": { + addr_type: { + "prefix_lists": "pf_ls_{}".format(addr_type) + } + }, + "set": {"locPrf": 700}, + } + ] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step("Verify bestpath is changed as per highest localpref") + + input_routes_r3 = { + "r3": {"static_routes": [{"network": [NETWORK3_4[addr_type]]}]} + } + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_routes_r3, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step("Create route-map to match prefix-list and set as-path prepend") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH2_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 20, + "match": { + addr_type: { + "prefix_lists": "pf_ls_{}".format(addr_type) + } + }, + "set": { + "localpref": 700, + "path": {"as_num": "111", "as_action": "prepend"}, + }, + } + ] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + attribute = "path" + + for addr_type in ADDR_TYPES: + step("Verify bestpath is changed as per shortest as-path") + + input_routes_r3 = { + "r3": {"static_routes": [{"network": [NETWORK3_4[addr_type]]}]} + } + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_routes_r3, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo2.py b/tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo2.py new file mode 100644 index 0000000..5d93964 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo2.py @@ -0,0 +1,526 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test BGP VRF Lite: +1. Verify that locally imported routes are selected as best path over eBGP imported routes + peers. +2. Verify ECMP for imported routes from different VRFs. +""" + +import os +import sys +import time +import pytest +import platform +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + create_static_routes, + check_router_status, + apply_raw_config +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, + verify_bgp_bestpath +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"} +NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"} +NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"} +NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"} +NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"} + +NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"} +NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"} +NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"} +NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"} +NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"} +NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"} + +NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"} +NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"} + +PREFIX_LIST = { + "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"], + "ipv6": ["11:11::1", "22:22::2", "22:22::22"], +} +PREFERRED_NEXT_HOP = "global" +VRF_LIST = ["RED", "BLUE", "GREEN"] +COMM_VAL_1 = "100:100" +COMM_VAL_2 = "500:500" +COMM_VAL_3 = "600:600" +BESTPATH = { + "ipv4": "0.0.0.0", + "ipv6": "::" +} + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_vrf_lite_best_path_topo2.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), "4.19") < 0: + error_msg = ( + "BGP vrf dynamic route leak tests will not run " + '(have kernel "{}", but it requires >= 4.19)'.format(platform.release()) + ) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + +def test_dynamic_import_ecmp_imported_routed_diffrent_vrfs_p0(request): + """ + Verify ECMP for imported routes from different VRFs. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure same static routes in tenant vrfs RED and GREEN on router " + "R3 and redistribute in respective BGP process") + + for vrf_name in ["RED", "GREEN"]: + for addr_type in ADDR_TYPES: + if vrf_name == "GREEN": + next_hop_vrf = topo["routers"]["r1"]["links"][ + "r3-link3"][addr_type].split("/")[0] + else: + next_hop_vrf = topo["routers"]["r2"]["links"][ + "r3-link1"][addr_type].split("/")[0] + static_routes = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": next_hop_vrf, + "vrf": vrf_name + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + step("Redistribute static route on BGP VRF : {}".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update({ + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static" + }] + } + } + }) + + redist_dict = {"r3": {"bgp": [{ + "vrf": vrf_name, "local_as": 3, "address_family": temp + }]}} + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + step("Verify that configured static routes are installed in respective " + "BGP table for vrf RED & GREEN") + for vrf_name in ["RED", "GREEN"]: + for addr_type in ADDR_TYPES: + if vrf_name == "GREEN": + next_hop_vrf = topo["routers"]["r1"]["links"][ + "r3-link3"][addr_type].split("/")[0] + else: + next_hop_vrf = topo["routers"]["r2"]["links"][ + "r3-link1"][addr_type].split("/")[0] + static_routes = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "vrf": vrf_name + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, + next_hop=next_hop_vrf) + assert result is True, "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + result = verify_rib(tgen, addr_type, "r3", static_routes, + next_hop=next_hop_vrf) + assert result is True, "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + step("Import vrf RED and GREEN into default vrf and Configure ECMP") + bgp_val = [] + for vrf_name in ["RED", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update({ + addr_type: { + "unicast": { + "import": { + "vrf": vrf_name + }, + "maximum_paths": { + "ebgp": 2 + } + } + } + }) + + bgp_val.append({ + "local_as": 3, "address_family": temp + }) + + import_dict = {"r3": {"bgp": bgp_val}} + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + step("Configure bgp bestpath on router r3") + r3_raw_config = { + "r3": { + "raw_config": [ + "router bgp 3", + "bgp bestpath as-path multipath-relax" + ] + } + } + result = apply_raw_config(tgen, r3_raw_config) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + step("Verify that routes are imported with two different next-hop vrfs " + "and IPs. Additionally R3 must do ECMP for both the routes.") + + for addr_type in ADDR_TYPES: + next_hop_vrf = [ + topo["routers"]["r2"]["links"]["r3-link1"][addr_type]. \ + split("/")[0], + topo["routers"]["r1"]["links"]["r3-link3"][addr_type]. \ + split("/")[0] + ] + static_routes = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, + next_hop=next_hop_vrf) + assert result is True, "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + result = verify_rib(tgen, addr_type, "r3", static_routes, + next_hop=next_hop_vrf) + assert result is True, "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + step("Now change the next-hop of static routes in vrf RED and GREEN to " + "same IP address") + for addr_type in ADDR_TYPES: + next_hop_vrf = topo["routers"]["r1"]["links"][ + "r3-link3"][addr_type].split("/")[0] + static_routes = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": next_hop_vrf, + "vrf": "RED" + }, + { + "network": [NETWORK1_1[addr_type]], + "next_hop": topo["routers"]["r2"]["links"][ + "r3-link1"][addr_type].split("/")[0], + "vrf": "RED", + "delete": True + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + step("Verify that now routes are imported with two different next-hop " + "vrfs but same IPs. Additionally R3 must do ECMP for both the routes") + + for addr_type in ADDR_TYPES: + next_hop_vrf = [ + topo["routers"]["r1"]["links"]["r3-link3"][addr_type].\ + split("/")[0], + topo["routers"]["r1"]["links"]["r3-link3"][addr_type]. \ + split("/")[0] + ] + static_routes = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, + next_hop=next_hop_vrf) + assert result is True, "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + result = verify_rib(tgen, addr_type, "r3", static_routes, + next_hop=next_hop_vrf) + assert result is True, "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + write_test_footer(tc_name) + + +def test_locally_imported_routes_selected_as_bestpath_over_ebgp_imported_routes_p0(request): + """ + Verify ECMP for imported routes from different VRFs. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure same static routes on R2 and R3 vrfs and redistribute in BGP " + "for GREEN and RED vrf instances") + for dut, network in zip(["r2", "r3"], [ + [NETWORK1_1, NETWORK1_2], [NETWORK1_1, NETWORK1_2]]): + for vrf_name, network_vrf in zip(["RED", "GREEN"], network): + step("Configure static route for VRF : {} on {}".format(vrf_name, + dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [network_vrf[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for dut, as_num in zip(["r2", "r3"], ["2", "3"]): + for vrf_name in ["RED", "GREEN"]: + step("Redistribute static route on BGP VRF : {}".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update({ + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static" + }] + } + } + }) + + redist_dict = {dut: {"bgp": [{ + "vrf": vrf_name, "local_as": as_num, "address_family": temp + }]}} + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + step("Verify that R2 and R3 has installed redistributed routes in default " + "and RED vrfs and GREEN respectively:") + for dut, network in zip(["r2", "r3"], + [[NETWORK1_1, NETWORK1_2], + [NETWORK1_1, NETWORK1_2]]): + for vrf_name, network_vrf in zip(["RED", "GREEN"], network): + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [network_vrf[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + step("Import vrf RED's route in vrf GREEN on R3") + temp = {} + for addr_type in ADDR_TYPES: + temp.update({ + addr_type: { + "unicast": { + "import": { + "vrf": "RED" + } + } + } + }) + + import_dict = {"r3": {"bgp": [{ + "vrf": "GREEN", "local_as": 3, "address_family": temp + }]}} + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + step("Verify that locally imported routes are installed over eBGP imported" + " routes from VRF RED into VRF GREEN") + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_2[addr_type]], + "next_hop": "blackhole", + "vrf": "GREEN" + } + ] + } + } + + input_routes = { + "r3": { + addr_type: [ + { + "network": NETWORK1_2[addr_type], + "bestpath": BESTPATH[addr_type], + "vrf": "GREEN" + } + ] + } + } + + result = verify_bgp_bestpath(tgen, addr_type, input_routes) + assert result is True, "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf new file mode 100644 index 0000000..0033bc2 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 101 vrf r1-cust1 + bgp router-id 10.254.254.1 + no bgp ebgp-requires-policy + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r1-eth0 interface peer-group r2g + neighbor r1-eth0 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json new file mode 100644 index 0000000..1c7500b --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json @@ -0,0 +1,44 @@ +{ + "10.254.254.2/32": [ + { + "prefix": "10.254.254.2/32", + "protocol": "bgp", + "vrfName": "r1-cust1", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "prefix": "10.254.254.1/32", + "protocol": "connected", + "vrfName": "r1-cust1", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "loop1", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json new file mode 100644 index 0000000..0e1de87 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json @@ -0,0 +1,37 @@ +{ + "2001:db8:1::/64": [ + { + "prefix": "2001:db8:1::/64", + "protocol": "bgp", + "vrfName": "r1-cust1", + "distance": 20, + "metric": 0, + "nexthops": [ + { + "afi": "ipv6", + "interfaceName": "r1-eth0", + "active": true + } + ] + }, + { + "prefix": "2001:db8:1::/64", + "protocol": "connected", + "vrfName": "r1-cust1", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf new file mode 100644 index 0000000..74359a5 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf @@ -0,0 +1,9 @@ +! debug zebra packet recv +! debug zebra packet send +log stdout +interface loop1 vrf r1-cust1 + ip address 10.254.254.1/32 +! +interface r1-eth0 vrf r1-cust1 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf new file mode 100644 index 0000000..183157e --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 102 vrf r2-cust1 + bgp router-id 10.254.254.2 + no bgp ebgp-requires-policy + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r2-eth0 interface peer-group r2g + neighbor r2-eth0 timers 3 10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json new file mode 100644 index 0000000..4d7b289 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json @@ -0,0 +1,44 @@ +{ + "10.254.254.2/32": [ + { + "prefix": "10.254.254.2/32", + "protocol": "connected", + "vrfName": "r2-cust1", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "loop1", + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "prefix": "10.254.254.1/32", + "protocol": "bgp", + "vrfName": "r2-cust1", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "r2-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json new file mode 100644 index 0000000..805b57d --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json @@ -0,0 +1,23 @@ +{ + "2001:db8:1::/64": [ + { + "prefix": "2001:db8:1::/64", + "protocol": "connected", + "vrfName": "r2-cust1", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r2-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf new file mode 100644 index 0000000..c3795ab --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf @@ -0,0 +1,9 @@ +ip forwarding +ipv6 forwarding +! +interface loop1 vrf r2-cust1 + ip address 10.254.254.2/32 +! +interface r2-eth0 vrf r2-cust1 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot new file mode 100644 index 0000000..da67c29 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot @@ -0,0 +1,44 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py new file mode 100644 index 0000000..32239e9 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_ipv6_rtadv.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# + +""" + test_bgp_ipv6_rtadv.py: Test the FRR BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import required_linux_kernel_version + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 2 routers. + tgen.add_router("r1") + tgen.add_router("r2") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("5.0") + if result is not True: + pytest.skip("Kernel requirements are not met") + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + logger.info("Testing with VRF Lite support") + + cmds = [ + "ip link add {0}-cust1 type vrf table 1001", + "ip link add loop1 type dummy", + "ip link set loop1 master {0}-cust1", + "ip link set {0}-eth0 master {0}-cust1", + ] + + for rname, router in router_list.items(): + for cmd in cmds: + output = tgen.net[rname].cmd(cmd.format(rname)) + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv4 routing tables. + logger.info("Checking IPv4 routes for convergence") + + for router in tgen.routers().values(): + json_file = "{}/{}/ipv4_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip route vrf {}-cust1 json".format(router.name), + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check IPv6 routing tables. + logger.info("Checking IPv6 routes for convergence") + for router in tgen.routers().values(): + json_file = "{}/{}/ipv6_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ipv6 route vrf {}-cust1 json".format(router.name), + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_md5_peering/__init__.py b/tests/topotests/bgp_vrf_md5_peering/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_vrf_md5_peering/exabgp.env b/tests/topotests/bgp_vrf_md5_peering/exabgp.env new file mode 100644 index 0000000..28e6423 --- /dev/null +++ b/tests/topotests/bgp_vrf_md5_peering/exabgp.env @@ -0,0 +1,53 @@ +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' +##daemonize = false + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg b/tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg new file mode 100644 index 0000000..3260513 --- /dev/null +++ b/tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg @@ -0,0 +1,13 @@ +neighbor 10.0.0.1 { + router-id 10.0.0.2; + local-address 10.0.0.2; + local-as 65001; + peer-as 65534; + md5 test123; + + static { + route 192.168.100.1/32 { + next-hop 10.0.0.2; + } + } +} diff --git a/tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf b/tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf new file mode 100644 index 0000000..9f2ee19 --- /dev/null +++ b/tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf @@ -0,0 +1,11 @@ +! +!debug bgp neighbor +! +router bgp 65534 vrf public + bgp router-id 10.0.0.1 + no bgp ebgp-requires-policy + neighbor 10.0.0.2 remote-as external + neighbor 10.0.0.2 timers 3 10 + neighbor 10.0.0.2 timers connect 1 + neighbor 10.0.0.2 password test123 +! diff --git a/tests/topotests/bgp_vrf_md5_peering/r1/zebra.conf b/tests/topotests/bgp_vrf_md5_peering/r1/zebra.conf new file mode 100644 index 0000000..0c183ae --- /dev/null +++ b/tests/topotests/bgp_vrf_md5_peering/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 vrf public + ip address 10.0.0.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_vrf_md5_peering/test_bgp_vrf_md5_peering.py b/tests/topotests/bgp_vrf_md5_peering/test_bgp_vrf_md5_peering.py new file mode 100644 index 0000000..eefe586 --- /dev/null +++ b/tests/topotests/bgp_vrf_md5_peering/test_bgp_vrf_md5_peering.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if BGP MD5 basic authentication works per-VRF. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + r1 = tgen.add_router("r1") + peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.2", defaultRoute="via 10.0.0.1") + + switch = tgen.add_switch("s1") + switch.add_link(r1) + switch.add_link(peer1) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + r1 = tgen.gears["r1"] + r1.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf")) + r1.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf")) + r1.start() + + peer = tgen.gears["peer1"] + peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env")) + + # VRF 'public' + r1.cmd_raises("ip link add public type vrf table 1001") + r1.cmd_raises("ip link set up dev public") + r1.cmd_raises("ip link set r1-eth0 master public") + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_vrf_md5_peering(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show ip bgp vrf public neighbor 10.0.0.2 json") + ) + expected = { + "10.0.0.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, "Can't peer with md5 per-VRF" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_netns/__init__.py b/tests/topotests/bgp_vrf_netns/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.dot b/tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.dot new file mode 100644 index 0000000..2b1f72b --- /dev/null +++ b/tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.dot @@ -0,0 +1,50 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph bgp_vrf_netns_eBGP_topo1 { + label="bgp vrf netns topo1 - eBGP with different AS numbers"; + labelloc="t"; + + # Routers + r1 [ + label="r1\nrtr-id 10.0.255.1/32", + shape=doubleoctagon, + fillcolor="#f08080", + style=filled, + ]; + + # 1 Switch for eBGP Peers + s1 [ + label="s1\n10.0.1.0/24", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + + # 1 ExaBGP Peers AS 101 + peer1 [ + label="eBGP peer1\nAS101\nrtr-id 10.0.1.101/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + + # Connections + r1 -- s1 [label="eth0\n.1"]; + + peer1 -- s1 [label="eth0\n.101"]; + + # Arrange network to make cleaner diagram + { rank=same peer1 } -- s1 -- { rank=same r1 } [style=invis] +} diff --git a/tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.pdf b/tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.pdf new file mode 100644 index 0000000..6da2288 Binary files /dev/null and b/tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.pdf differ diff --git a/tests/topotests/bgp_vrf_netns/exabgp.env b/tests/topotests/bgp_vrf_netns/exabgp.env new file mode 100644 index 0000000..a328e04 --- /dev/null +++ b/tests/topotests/bgp_vrf_netns/exabgp.env @@ -0,0 +1,54 @@ + +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' +##daemonize = false + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_vrf_netns/peer1/exa-send.py b/tests/topotests/bgp_vrf_netns/peer1/exa-send.py new file mode 100755 index 0000000..ab0eb8c --- /dev/null +++ b/tests/topotests/bgp_vrf_netns/peer1/exa-send.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python2 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +asnum = 99 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 community %i:1 next-hop 10.0.%i.%i\n" + % (i, i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg b/tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg new file mode 100644 index 0000000..2d0ca89 --- /dev/null +++ b/tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa-send.py 1 10"; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 1"; + receive-routes; + encoder text; + } + + neighbor 10.0.1.1 { + router-id 10.0.1.101; + local-address 10.0.1.101; + local-as 99; + peer-as 100; + graceful-restart; + } + +} diff --git a/tests/topotests/bgp_vrf_netns/r1/bgpd.conf b/tests/topotests/bgp_vrf_netns/r1/bgpd.conf new file mode 100644 index 0000000..572dce7 --- /dev/null +++ b/tests/topotests/bgp_vrf_netns/r1/bgpd.conf @@ -0,0 +1,10 @@ +! +router bgp 100 vrf r1-bgp-cust1 + bgp router-id 10.0.1.1 + bgp bestpath as-path multipath-relax + no bgp ebgp-requires-policy + neighbor 10.0.1.101 remote-as 99 + neighbor 10.0.1.101 timers 3 10 + ! +! + diff --git a/tests/topotests/bgp_vrf_netns/r1/summary.txt b/tests/topotests/bgp_vrf_netns/r1/summary.txt new file mode 100644 index 0000000..819f261 --- /dev/null +++ b/tests/topotests/bgp_vrf_netns/r1/summary.txt @@ -0,0 +1,17 @@ +{ +"ipv4Unicast":{ + "routerId":"10.0.1.1", + "as":100, + "vrfName":"r1-bgp-cust1", + "peerCount":1, + "peers":{ + "10.0.1.101":{ + "outq":0, + "inq":0, + "pfxRcd":10, + "state":"Established" + } + }, + "totalPeers":1 +} +} diff --git a/tests/topotests/bgp_vrf_netns/r1/summary20.txt b/tests/topotests/bgp_vrf_netns/r1/summary20.txt new file mode 100644 index 0000000..ea04a56 --- /dev/null +++ b/tests/topotests/bgp_vrf_netns/r1/summary20.txt @@ -0,0 +1,15 @@ +{ + "routerId":"10.0.1.1", + "as":100, + "vrfName":"re1-bgp-cust1", + "peerCount":1, + "peers":{ + "10.0.1.101":{ + "outq":0, + "inq":0, + "pfxRcd":10, + "state":"Established" + } + }, + "totalPeers":1 +} diff --git a/tests/topotests/bgp_vrf_netns/r1/zebra.conf b/tests/topotests/bgp_vrf_netns/r1/zebra.conf new file mode 100644 index 0000000..ed4edfa --- /dev/null +++ b/tests/topotests/bgp_vrf_netns/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface r1-eth0 vrf r1-bgp-cust1 + ip address 10.0.1.1/24 +! +line vty +! +! debug vrf diff --git a/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py b/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py new file mode 100644 index 0000000..8457b75 --- /dev/null +++ b/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_vrf_netns_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2018 by 6WIND +# + +""" +test_bgp_vrf_netns_topo1.py: Test BGP topology with EBGP on NETNS VRF +""" + +import json +import os +import sys +import functools +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.bgpd] + + +total_ebgp_peers = 1 +CustomizeVrfWithNetns = True + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +def build_topo(tgen): + tgen.add_router("r1") + + # Setup Switches + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + + # Add eBGP ExaBGP neighbors + peer_ip = "10.0.1.101" + peer_route = "via 10.0.1.1" + peer = tgen.add_exabgp_peer("peer1", ip=peer_ip, defaultRoute=peer_route) + switch = tgen.gears["s1"] + switch.add_link(peer) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + # Get r1 reference + router = tgen.gears["r1"] + + # check for zebra capability + if CustomizeVrfWithNetns == True: + if router.check_capability(TopoRouter.RD_ZEBRA, "--vrfwnetns") == False: + return pytest.skip( + "Skipping BGP VRF NETNS Test. VRF NETNS backend not available on FRR" + ) + if os.system("ip netns list") != 0: + return pytest.skip( + "Skipping BGP VRF NETNS Test. NETNS not available on System" + ) + # retrieve VRF backend kind + if CustomizeVrfWithNetns == True: + logger.info("Testing with VRF Namespace support") + + # create VRF r1-bgp-cust1 + # move r1-eth0 to VRF r1-bgp-cust1 + + ns = "{}-bgp-cust1".format("r1") + router.net.add_netns(ns) + router.net.set_intf_netns("r1-eth0", ns, up=True) + + # run daemons + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, "{}/zebra.conf".format("r1")), + "--vrfwnetns", + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format("r1")) + ) + + logger.info("Launching BGP and ZEBRA") + # BGP and ZEBRA start without underlying VRF + router.start() + # Starting Hosts and init ExaBGP on each of them + logger.info("starting exaBGP on peer1") + peer_list = tgen.exabgp_peers() + for pname, peer in peer_list.items(): + peer_dir = os.path.join(CWD, pname) + env_file = os.path.join(CWD, "exabgp.env") + logger.info("Running ExaBGP peer") + peer.start(peer_dir, env_file) + logger.info(pname) + + +def teardown_module(module): + tgen = get_topogen() + + # Move interfaces out of vrf namespace and delete the namespace + tgen.net["r1"].reset_intf_netns("r1-eth0") + tgen.net["r1"].delete_netns("r1-bgp-cust1") + + tgen.stop_topology() + + +def test_bgp_vrf_learn(): + "Test daemon learnt VRF context" + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Expected result + output = tgen.gears["r1"].vtysh_cmd("show vrf", isjson=False) + logger.info("output is: {}".format(output)) + + output = tgen.gears["r1"].vtysh_cmd("show bgp vrfs", isjson=False) + logger.info("output is: {}".format(output)) + + +def test_bgp_convergence(): + "Test for BGP topology convergence" + tgen = get_topogen() + + # uncomment if you want to troubleshoot + # tgen.mininet_cli() + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bgp convergence") + + # Expected result + router = tgen.gears["r1"] + if router.has_version("<", "3.0"): + reffile = os.path.join(CWD, "r1/summary20.txt") + else: + reffile = os.path.join(CWD, "r1/summary.txt") + + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show bgp vrf r1-bgp-cust1 summary json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=90, wait=0.5) + assertmsg = "BGP router network did not converge" + assert res is None, assertmsg + + +def test_bgp_vrf_netns(): + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + expect = { + "routerId": "10.0.1.1", + "routes": {}, + } + + for subnet in range(0, 10): + netkey = "10.201.{}.0/24".format(subnet) + expect["routes"][netkey] = [] + peer = {"valid": True} + expect["routes"][netkey].append(peer) + + test_func = functools.partial( + topotest.router_json_cmp, + tgen.gears["r1"], + "show ip bgp vrf r1-bgp-cust1 ipv4 json", + expect, + ) + _, res = topotest.run_and_expect(test_func, None, count=12, wait=0.5) + assertmsg = 'expected routes in "show ip bgp vrf r1-bgp-cust1 ipv4" output' + assert res is None, assertmsg + + +if __name__ == "__main__": + + args = ["-s"] + sys.argv[1:] + ret = pytest.main(args) + + sys.exit(ret) diff --git a/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf b/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf new file mode 100644 index 0000000..03dfbf9 --- /dev/null +++ b/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf @@ -0,0 +1,16 @@ +hostname r1 + +router bgp 99 vrf DONNA + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + import vrf EVA + ! +! +router bgp 99 vrf EVA + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + import vrf DONNA + ! +! diff --git a/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf b/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf new file mode 100644 index 0000000..3503855 --- /dev/null +++ b/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf @@ -0,0 +1,18 @@ +hostname r1 + +int dummy1 + ip address 10.0.0.1/24 + no shut +! +int dummy2 + ip address 10.0.1.1/24 + no shut +! +int dummy3 + ip address 10.0.2.1/24 + no shut +! +int dummy4 + ip address 10.0.3.1/24 + no shut +! diff --git a/tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs b/tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs new file mode 100644 index 0000000..fb67953 --- /dev/null +++ b/tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs @@ -0,0 +1,16 @@ +#!/bin/bash + +ip link add DONNA type vrf table 1001 +ip link add EVA type vrf table 1002 + +ip link add dummy1 type dummy +ip link add dummy2 type dummy +ip link add dummy3 type dummy +ip link add dummy4 type dummy + +ip link set dummy1 master DONNA +ip link set dummy2 master EVA +ip link set dummy3 master DONNA +ip link set dummy4 master EVA + + diff --git a/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py b/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py new file mode 100644 index 0000000..fd7ffff --- /dev/null +++ b/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp-vrf-route-leak-basic.py +# +# Copyright (c) 2018 Cumulus Networks, Inc. +# Donald Sharp +# + +""" +test_bgp-vrf-route-leak-basic.py.py: Test basic vrf route leaking +""" + +import os +import sys +from functools import partial +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + for routern in range(1, 2): + tgen.add_router("r{}".format(routern)) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + # For all registered routers, load the zebra configuration file + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/setup_vrfs".format(CWD)) + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + # tgen.mininet_cli() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_vrf_route_leak(): + logger.info("Ensure that routes are leaked back and forth") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Test DONNA VRF. + expect = { + "10.0.0.0/24": [ + { + "protocol": "connected", + } + ], + "10.0.1.0/24": [ + {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + ], + "10.0.2.0/24": [{"protocol": "connected"}], + "10.0.3.0/24": [ + {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + ], + } + + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf DONNA json", expect + ) + result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result, "BGP VRF DONNA check failed:\n{}".format(diff) + + # Test EVA VRF. + expect = { + "10.0.0.0/24": [ + {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + ], + "10.0.1.0/24": [ + { + "protocol": "connected", + } + ], + "10.0.2.0/24": [ + {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + ], + "10.0.3.0/24": [ + { + "protocol": "connected", + } + ], + } + + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf EVA json", expect + ) + result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result, "BGP VRF EVA check failed:\n{}".format(diff) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/config_timing/r1/staticd.conf b/tests/topotests/config_timing/r1/staticd.conf new file mode 100644 index 0000000..0f9f97c --- /dev/null +++ b/tests/topotests/config_timing/r1/staticd.conf @@ -0,0 +1 @@ +log timestamp precision 3 diff --git a/tests/topotests/config_timing/r1/zebra.conf b/tests/topotests/config_timing/r1/zebra.conf new file mode 100644 index 0000000..b4dc338 --- /dev/null +++ b/tests/topotests/config_timing/r1/zebra.conf @@ -0,0 +1,16 @@ +log timestamp precision 3 + +ip prefix-list ANY permit 0.0.0.0/0 le 32 +ipv6 prefix-list ANY seq 10 permit any + +route-map RM-NONE4 deny 10 + +route-map RM-NONE6 deny 10 + +interface r1-eth0 + ip address 100.0.0.1/24 + ipv6 address 2102::1/64 +exit + +ip protocol static route-map RM-NONE4 +ipv6 protocol static route-map RM-NONE6 diff --git a/tests/topotests/config_timing/test_config_timing.py b/tests/topotests/config_timing/test_config_timing.py new file mode 100644 index 0000000..5c1b972 --- /dev/null +++ b/tests/topotests/config_timing/test_config_timing.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# June 2 2021, Christian Hopps +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2019-2020 by +# Donatas Abraitis +# + +""" +Test the timing of config operations. + +The initial add of 10k routes is used as a baseline for timing and all future +operations are expected to complete in under 2 times that baseline. This is a +lot of slop; however, the pre-batching code some of these operations (e.g., +adding the same set of 10k routes) would take 100 times longer, so the intention +is to catch those types of regressions. +""" + +import datetime +import ipaddress +import math +import os +import sys +import pytest +from lib import topotest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.staticd] + + +def build_topo(tgen): + tgen.add_router("r1") + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, "{}/zebra.conf".format(rname)), + ) + router.load_config( + TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def get_ip_networks(super_prefix, base_count, count): + count_log2 = math.log(base_count, 2) + if count_log2 != int(count_log2): + count_log2 = int(count_log2) + 1 + else: + count_log2 = int(count_log2) + network = ipaddress.ip_network(super_prefix) + return tuple(network.subnets(count_log2))[0:count] + + +def test_static_timing(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def do_config( + base_count, + count, + bad_indices, + base_delta, + d_multiplier, + add=True, + do_ipv6=False, + super_prefix=None, + en_dbg=False, + ): + router_list = tgen.routers() + tot_delta = float(0) + + optype = "adding" if add else "removing" + iptype = "IPv6" if do_ipv6 else "IPv4" + if super_prefix is None: + super_prefix = u"2001::/48" if do_ipv6 else u"10.0.0.0/8" + via = u"lo" + optyped = "added" if add else "removed" + + for rname, router in router_list.items(): + router.logger.info("{} {} static {} routes".format(optype, count, iptype)) + + # Generate config file. + config_file = os.path.join( + router.logdir, rname, "{}-routes-{}.conf".format(iptype.lower(), optype) + ) + with open(config_file, "w") as f: + for i, net in enumerate( + get_ip_networks(super_prefix, base_count, count) + ): + if i in bad_indices: + if add: + f.write("ip route {} {} bad_input\n".format(net, via)) + else: + f.write("no ip route {} {} bad_input\n".format(net, via)) + elif add: + f.write("ip route {} {}\n".format(net, via)) + else: + f.write("no ip route {} {}\n".format(net, via)) + + # Enable debug + if en_dbg: + router.vtysh_cmd("debug northbound callbacks configuration") + + # Load config file. + load_command = 'vtysh -f "{}"'.format(config_file) + tstamp = datetime.datetime.now() + output = router.run(load_command) + delta = (datetime.datetime.now() - tstamp).total_seconds() + tot_delta += delta + + router.logger.debug( + "\nvtysh command => {}\nvtysh output <= {}\nin {}s".format( + load_command, output, delta + ) + ) + + limit_delta = base_delta * d_multiplier + logger.info( + "{} {} {} static routes under {} in {}s (limit: {}s)".format( + optyped, count, iptype.lower(), super_prefix, tot_delta, limit_delta + ) + ) + if limit_delta: + assert tot_delta <= limit_delta + + return tot_delta + + # Number of static routes + router = tgen.gears["r1"] + output = router.net.cmd_legacy("vtysh -h | grep address-sanitizer", warn=False) + if output == "": + logger.info("No Address Sanitizer, generating 10000 routes") + prefix_count = 10000 + else: + logger.info("Address Sanitizer build, only testing 50 routes") + prefix_count = 50 + + prefix_base = [ + [u"10.0.0.0/8", u"11.0.0.0/8"], + [u"2100:1111:2220::/44", u"2100:3333:4440::/44"], + ] + + # This apparently needed to allow for various mgmtd/staticd/zebra connections to form + # which then SLOWS execution down. If we don't include this value then the + # initial, baseline establishing, time is 2 time faster (e.g., 5s instead of 10s), + # but all later runs are slower and fail. + # + # This should be done differently based on actual facts. + topotest.sleep(5) + + bad_indices = [] + for ipv6 in [False, True]: + base_delta = do_config( + prefix_count, + prefix_count, + bad_indices, + 0, + 0, + True, + ipv6, + prefix_base[ipv6][0], + ) + + # Another set of same number of prefixes + do_config( + prefix_count, + prefix_count, + bad_indices, + base_delta, + 3, + True, + ipv6, + prefix_base[ipv6][1], + ) + + # Duplicate config + do_config( + prefix_count, + prefix_count, + bad_indices, + base_delta, + 3, + True, + ipv6, + prefix_base[ipv6][0], + ) + + # Remove 1/2 of duplicate + do_config( + prefix_count, + prefix_count // 2, + bad_indices, + base_delta, + 3, + False, + ipv6, + prefix_base[ipv6][0], + ) + + # Add all back in so 1/2 replicate 1/2 new + do_config( + prefix_count, + prefix_count, + bad_indices, + base_delta, + 3, + True, + ipv6, + prefix_base[ipv6][0], + ) + + # remove all + delta = do_config( + prefix_count, + prefix_count, + bad_indices, + base_delta, + 3, + False, + ipv6, + prefix_base[ipv6][0], + ) + delta += do_config( + prefix_count, + prefix_count, + bad_indices, + base_delta, + 3, + False, + ipv6, + prefix_base[ipv6][1], + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py new file mode 100755 index 0000000..0afebde --- /dev/null +++ b/tests/topotests/conftest.py @@ -0,0 +1,691 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +""" +Topotest conftest.py file. +""" +# pylint: disable=consider-using-f-string + +import contextlib +import glob +import logging +import os +import re +import resource +import subprocess +import sys +import time +from pathlib import Path + +import lib.fixtures +import pytest +from lib.micronet_compat import Mininet +from lib.topogen import diagnose_env, get_topogen +from lib.topolog import get_test_logdir, logger +from lib.topotest import json_cmp_result +from munet import cli +from munet.base import Commander, proc_error +from munet.cleanup import cleanup_current, cleanup_previous +from munet.config import ConfigOptionsProxy +from munet.testing.util import pause_test + +from lib import topolog, topotest + +try: + # Used by munet native tests + from munet.testing.fixtures import event_loop, unet # pylint: disable=all # noqa + + @pytest.fixture(scope="module") + def rundir_module(pytestconfig): + d = os.path.join(pytestconfig.option.rundir, get_test_logdir()) + logging.debug("rundir_module: test module rundir %s", d) + return d + +except (AttributeError, ImportError): + pass + + +# Remove this and use munet version when we move to pytest_asyncio +@contextlib.contextmanager +def chdir(ndir, desc=""): + odir = os.getcwd() + os.chdir(ndir) + if desc: + logging.debug("%s: chdir from %s to %s", desc, odir, ndir) + try: + yield + finally: + if desc: + logging.debug("%s: chdir back from %s to %s", desc, ndir, odir) + os.chdir(odir) + + +@contextlib.contextmanager +def log_handler(basename, logpath): + topolog.logstart(basename, logpath) + try: + yield + finally: + topolog.logfinish(basename, logpath) + + +def pytest_addoption(parser): + """ + Add topology-only option to the topology tester. This option makes pytest + only run the setup_module() to setup the topology without running any tests. + """ + parser.addoption( + "--asan-abort", + action="store_true", + help="Configure address sanitizer to abort process on error", + ) + + parser.addoption( + "--cli-on-error", + action="store_true", + help="Mininet cli on test failure", + ) + + parser.addoption( + "--gdb-breakpoints", + metavar="SYMBOL[,SYMBOL...]", + help="Comma-separated list of functions to set gdb breakpoints on", + ) + + parser.addoption( + "--gdb-daemons", + metavar="DAEMON[,DAEMON...]", + help="Comma-separated list of daemons to spawn gdb on, or 'all'", + ) + + parser.addoption( + "--gdb-routers", + metavar="ROUTER[,ROUTER...]", + help="Comma-separated list of routers to spawn gdb on, or 'all'", + ) + + parser.addoption( + "--logd", + action="append", + metavar="DAEMON[,ROUTER[,...]", + help=( + "Tail-F the DAEMON log file on all or a subset of ROUTERs." + " Option can be given multiple times." + ), + ) + + parser.addoption( + "--memleaks", + action="store_true", + help="Report memstat results as errors", + ) + + parser.addoption( + "--pause", + action="store_true", + help="Pause after each test", + ) + + parser.addoption( + "--pause-at-end", + action="store_true", + help="Pause before taking munet down", + ) + + parser.addoption( + "--pause-on-error", + action="store_true", + help="Do not pause after (disables default when --shell or -vtysh given)", + ) + + parser.addoption( + "--no-pause-on-error", + dest="pause_on_error", + action="store_false", + help="Do not pause after (disables default when --shell or -vtysh given)", + ) + + parser.addoption( + "--pcap", + default="", + metavar="NET[,NET...]", + help="Comma-separated list of networks to capture packets on, or 'all'", + ) + + parser.addoption( + "--perf", + action="append", + metavar="DAEMON[,ROUTER[,...]", + help=( + "Collect performance data from given DAEMON on all or a subset of ROUTERs." + " Option can be given multiple times." + ), + ) + + parser.addoption( + "--perf-options", + metavar="OPTS", + default="-g", + help="Options to pass to `perf record`.", + ) + + rundir_help = "directory for running in and log files" + parser.addini("rundir", rundir_help, default="/tmp/topotests") + parser.addoption("--rundir", metavar="DIR", help=rundir_help) + + parser.addoption( + "--shell", + metavar="ROUTER[,ROUTER...]", + help="Comma-separated list of routers to spawn shell on, or 'all'", + ) + + parser.addoption( + "--shell-on-error", + action="store_true", + help="Spawn shell on all routers on test failure", + ) + + parser.addoption( + "--strace-daemons", + metavar="DAEMON[,DAEMON...]", + help="Comma-separated list of daemons to strace, or 'all'", + ) + + parser.addoption( + "--topology-only", + action="store_true", + default=False, + help="Only set up this topology, don't run tests", + ) + + parser.addoption( + "--valgrind-extra", + action="store_true", + help="Generate suppression file, and enable more precise (slower) valgrind checks", + ) + + parser.addoption( + "--valgrind-memleaks", + action="store_true", + help="Run all daemons under valgrind for memleak detection", + ) + + parser.addoption( + "--vtysh", + metavar="ROUTER[,ROUTER...]", + help="Comma-separated list of routers to spawn vtysh on, or 'all'", + ) + + parser.addoption( + "--vtysh-on-error", + action="store_true", + help="Spawn vtysh on all routers on test failure", + ) + + +def check_for_valgrind_memleaks(): + assert topotest.g_pytest_config.option.valgrind_memleaks + + leaks = [] + tgen = get_topogen() # pylint: disable=redefined-outer-name + latest = [] + existing = [] + if tgen is not None: + logdir = tgen.logdir + if hasattr(tgen, "valgrind_existing_files"): + existing = tgen.valgrind_existing_files + latest = glob.glob(os.path.join(logdir, "*.valgrind.*")) + latest = [x for x in latest if "core" not in x] + + daemons = set() + for vfile in latest: + if vfile in existing: + continue + # do not consider memleaks from parent fork (i.e., owned by root) + if os.stat(vfile).st_uid == 0: + existing.append(vfile) # do not check again + logger.debug("Skipping valgrind file %s owned by root", vfile) + continue + logger.debug("Checking valgrind file %s not owned by root", vfile) + with open(vfile, encoding="ascii") as vf: + vfcontent = vf.read() + match = re.search(r"ERROR SUMMARY: (\d+) errors", vfcontent) + if match: + existing.append(vfile) # have summary don't check again + if match and match.group(1) != "0": + emsg = "{} in {}".format(match.group(1), vfile) + leaks.append(emsg) + daemon = re.match(r".*\.valgrind\.(.*)\.\d+", vfile).group(1) + daemons.add("{}({})".format(daemon, match.group(1))) + + if tgen is not None: + tgen.valgrind_existing_files = existing + + if leaks: + logger.error("valgrind memleaks found:\n\t%s", "\n\t".join(leaks)) + pytest.fail("valgrind memleaks found for daemons: " + " ".join(daemons)) + + +def check_for_memleaks(): + leaks = [] + tgen = get_topogen() # pylint: disable=redefined-outer-name + latest = [] + existing = [] + if tgen is not None: + logdir = tgen.logdir + if hasattr(tgen, "memstat_existing_files"): + existing = tgen.memstat_existing_files + latest = glob.glob(os.path.join(logdir, "*/*.err")) + + daemons = [] + for vfile in latest: + if vfile in existing: + continue + with open(vfile, encoding="ascii") as vf: + vfcontent = vf.read() + num = vfcontent.count("memstats:") + if num: + existing.append(vfile) # have summary don't check again + emsg = "{} types in {}".format(num, vfile) + leaks.append(emsg) + daemon = re.match(r".*test[a-z_A-Z0-9\+]*/(.*)\.err", vfile).group(1) + daemons.append("{}({})".format(daemon, num)) + + if tgen is not None: + tgen.memstat_existing_files = existing + + if leaks: + logger.error("memleaks found:\n\t%s", "\n\t".join(leaks)) + pytest.fail("memleaks found for daemons: " + " ".join(daemons)) + + +def check_for_core_dumps(): + tgen = get_topogen() # pylint: disable=redefined-outer-name + if not tgen: + return + + if not hasattr(tgen, "existing_core_files"): + tgen.existing_core_files = set() + existing = tgen.existing_core_files + + cores = glob.glob(os.path.join(tgen.logdir, "*/*.dmp")) + latest = {x for x in cores if x not in existing} + if latest: + existing |= latest + tgen.existing_core_files = existing + + emsg = "New core[s] found: " + ", ".join(latest) + logger.error(emsg) + pytest.fail(emsg) + + +def check_for_backtraces(): + tgen = get_topogen() # pylint: disable=redefined-outer-name + if not tgen: + return + + if not hasattr(tgen, "existing_backtrace_files"): + tgen.existing_backtrace_files = {} + existing = tgen.existing_backtrace_files + + latest = glob.glob(os.path.join(tgen.logdir, "*/*.log")) + backtraces = [] + for vfile in latest: + with open(vfile, encoding="ascii") as vf: + vfcontent = vf.read() + btcount = vfcontent.count("Backtrace:") + if not btcount: + continue + if vfile not in existing: + existing[vfile] = 0 + if btcount == existing[vfile]: + continue + existing[vfile] = btcount + backtraces.append(vfile) + + if backtraces: + emsg = "New backtraces found in: " + ", ".join(backtraces) + logger.error(emsg) + pytest.fail(emsg) + + +@pytest.fixture(autouse=True, scope="module") +def module_autouse(request): + basename = get_test_logdir(request.node.nodeid, True) + logdir = Path(topotest.g_pytest_config.option.rundir) / basename + logpath = logdir / "exec.log" + + subprocess.check_call("mkdir -p -m 1777 {}".format(logdir), shell=True) + + with log_handler(basename, logpath): + sdir = os.path.dirname(os.path.realpath(request.fspath)) + with chdir(sdir, "module autouse fixture"): + yield + + +@pytest.fixture(autouse=True, scope="module") +def module_check_memtest(request): + yield + if request.config.option.valgrind_memleaks: + if get_topogen() is not None: + check_for_valgrind_memleaks() + if request.config.option.memleaks: + if get_topogen() is not None: + check_for_memleaks() + + +# +# Disable per test function logging as FRR CI system can't handle it. +# +# @pytest.fixture(autouse=True, scope="function") +# def function_autouse(request): +# # For tests we actually use the logdir name as the logfile base +# logbase = get_test_logdir(nodeid=request.node.nodeid, module=False) +# logbase = os.path.join(topotest.g_pytest_config.option.rundir, logbase) +# logpath = Path(logbase) +# path = Path(f"{logpath.parent}/exec-{logpath.name}.log") +# subprocess.check_call("mkdir -p -m 1777 {}".format(logpath.parent), shell=True) +# with log_handler(request.node.nodeid, path): +# yield + + +@pytest.hookimpl(hookwrapper=True) +def pytest_runtest_call(item: pytest.Item) -> None: + "Hook the function that is called to execute the test." + + # For topology only run the CLI then exit + if item.config.option.topology_only: + get_topogen().cli() + pytest.exit("exiting after --topology-only") + + # Let the default pytest_runtest_call execute the test function + yield + + check_for_backtraces() + check_for_core_dumps() + + # Check for leaks if requested + if item.config.option.valgrind_memleaks: + check_for_valgrind_memleaks() + + if item.config.option.memleaks: + check_for_memleaks() + + +def pytest_assertrepr_compare(op, left, right): + """ + Show proper assertion error message for json_cmp results. + """ + del op + + json_result = left + if not isinstance(json_result, json_cmp_result): + json_result = right + if not isinstance(json_result, json_cmp_result): + return None + + return json_result.gen_report() + + +def pytest_configure(config): + """ + Assert that the environment is correctly configured, and get extra config. + """ + topotest.g_pytest_config = ConfigOptionsProxy(config) + + if config.getoption("--collect-only"): + return + + if "PYTEST_XDIST_WORKER" not in os.environ: + os.environ["PYTEST_XDIST_MODE"] = config.getoption("dist", "no") + os.environ["PYTEST_TOPOTEST_WORKER"] = "" + is_xdist = os.environ["PYTEST_XDIST_MODE"] != "no" + is_worker = False + wname = "" + else: + wname = os.environ["PYTEST_XDIST_WORKER"] + os.environ["PYTEST_TOPOTEST_WORKER"] = wname + is_xdist = True + is_worker = True + + resource.setrlimit( + resource.RLIMIT_CORE, (resource.RLIM_INFINITY, resource.RLIM_INFINITY) + ) + # ----------------------------------------------------- + # Set some defaults for the pytest.ini [pytest] section + # --------------------------------------------------- + + rundir = config.option.rundir + if not rundir: + rundir = config.getini("rundir") + if not rundir: + rundir = "/tmp/topotests" + config.option.rundir = rundir + + if not config.getoption("--junitxml"): + config.option.xmlpath = os.path.join(rundir, "topotests.xml") + xmlpath = config.option.xmlpath + + # Save an existing topotest.xml + if os.path.exists(xmlpath): + fmtime = time.localtime(os.path.getmtime(xmlpath)) + suffix = "-" + time.strftime("%Y%m%d%H%M%S", fmtime) + commander = Commander("pytest") + mv_path = commander.get_exec_path("mv") + commander.cmd_status([mv_path, xmlpath, xmlpath + suffix]) + + # Set the log_file (exec) to inside the rundir if not specified + if not config.getoption("--log-file") and not config.getini("log_file"): + config.option.log_file = os.path.join(rundir, "exec.log") + + # Handle pytest-xdist each worker get's it's own top level log file + # `exec-worker-N.log` + if wname: + wname = wname.replace("gw", "worker-") + cpath = Path(config.option.log_file).absolute() + config.option.log_file = f"{cpath.parent}/{cpath.stem}-{wname}{cpath.suffix}" + elif is_xdist: + cpath = Path(config.option.log_file).absolute() + config.option.log_file = f"{cpath.parent}/{cpath.stem}-xdist{cpath.suffix}" + + # Turn on live logging if user specified verbose and the config has a CLI level set + if config.getoption("--verbose") and not is_xdist and not config.getini("log_cli"): + if config.getoption("--log-cli-level", None) is None: + # By setting the CLI option to the ini value it enables log_cli=1 + cli_level = config.getini("log_cli_level") + if cli_level is not None: + config.option.log_cli_level = cli_level + + have_tmux = bool(os.getenv("TMUX", "")) + have_screen = not have_tmux and bool(os.getenv("STY", "")) + have_xterm = not have_tmux and not have_screen and bool(os.getenv("DISPLAY", "")) + have_windows = have_tmux or have_screen or have_xterm + have_windows_pause = have_tmux or have_xterm + xdist_no_windows = is_xdist and not is_worker and not have_windows_pause + + def assert_feature_windows(b, feature): + if b and xdist_no_windows: + pytest.exit( + "{} use requires byobu/TMUX/XTerm under dist {}".format( + feature, os.environ["PYTEST_XDIST_MODE"] + ) + ) + elif b and not is_xdist and not have_windows: + pytest.exit("{} use requires byobu/TMUX/SCREEN/XTerm".format(feature)) + + # + # Check for window capability if given options that require window + # + assert_feature_windows(config.option.gdb_routers, "GDB") + assert_feature_windows(config.option.gdb_daemons, "GDB") + assert_feature_windows(config.option.cli_on_error, "--cli-on-error") + assert_feature_windows(config.option.shell, "--shell") + assert_feature_windows(config.option.shell_on_error, "--shell-on-error") + assert_feature_windows(config.option.vtysh, "--vtysh") + assert_feature_windows(config.option.vtysh_on_error, "--vtysh-on-error") + + if config.option.topology_only and is_xdist: + pytest.exit("Cannot use --topology-only with distributed test mode") + + pytest.exit("Cannot use --topology-only with distributed test mode") + + # Check environment now that we have config + if not diagnose_env(rundir): + pytest.exit("environment has errors, please read the logs in %s" % rundir) + + # slave TOPOTESTS_CHECK_MEMLEAK to memleaks flag + if config.option.memleaks: + if "TOPOTESTS_CHECK_MEMLEAK" not in os.environ: + os.environ["TOPOTESTS_CHECK_MEMLEAK"] = "/dev/null" + else: + if "TOPOTESTS_CHECK_MEMLEAK" in os.environ: + del os.environ["TOPOTESTS_CHECK_MEMLEAK"] + if "TOPOTESTS_CHECK_STDERR" in os.environ: + del os.environ["TOPOTESTS_CHECK_STDERR"] + + +@pytest.fixture(autouse=True, scope="session") +def setup_session_auto(): + # Aligns logs nicely + logging.addLevelName(logging.WARNING, " WARN") + logging.addLevelName(logging.INFO, " INFO") + + if "PYTEST_TOPOTEST_WORKER" not in os.environ: + is_worker = False + elif not os.environ["PYTEST_TOPOTEST_WORKER"]: + is_worker = False + else: + is_worker = True + + logger.debug("Before the run (is_worker: %s)", is_worker) + if not is_worker: + cleanup_previous() + yield + if not is_worker: + cleanup_current() + logger.debug("After the run (is_worker: %s)", is_worker) + + +def pytest_runtest_setup(item): + module = item.parent.module + script_dir = os.path.abspath(os.path.dirname(module.__file__)) + os.environ["PYTEST_TOPOTEST_SCRIPTDIR"] = script_dir + + +def pytest_runtest_makereport(item, call): + "Log all assert messages to default logger with error level" + + pause = bool(item.config.getoption("--pause")) + title = "unset" + + if call.excinfo is None: + error = False + else: + parent = item.parent + modname = parent.module.__name__ + + # Treat skips as non errors, don't pause after + if call.excinfo.typename == "Skipped": + pause = False + error = False + logger.info( + 'test skipped at "{}/{}": {}'.format( + modname, item.name, call.excinfo.value + ) + ) + else: + error = True + # Handle assert failures + parent._previousfailed = item # pylint: disable=W0212 + logger.error( + 'test failed at "{}/{}": {}'.format( + modname, item.name, call.excinfo.value + ) + ) + title = "{}/{}".format(modname, item.name) + + # We want to pause, if requested, on any error not just test cases + # (e.g., call.when == "setup") + if not pause: + pause = item.config.option.pause_on_error or item.config.option.pause + + # (topogen) Set topology error to avoid advancing in the test. + tgen = get_topogen() # pylint: disable=redefined-outer-name + if tgen is not None: + # This will cause topogen to report error on `routers_have_failure`. + tgen.set_error("{}/{}".format(modname, item.name)) + + commander = Commander("pytest") + isatty = sys.stdout.isatty() + error_cmd = None + + if error and item.config.option.vtysh_on_error: + error_cmd = commander.get_exec_path(["vtysh"]) + elif error and item.config.option.shell_on_error: + error_cmd = os.getenv("SHELL", commander.get_exec_path(["bash"])) + + if error_cmd: + is_tmux = bool(os.getenv("TMUX", "")) + is_screen = not is_tmux and bool(os.getenv("STY", "")) + is_xterm = not is_tmux and not is_screen and bool(os.getenv("DISPLAY", "")) + + channel = None + win_info = None + wait_for_channels = [] + wait_for_procs = [] + # Really would like something better than using this global here. + # Not all tests use topogen though so get_topogen() won't work. + for node in Mininet.g_mnet_inst.hosts.values(): + pause = True + + if is_tmux: + channel = ( + "{}-{}".format(os.getpid(), Commander.tmux_wait_gen) + if not isatty + else None + ) + Commander.tmux_wait_gen += 1 + wait_for_channels.append(channel) + + pane_info = node.run_in_window( + error_cmd, + new_window=win_info is None, + background=True, + title="{} ({})".format(title, node.name), + name=title, + tmux_target=win_info, + wait_for=channel, + ) + if is_tmux: + if win_info is None: + win_info = pane_info + elif is_xterm: + assert isinstance(pane_info, subprocess.Popen) + wait_for_procs.append(pane_info) + + # Now wait on any channels + for channel in wait_for_channels: + logger.debug("Waiting on TMUX channel %s", channel) + commander.cmd_raises([commander.get_exec_path("tmux"), "wait", channel]) + for p in wait_for_procs: + logger.debug("Waiting on TMUX xterm process %s", p) + o, e = p.communicate() + if p.wait(): + logger.warning("xterm proc failed: %s:", proc_error(p, o, e)) + + if error and item.config.option.cli_on_error: + # Really would like something better than using this global here. + # Not all tests use topogen though so get_topogen() won't work. + if Mininet.g_mnet_inst: + cli.cli(Mininet.g_mnet_inst, title=title, background=False) + else: + logger.error("Could not launch CLI b/c no mininet exists yet") + + if pause and isatty: + pause_test() + + +# +# Add common fixtures available to all tests as parameters +# + +tgen = pytest.fixture(lib.fixtures.tgen) +topo = pytest.fixture(lib.fixtures.topo) diff --git a/tests/topotests/cspf_topo1/r1/isisd.conf b/tests/topotests/cspf_topo1/r1/isisd.conf new file mode 100644 index 0000000..2dc7329 --- /dev/null +++ b/tests/topotests/cspf_topo1/r1/isisd.conf @@ -0,0 +1,35 @@ +! +hostname r1 +! +interface lo + ip router isis TE + ipv6 router isis TE + isis circuit-type level-2-only + isis passive +! +interface r1-eth0 + ip router isis TE + isis circuit-type level-2-only + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +interface r1-eth1 + ip router isis TE + ipv6 router isis TE + isis circuit-type level-2-only + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +router isis TE + net 49.0000.0000.0000.0001.00 + is-type level-2-only + topology ipv6-unicast + lsp-timers gen-interval 2 refresh-interval 10 max-lifetime 350 + mpls-te on + mpls-te router-address 10.0.255.1 + mpls-te router-address ipv6 2001:db8::1 + mpls-te export +! + diff --git a/tests/topotests/cspf_topo1/r1/sharpd.conf b/tests/topotests/cspf_topo1/r1/sharpd.conf new file mode 100644 index 0000000..465034f --- /dev/null +++ b/tests/topotests/cspf_topo1/r1/sharpd.conf @@ -0,0 +1,2 @@ +! +! diff --git a/tests/topotests/cspf_topo1/r1/zebra.conf b/tests/topotests/cspf_topo1/r1/zebra.conf new file mode 100644 index 0000000..3b615b0 --- /dev/null +++ b/tests/topotests/cspf_topo1/r1/zebra.conf @@ -0,0 +1,28 @@ +! +hostname r1 +! +interface lo + ip address 10.0.255.1/32 + ipv6 address 2001:db8::1/128 +! +interface r1-eth0 + ip address 10.0.0.1/24 + link-params + metric 20 + delay 10000 + max-bw 10e+10 + ava-bw 1.25e+08 + enable + exit-link-params +! +interface r1-eth1 + ip address 10.0.1.1/24 + ipv6 address 2001:db8:1::1:1/64 + link-params + metric 10 + delay 20000 + enable + exit-link-params +! +ip forwarding +! diff --git a/tests/topotests/cspf_topo1/r2/isisd.conf b/tests/topotests/cspf_topo1/r2/isisd.conf new file mode 100644 index 0000000..59bb3be --- /dev/null +++ b/tests/topotests/cspf_topo1/r2/isisd.conf @@ -0,0 +1,50 @@ +! +hostname r2 +! +! debug isis te-events +! +interface lo + ip router isis TE + ipv6 router isis TE + isis circuit-type level-2-only + isis passive +! +interface r2-eth0 + ip router isis TE + isis circuit-type level-2-only + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +interface r2-eth1 + ip router isis TE + ipv6 router isis TE + isis circuit-type level-2-only + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +interface r2-eth2 + ip router isis TE + ipv6 router isis TE + isis circuit-type level-2-only + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +interface r2-eth3 + ip router isis TE + isis circuit-type level-2-only + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +router isis TE + net 49.0000.0000.0000.0002.00 + is-type level-2-only + topology ipv6-unicast + lsp-timers gen-interval 2 refresh-interval 10 max-lifetime 350 + mpls-te on + mpls-te router-address 10.0.255.2 + mpls-te router-address ipv6 2001:db8::2 +! diff --git a/tests/topotests/cspf_topo1/r2/zebra.conf b/tests/topotests/cspf_topo1/r2/zebra.conf new file mode 100644 index 0000000..55d9563 --- /dev/null +++ b/tests/topotests/cspf_topo1/r2/zebra.conf @@ -0,0 +1,46 @@ +! +hostname r2 +! +interface lo + ip address 10.0.255.2/32 + ipv6 address 2001:db8::2/128 +! +interface r2-eth0 + ip address 10.0.0.2/24 + link-params + metric 20 + delay 10000 + enable + exit-link-params +! +interface r2-eth1 + ip address 10.0.1.2/24 + ipv6 address 2001:db8:1::1:2/64 + link-params + metric 10 + delay 20000 + enable + exit-link-params +! +interface r2-eth2 + ip address 10.0.3.2/24 + ipv6 address 2001:db8:3::3:2/64 + link-params + metric 40 + delay 40000 + enable + exit-link-params +! +interface r2-eth3 + ip address 10.0.4.2/24 + ipv6 address 2001:db8:4::4:2/64 + link-params + metric 25 + delay 25000 + max-bw 10e+10 + use-bw 1.25e+8 + enable + exit-link-params +! +ip forwarding +! diff --git a/tests/topotests/cspf_topo1/r3/isisd.conf b/tests/topotests/cspf_topo1/r3/isisd.conf new file mode 100644 index 0000000..7d3e4de --- /dev/null +++ b/tests/topotests/cspf_topo1/r3/isisd.conf @@ -0,0 +1,36 @@ +! +hostname r3 +! +! debug isis te-events +! +interface lo + ip router isis TE + ipv6 router isis TE + isis circuit-type level-2-only + isis passive +! +interface r3-eth0 + ip router isis TE + ipv6 router isis TE + isis circuit-type level-2-only + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +interface r3-eth1 + ipv6 router isis TE + isis circuit-type level-2-only + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +! +router isis TE + net 49.0000.0000.0000.0003.00 + is-type level-2-only + topology ipv6-unicast + lsp-timers gen-interval 2 refresh-interval 10 max-lifetime 350 + mpls-te on + mpls-te router-address 10.0.255.3 + mpls-te router-address ipv6 2001:db8::3 +! diff --git a/tests/topotests/cspf_topo1/r3/zebra.conf b/tests/topotests/cspf_topo1/r3/zebra.conf new file mode 100644 index 0000000..29a4c51 --- /dev/null +++ b/tests/topotests/cspf_topo1/r3/zebra.conf @@ -0,0 +1,27 @@ +! +hostname r3 +! +interface lo + ip address 10.0.255.3/32 + ipv6 address 2001:db8::3/128 +! +interface r3-eth0 + ip address 10.0.3.3/24 + ipv6 address 2001:db8:3::3:3/64 + link-params + metric 25 + delay 25000 + enable + admin-grp 0x20 + exit-link-params +! +interface r3-eth1 + ipv6 address 2001:db8:5::4:3/64 + link-params + enable + metric 10 + delay 10000 + exit-link-params +! +ip forwarding +! diff --git a/tests/topotests/cspf_topo1/r4/isisd.conf b/tests/topotests/cspf_topo1/r4/isisd.conf new file mode 100644 index 0000000..674b738 --- /dev/null +++ b/tests/topotests/cspf_topo1/r4/isisd.conf @@ -0,0 +1,42 @@ +! +hostname r4 +! +! debug isis te-events +! debug isis sr-events +! debug isis lsp-gen +! +interface lo + ip router isis TE + ipv6 router isis TE + isis circuit-type level-2-only + isis passive +! +interface r4-eth0 + ip router isis TE + isis circuit-type level-2-only + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +interface r4-eth1 + ipv6 router isis TE + isis circuit-type level-2-only + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +! +router isis TE + net 49.0000.0000.0000.0004.00 + is-type level-2-only + topology ipv6-unicast + lsp-timers gen-interval 2 refresh-interval 10 max-lifetime 350 + mpls-te on + mpls-te router-address 10.0.255.4 + mpls-te router-address ipv6 2001:db8::4 + segment-routing on + segment-routing global-block 10000 19999 local-block 5000 5999 + segment-routing node-msd 12 + segment-routing prefix 10.0.255.4/32 index 400 no-php-flag + segment-routing prefix 2001:db8:ffff::4/128 index 1400 no-php-flag +! diff --git a/tests/topotests/cspf_topo1/r4/zebra.conf b/tests/topotests/cspf_topo1/r4/zebra.conf new file mode 100644 index 0000000..bf5306d --- /dev/null +++ b/tests/topotests/cspf_topo1/r4/zebra.conf @@ -0,0 +1,26 @@ +! +hostname r4 +! +interface lo + ip address 10.0.255.4/32 + ipv6 address 2001:db8::4/128 +! +interface r4-eth0 + ip address 10.0.4.4/24 + ipv6 address 2001:db8:4::2:4/64 + link-params + metric 40 + delay 40000 + enable + exit-link-params +! +interface r4-eth1 + ipv6 address 2001:db8:5::3:4/64 + link-params + metric 10 + delay 10000 + enable + exit-link-params +! +ip forwarding +! diff --git a/tests/topotests/cspf_topo1/reference/cspf-failed-dst.txt b/tests/topotests/cspf_topo1/reference/cspf-failed-dst.txt new file mode 100644 index 0000000..df792ce --- /dev/null +++ b/tests/topotests/cspf_topo1/reference/cspf-failed-dst.txt @@ -0,0 +1 @@ +Path computation failed: 2 diff --git a/tests/topotests/cspf_topo1/reference/cspf-failed-same.txt b/tests/topotests/cspf_topo1/reference/cspf-failed-same.txt new file mode 100644 index 0000000..48f6fd9 --- /dev/null +++ b/tests/topotests/cspf_topo1/reference/cspf-failed-same.txt @@ -0,0 +1 @@ +Path computation failed: 3 diff --git a/tests/topotests/cspf_topo1/reference/cspf-failed-src.txt b/tests/topotests/cspf_topo1/reference/cspf-failed-src.txt new file mode 100644 index 0000000..62d00cc --- /dev/null +++ b/tests/topotests/cspf_topo1/reference/cspf-failed-src.txt @@ -0,0 +1 @@ +Path computation failed: 1 diff --git a/tests/topotests/cspf_topo1/reference/cspf-failed.txt b/tests/topotests/cspf_topo1/reference/cspf-failed.txt new file mode 100644 index 0000000..de53a93 --- /dev/null +++ b/tests/topotests/cspf_topo1/reference/cspf-failed.txt @@ -0,0 +1 @@ +Path computation failed: 0 diff --git a/tests/topotests/cspf_topo1/reference/cspf-ipv4-delay.txt b/tests/topotests/cspf_topo1/reference/cspf-ipv4-delay.txt new file mode 100644 index 0000000..2cc0428 --- /dev/null +++ b/tests/topotests/cspf_topo1/reference/cspf-ipv4-delay.txt @@ -0,0 +1,3 @@ +Path computation success + Cost: 35000 + Edges: 10.0.0.2 10.0.4.4 diff --git a/tests/topotests/cspf_topo1/reference/cspf-ipv4-metric.txt b/tests/topotests/cspf_topo1/reference/cspf-ipv4-metric.txt new file mode 100644 index 0000000..060a39c --- /dev/null +++ b/tests/topotests/cspf_topo1/reference/cspf-ipv4-metric.txt @@ -0,0 +1,3 @@ +Path computation success + Cost: 20 + Edges: 10.0.0.2 10.0.4.4 diff --git a/tests/topotests/cspf_topo1/reference/cspf-ipv4-te-metric.txt b/tests/topotests/cspf_topo1/reference/cspf-ipv4-te-metric.txt new file mode 100644 index 0000000..6d98306 --- /dev/null +++ b/tests/topotests/cspf_topo1/reference/cspf-ipv4-te-metric.txt @@ -0,0 +1,3 @@ +Path computation success + Cost: 35 + Edges: 10.0.1.2 10.0.4.4 diff --git a/tests/topotests/cspf_topo1/reference/cspf-ipv6-delay.txt b/tests/topotests/cspf_topo1/reference/cspf-ipv6-delay.txt new file mode 100644 index 0000000..b65869b --- /dev/null +++ b/tests/topotests/cspf_topo1/reference/cspf-ipv6-delay.txt @@ -0,0 +1,3 @@ +Path computation success + Cost: 70000 + Edges: 2001:db8:1::1:2 2001:db8:3::3:3 2001:db8:5::3:4 diff --git a/tests/topotests/cspf_topo1/reference/cspf-ipv6-metric.txt b/tests/topotests/cspf_topo1/reference/cspf-ipv6-metric.txt new file mode 100644 index 0000000..5acbb74 --- /dev/null +++ b/tests/topotests/cspf_topo1/reference/cspf-ipv6-metric.txt @@ -0,0 +1,3 @@ +Path computation success + Cost: 30 + Edges: 2001:db8:1::1:2 2001:db8:3::3:3 2001:db8:5::3:4 diff --git a/tests/topotests/cspf_topo1/reference/cspf-ipv6-te-metric.txt b/tests/topotests/cspf_topo1/reference/cspf-ipv6-te-metric.txt new file mode 100644 index 0000000..2290a04 --- /dev/null +++ b/tests/topotests/cspf_topo1/reference/cspf-ipv6-te-metric.txt @@ -0,0 +1,3 @@ +Path computation success + Cost: 60 + Edges: 2001:db8:1::1:2 2001:db8:3::3:3 2001:db8:5::3:4 diff --git a/tests/topotests/cspf_topo1/reference/sharp-ted.json b/tests/topotests/cspf_topo1/reference/sharp-ted.json new file mode 100644 index 0000000..359b655 --- /dev/null +++ b/tests/topotests/cspf_topo1/reference/sharp-ted.json @@ -0,0 +1,858 @@ +{ + "ted":{ + "name":"Sharp", + "key":1, + "verticesCount":4, + "edgesCount":14, + "subnetsCount":22, + "vertices":[ + { + "vertex-id":1, + "status":"Sync", + "origin":"ISIS_L2", + "name":"r1", + "router-id":"10.0.255.1", + "router-id-v6":"2001:db8::1" + }, + { + "vertex-id":2, + "status":"Sync", + "origin":"ISIS_L2", + "name":"r2", + "router-id":"10.0.255.2", + "router-id-v6":"2001:db8::2" + }, + { + "vertex-id":3, + "status":"Sync", + "origin":"ISIS_L2", + "name":"r3", + "router-id":"10.0.255.3", + "router-id-v6":"2001:db8::3" + }, + { + "vertex-id":4, + "status":"Sync", + "origin":"ISIS_L2", + "name":"r4", + "router-id":"10.0.255.4", + "router-id-v6":"2001:db8::4", + "segment-routing":{ + "srgb-size":10000, + "srgb-lower":10000, + "algorithms":[ + { + "0":"SPF" + } + ], + "srlb-size":1000, + "srlb-lower":5000, + "msd":12 + } + } + ], + "edges":[ + { + "edge-id":"2001:db8:1::1:1", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0001", + "local-vertex-id":1, + "remote-vertex-id":2, + "metric":10, + "edge-attributes":{ + "te-metric":10, + "local-address-v6":"2001:db8:1::1:1", + "remote-address-v6":"2001:db8:1::1:2", + "max-link-bandwidth":1250000, + "max-resv-link-bandwidth":1250000, + "unreserved-bandwidth":[ + { + "class-type-0":1250000 + }, + { + "class-type-1":1250000 + }, + { + "class-type-2":1250000 + }, + { + "class-type-3":1250000 + }, + { + "class-type-4":1250000 + }, + { + "class-type-5":1250000 + }, + { + "class-type-6":1250000 + }, + { + "class-type-7":1250000 + } + ], + "delay":20000 + } + }, + { + "edge-id":"2001:db8:1::1:2", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0002", + "local-vertex-id":2, + "remote-vertex-id":1, + "metric":10, + "edge-attributes":{ + "te-metric":10, + "local-address-v6":"2001:db8:1::1:2", + "remote-address-v6":"2001:db8:1::1:1", + "max-link-bandwidth":1250000, + "max-resv-link-bandwidth":1250000, + "unreserved-bandwidth":[ + { + "class-type-0":1250000 + }, + { + "class-type-1":1250000 + }, + { + "class-type-2":1250000 + }, + { + "class-type-3":1250000 + }, + { + "class-type-4":1250000 + }, + { + "class-type-5":1250000 + }, + { + "class-type-6":1250000 + }, + { + "class-type-7":1250000 + } + ], + "delay":20000 + } + }, + { + "edge-id":"2001:db8:3::3:2", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0002", + "local-vertex-id":2, + "remote-vertex-id":3, + "metric":10, + "edge-attributes":{ + "te-metric":40, + "local-address-v6":"2001:db8:3::3:2", + "remote-address-v6":"2001:db8:3::3:3", + "max-link-bandwidth":1250000, + "max-resv-link-bandwidth":1250000, + "unreserved-bandwidth":[ + { + "class-type-0":1250000 + }, + { + "class-type-1":1250000 + }, + { + "class-type-2":1250000 + }, + { + "class-type-3":1250000 + }, + { + "class-type-4":1250000 + }, + { + "class-type-5":1250000 + }, + { + "class-type-6":1250000 + }, + { + "class-type-7":1250000 + } + ], + "delay":40000 + } + }, + { + "edge-id":"2001:db8:3::3:3", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0003", + "local-vertex-id":3, + "remote-vertex-id":2, + "metric":10, + "edge-attributes":{ + "te-metric":25, + "admin-group":32, + "local-address-v6":"2001:db8:3::3:3", + "remote-address-v6":"2001:db8:3::3:2", + "max-link-bandwidth":1250000, + "max-resv-link-bandwidth":1250000, + "unreserved-bandwidth":[ + { + "class-type-0":1250000 + }, + { + "class-type-1":1250000 + }, + { + "class-type-2":1250000 + }, + { + "class-type-3":1250000 + }, + { + "class-type-4":1250000 + }, + { + "class-type-5":1250000 + }, + { + "class-type-6":1250000 + }, + { + "class-type-7":1250000 + } + ], + "delay":25000 + } + }, + { + "edge-id":"2001:db8:5::3:4", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0004", + "local-vertex-id":4, + "remote-vertex-id":3, + "metric":10, + "edge-attributes":{ + "te-metric":10, + "local-address-v6":"2001:db8:5::3:4", + "remote-address-v6":"2001:db8:5::4:3", + "max-link-bandwidth":1250000, + "max-resv-link-bandwidth":1250000, + "unreserved-bandwidth":[ + { + "class-type-0":1250000 + }, + { + "class-type-1":1250000 + }, + { + "class-type-2":1250000 + }, + { + "class-type-3":1250000 + }, + { + "class-type-4":1250000 + }, + { + "class-type-5":1250000 + }, + { + "class-type-6":1250000 + }, + { + "class-type-7":1250000 + } + ], + "delay":10000 + }, + "segment-routing":[ + { + "flags":"0xb0", + "weight":0 + } + ] + }, + { + "edge-id":"2001:db8:5::4:3", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0003", + "local-vertex-id":3, + "remote-vertex-id":4, + "metric":10, + "edge-attributes":{ + "te-metric":10, + "local-address-v6":"2001:db8:5::4:3", + "remote-address-v6":"2001:db8:5::3:4", + "max-link-bandwidth":1250000, + "max-resv-link-bandwidth":1250000, + "unreserved-bandwidth":[ + { + "class-type-0":1250000 + }, + { + "class-type-1":1250000 + }, + { + "class-type-2":1250000 + }, + { + "class-type-3":1250000 + }, + { + "class-type-4":1250000 + }, + { + "class-type-5":1250000 + }, + { + "class-type-6":1250000 + }, + { + "class-type-7":1250000 + } + ], + "delay":10000 + } + }, + { + "edge-id":"10.0.0.1", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0001", + "local-vertex-id":1, + "remote-vertex-id":2, + "metric":10, + "edge-attributes":{ + "te-metric":20, + "local-address":"10.0.0.1", + "remote-address":"10.0.0.2", + "max-link-bandwidth":99999997952, + "max-resv-link-bandwidth":1250000, + "unreserved-bandwidth":[ + { + "class-type-0":1250000 + }, + { + "class-type-1":1250000 + }, + { + "class-type-2":1250000 + }, + { + "class-type-3":1250000 + }, + { + "class-type-4":1250000 + }, + { + "class-type-5":1250000 + }, + { + "class-type-6":1250000 + }, + { + "class-type-7":1250000 + } + ], + "delay":10000, + "available-bandwidth":125000000.0 + } + }, + { + "edge-id":"10.0.0.2", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0002", + "local-vertex-id":2, + "remote-vertex-id":1, + "metric":10, + "edge-attributes":{ + "te-metric":20, + "local-address":"10.0.0.2", + "remote-address":"10.0.0.1", + "max-link-bandwidth":1250000, + "max-resv-link-bandwidth":1250000, + "unreserved-bandwidth":[ + { + "class-type-0":1250000 + }, + { + "class-type-1":1250000 + }, + { + "class-type-2":1250000 + }, + { + "class-type-3":1250000 + }, + { + "class-type-4":1250000 + }, + { + "class-type-5":1250000 + }, + { + "class-type-6":1250000 + }, + { + "class-type-7":1250000 + } + ], + "delay":10000 + } + }, + { + "edge-id":"10.0.1.1", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0001", + "local-vertex-id":1, + "remote-vertex-id":2, + "metric":10, + "edge-attributes":{ + "te-metric":10, + "local-address":"10.0.1.1", + "remote-address":"10.0.1.2", + "max-link-bandwidth":1250000, + "max-resv-link-bandwidth":1250000, + "unreserved-bandwidth":[ + { + "class-type-0":1250000 + }, + { + "class-type-1":1250000 + }, + { + "class-type-2":1250000 + }, + { + "class-type-3":1250000 + }, + { + "class-type-4":1250000 + }, + { + "class-type-5":1250000 + }, + { + "class-type-6":1250000 + }, + { + "class-type-7":1250000 + } + ], + "delay":20000 + } + }, + { + "edge-id":"10.0.1.2", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0002", + "local-vertex-id":2, + "remote-vertex-id":1, + "metric":10, + "edge-attributes":{ + "te-metric":10, + "local-address":"10.0.1.2", + "remote-address":"10.0.1.1", + "max-link-bandwidth":1250000, + "max-resv-link-bandwidth":1250000, + "unreserved-bandwidth":[ + { + "class-type-0":1250000 + }, + { + "class-type-1":1250000 + }, + { + "class-type-2":1250000 + }, + { + "class-type-3":1250000 + }, + { + "class-type-4":1250000 + }, + { + "class-type-5":1250000 + }, + { + "class-type-6":1250000 + }, + { + "class-type-7":1250000 + } + ], + "delay":20000 + } + }, + { + "edge-id":"10.0.3.2", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0002", + "local-vertex-id":2, + "remote-vertex-id":3, + "metric":10, + "edge-attributes":{ + "te-metric":40, + "local-address":"10.0.3.2", + "remote-address":"10.0.3.3", + "max-link-bandwidth":1250000, + "max-resv-link-bandwidth":1250000, + "unreserved-bandwidth":[ + { + "class-type-0":1250000 + }, + { + "class-type-1":1250000 + }, + { + "class-type-2":1250000 + }, + { + "class-type-3":1250000 + }, + { + "class-type-4":1250000 + }, + { + "class-type-5":1250000 + }, + { + "class-type-6":1250000 + }, + { + "class-type-7":1250000 + } + ], + "delay":40000 + } + }, + { + "edge-id":"10.0.3.3", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0003", + "local-vertex-id":3, + "remote-vertex-id":2, + "metric":10, + "edge-attributes":{ + "te-metric":25, + "admin-group":32, + "local-address":"10.0.3.3", + "remote-address":"10.0.3.2", + "max-link-bandwidth":1250000, + "max-resv-link-bandwidth":1250000, + "unreserved-bandwidth":[ + { + "class-type-0":1250000 + }, + { + "class-type-1":1250000 + }, + { + "class-type-2":1250000 + }, + { + "class-type-3":1250000 + }, + { + "class-type-4":1250000 + }, + { + "class-type-5":1250000 + }, + { + "class-type-6":1250000 + }, + { + "class-type-7":1250000 + } + ], + "delay":25000 + } + }, + { + "edge-id":"10.0.4.2", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0002", + "local-vertex-id":2, + "remote-vertex-id":4, + "metric":10, + "edge-attributes":{ + "te-metric":25, + "local-address":"10.0.4.2", + "remote-address":"10.0.4.4", + "max-link-bandwidth":99999997952, + "max-resv-link-bandwidth":1250000, + "unreserved-bandwidth":[ + { + "class-type-0":1250000 + }, + { + "class-type-1":1250000 + }, + { + "class-type-2":1250000 + }, + { + "class-type-3":1250000 + }, + { + "class-type-4":1250000 + }, + { + "class-type-5":1250000 + }, + { + "class-type-6":1250000 + }, + { + "class-type-7":1250000 + } + ], + "delay":25000, + "utilized-bandwidth":125000000.0 + } + }, + { + "edge-id":"10.0.4.4", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0004", + "local-vertex-id":4, + "remote-vertex-id":2, + "metric":10, + "edge-attributes":{ + "te-metric":40, + "local-address":"10.0.4.4", + "remote-address":"10.0.4.2", + "max-link-bandwidth":1250000, + "max-resv-link-bandwidth":1250000, + "unreserved-bandwidth":[ + { + "class-type-0":1250000 + }, + { + "class-type-1":1250000 + }, + { + "class-type-2":1250000 + }, + { + "class-type-3":1250000 + }, + { + "class-type-4":1250000 + }, + { + "class-type-5":1250000 + }, + { + "class-type-6":1250000 + }, + { + "class-type-7":1250000 + } + ], + "delay":40000 + }, + "segment-routing":[ + { + "flags":"0x30", + "weight":0 + } + ] + } + ], + "subnets":[ + { + "subnet-id":"10.0.0.1/24", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0001", + "vertex-id":1, + "metric":10 + }, + { + "subnet-id":"10.0.0.2/24", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0002", + "vertex-id":2, + "metric":10 + }, + { + "subnet-id":"10.0.1.1/24", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0001", + "vertex-id":1, + "metric":10 + }, + { + "subnet-id":"10.0.1.2/24", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0002", + "vertex-id":2, + "metric":10 + }, + { + "subnet-id":"10.0.3.2/24", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0002", + "vertex-id":2, + "metric":10 + }, + { + "subnet-id":"10.0.3.3/24", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0003", + "vertex-id":3, + "metric":10 + }, + { + "subnet-id":"10.0.4.2/24", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0002", + "vertex-id":2, + "metric":10 + }, + { + "subnet-id":"10.0.4.4/24", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0004", + "vertex-id":4, + "metric":10 + }, + { + "subnet-id":"10.0.255.1/32", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0001", + "vertex-id":1, + "metric":10 + }, + { + "subnet-id":"10.0.255.2/32", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0002", + "vertex-id":2, + "metric":10 + }, + { + "subnet-id":"10.0.255.3/32", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0003", + "vertex-id":3, + "metric":10 + }, + { + "subnet-id":"10.0.255.4/32", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0004", + "vertex-id":4, + "metric":10, + "segment-routing":{ + "pref-sid":400, + "algo":0, + "flags":"0x60" + } + }, + { + "subnet-id":"2001:db8:1::1:1/64", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0001", + "vertex-id":1, + "metric":10 + }, + { + "subnet-id":"2001:db8:1::1:2/64", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0002", + "vertex-id":2, + "metric":10 + }, + { + "subnet-id":"2001:db8:3::3:2/64", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0002", + "vertex-id":2, + "metric":10 + }, + { + "subnet-id":"2001:db8:3::3:3/64", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0003", + "vertex-id":3, + "metric":10 + }, + { + "subnet-id":"2001:db8:5::3:4/64", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0004", + "vertex-id":4, + "metric":10 + }, + { + "subnet-id":"2001:db8:5::4:3/64", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0003", + "vertex-id":3, + "metric":10 + }, + { + "subnet-id":"2001:db8::1/128", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0001", + "vertex-id":1, + "metric":10 + }, + { + "subnet-id":"2001:db8::2/128", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0002", + "vertex-id":2, + "metric":10 + }, + { + "subnet-id":"2001:db8::3/128", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0003", + "vertex-id":3, + "metric":10 + }, + { + "subnet-id":"2001:db8::4/128", + "status":"Sync", + "origin":"ISIS_L2", + "advertised-router":"0000.0000.0004", + "vertex-id":4, + "metric":10 + } + ] + } +} diff --git a/tests/topotests/cspf_topo1/test_cspf_topo1.py b/tests/topotests/cspf_topo1/test_cspf_topo1.py new file mode 100644 index 0000000..072a2dd --- /dev/null +++ b/tests/topotests/cspf_topo1/test_cspf_topo1.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_cspf_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2022 by Orange +# Author: Olivier Dugeon +# + +""" +test_cspf_topo1.py: Test the FRR Constraint Shortest Path First algorithm. + + +------------+ + | | + | R1 | + | 10.0.225.1 | + | | + +------------+ + r1-eth0| |r1-eth1 + | | + 10.0.0.0/24| |10.0.1.0/24 + | |2001:db8:1:/64 + | | + r2-eth0| |r2-eth1 + +------------+ +------------+ + | | | | + | R2 |r2-eth2 r3-eth0| R3 | + | 10.0.255.2 +------------------+ 10.0.255.3 | + | | 10.0.3.0/24 | | + +------------+ 2001:db8:3:/64 +------+-----+ + r2-eth3| r3-eth1| + | | + 10.0.4.0/24| | + | | + | | + r4-eth0| 2001:db8:5:/64| + +------------+ | + | | | + | R4 |r4-eth1 | + | 10.0.255.4 +-------------------------+ + | | + +------------+ + +""" + +import os +import sys +import json +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 + +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# and Finally pytest +import pytest + +pytestmark = [pytest.mark.isisd] + + +def build_topo(tgen): + "Build function" + + # Create 4 routers + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + # Interconect router 1 and 2 with 2 links + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # Interconect router 3 and 2 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r2"]) + + # Interconect router 4 and 2 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["r2"]) + + # Interconnect router 3 and 4 + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + "Sets up the pytest environment" + + logger.info("\n\n---- Starting CSPF tests ----\n") + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + if rname == "r1": + router.load_config( + TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format("r1")) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(): + "Teardown the pytest environment" + + tgen = get_topogen() + tgen.stop_topology() + + logger.info("\n\n---- CSPF tests End ----\n") + + +def compare_ted_json_output(tgen, rname, fileref): + "Compare TED JSON output" + + logger.info('Comparing router "%s" TED output', rname) + + filename = "{}/reference/{}".format(CWD, fileref) + expected = json.loads(open(filename).read()) + command = "show sharp ted json" + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = '"{}" TED JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +def compare_cspf_output(tgen, rname, fileref, src, dst, cost, bw=""): + "Compare CSPF output" + + logger.info('Comparing router "%s" CSPF output', rname) + + filename = "{}/reference/{}".format(CWD, fileref) + expected = open(filename).read() + command = "show sharp cspf source {} destination {} {} {}".format( + src, dst, cost, bw + ) + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial( + topotest.router_output_cmp, tgen.gears[rname], command, expected + ) + result, diff = topotest.run_and_expect(test_func, "", count=5, wait=2) + assert result, "CSPF output mismatches the expected result on {}:\n{}".format( + rname, diff + ) + + +def setup_testcase(msg): + "Setup test case" + + logger.info(msg) + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + return tgen + + +# Note that all routers must discover the same Network Topology, so the same TED. + + +def test_step1(): + "Step1: Check initial topology" + + tgen = setup_testcase("Step1: test initial IS-IS TE Data Base import") + tgen.net["r1"].cmd('vtysh -c "sharp import-te"') + + compare_ted_json_output(tgen, "r1", "sharp-ted.json") + + +def test_step2(): + "Step2: Test CSPF from r1 to r4 for IPv4 with various metric" + + tgen = setup_testcase("Step2: CSPF(r1, r4, IPv4)") + + compare_cspf_output( + tgen, "r1", "cspf-ipv4-metric.txt", "10.0.0.1", "10.0.255.4", "metric 50" + ) + compare_cspf_output( + tgen, "r1", "cspf-ipv4-te-metric.txt", "10.0.255.1", "10.0.4.4", "te-metric 50" + ) + compare_cspf_output( + tgen, "r1", "cspf-ipv4-delay.txt", "10.0.255.1", "10.0.255.4", "delay 50000" + ) + compare_cspf_output( + tgen, + "r1", + "cspf-ipv4-delay.txt", + "10.0.255.1", + "10.0.255.4", + "delay 50000", + "rsv 7 1000000", + ) + + +def test_step3(): + "Step3: Test CSPF from r1 to r4 for IPv6 with various metric" + + tgen = setup_testcase("Step2: CSPF(r1, r4, IPv6)") + + compare_cspf_output( + tgen, + "r1", + "cspf-ipv6-metric.txt", + "2001:db8:1::1:1", + "2001:db8::4", + "metric 50", + ) + compare_cspf_output( + tgen, + "r1", + "cspf-ipv6-te-metric.txt", + "2001:db8::1", + "2001:db8:5::3:4", + "te-metric 80", + ) + compare_cspf_output( + tgen, "r1", "cspf-ipv6-delay.txt", "2001:db8::1", "2001:db8::4", "delay 80000" + ) + compare_cspf_output( + tgen, + "r1", + "cspf-ipv6-delay.txt", + "2001:db8::1", + "2001:db8::4", + "delay 80000", + "rsv 7 1000000", + ) + + +def test_step4(): + "Step4: Test CSPF from r1 to r4 with no possible path" + + tgen = setup_testcase("Step2: CSPF(r1, r4, failure)") + + compare_cspf_output( + tgen, "r1", "cspf-failed.txt", "10.0.255.1", "10.0.255.4", "metric 10" + ) + compare_cspf_output( + tgen, "r1", "cspf-failed.txt", "2001:db8::1", "2001:db8::4", "te-metric 50" + ) + compare_cspf_output( + tgen, "r1", "cspf-failed.txt", "10.0.255.1", "10.0.255.4", "delay 5000" + ) + compare_cspf_output( + tgen, + "r1", + "cspf-failed.txt", + "2001:db8::1", + "2001:db8::4", + "delay 80000", + "rsv 7 10000000", + ) + compare_cspf_output( + tgen, "r1", "cspf-failed-src.txt", "10.0.0.3", "10.0.255.4", "metric 10" + ) + compare_cspf_output( + tgen, "r1", "cspf-failed-dst.txt", "10.0.0.1", "10.0.4.40", "metric 10" + ) + compare_cspf_output( + tgen, "r1", "cspf-failed-same.txt", "10.0.0.1", "10.0.0.1", "metric 10" + ) + + +def test_memory_leak(): + "Run the memory leak test and report results." + + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/docker/README.md b/tests/topotests/docker/README.md new file mode 100644 index 0000000..2b40994 --- /dev/null +++ b/tests/topotests/docker/README.md @@ -0,0 +1,72 @@ +# Topotests in Docker + +## Quickstart + +If you have Docker installed, you can run the topotests in Docker. +The easiest way to do this, is to use the make targets from this +repository. + +Your current user needs to have access to the Docker daemon. Alternatively +you can run these commands as root. + +```console +make topotests +``` + +This command will pull the most recent topotests image from dockerhub, compile FRR inside +of it, and run the topotests. + +## Advanced Usage + +Internally, the topotests make target uses a shell script to pull the image and spawn the docker +container. + +There are several environment variables which can be used to modify the behavior +of the script, these can be listed by calling it with `-h`: + +```console +./tests/topotests/docker/frr-topotests.sh -h +``` + +For example, a volume is used to cache build artifacts between multiple runs +of the image. If you need to force a complete recompile, you can set `TOPOTEST_CLEAN`: + +```console +TOPOTEST_CLEAN=1 ./tests/topotests/docker/frr-topotests.sh +``` + +By default, `frr-topotests.sh` will build frr and run pytest. If you append +arguments and the first one starts with `/` or `./`, they will replace the call to +pytest. If the appended arguments do not match this patttern, they will be provided to +pytest as arguments. + +So, to run a specific test with more verbose logging: + +```console +./tests/topotests/docker/frr-topotests.sh -vv -s all-protocol-startup/test_all_protocol_startup.py +``` + +And to compile FRR but drop into a shell instead of running pytest: + +```console +./tests/topotests/docker/frr-topotests.sh /bin/bash +``` + +## Development + +The docker image just includes all the components to run the topotests, but not the topotests +themselves. So if you just want to write tests and don't want to make changes to the environment +provided by the docker image. You don't need to build your own docker image if you do not want to. + +When developing new tests, there is one caveat though: The startup script of the container will +run a `git-clean` on its copy of the FRR tree to avoid any pollution of the container with build +artefacts from the host. This will also result in your newly written tests being unavailable in the +container unless at least added to the index with `git-add`. + +If you do want to test changes to the docker image, you can locally build the image and run the tests +without pulling from the registry using the following commands: + +```console +make topotests-build +TOPOTEST_PULL=0 make topotests +``` diff --git a/tests/topotests/docker/build.sh b/tests/topotests/docker/build.sh new file mode 100755 index 0000000..aec2058 --- /dev/null +++ b/tests/topotests/docker/build.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT +# +# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF") + +cd "$(dirname "$0")"/.. + +exec docker build --pull \ + --compress \ + -t frrouting/topotests:latest \ + . diff --git a/tests/topotests/docker/frr-topotests.sh b/tests/topotests/docker/frr-topotests.sh new file mode 100755 index 0000000..ce373d9 --- /dev/null +++ b/tests/topotests/docker/frr-topotests.sh @@ -0,0 +1,155 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT +# +# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF") + +set -e + +if [[ "$1" = "-h" ]] || [[ "$1" = "--help" ]]; then + cat >&2 <<-EOF + + This script runs the FRRouting topotests on the FRR tree + in the current working directory. + + Usage: $0 [args...] + + If any arguments are provided and the first argument starts with / or ./ + the arguments are interpreted as command and will be executed instead + of pytest. + + Behavior can be further modified by the following environment variables: + + TOPOTEST_AUTOLOAD If set to 1, the script will try to load necessary + kernel modules without asking for confirmation first. + + TOPOTEST_NOLOAD If set to 1, don't try to load necessary kernel + modules and don't even ask. + + TOPOTEST_BUILDCACHE Docker volume used for caching multiple FRR builds + over container runs. By default a + \`topotest-buildcache\` volume will be created for + that purpose. + + TOPOTEST_CLEAN Clean all previous build artifacts prior to + building. Disabled by default, set to 1 to enable. + + TOPOTEST_DOC Build the documentation associated with FRR. + Disabled by default, set to 1 to enable. + + TOPOTEST_FRR If set, don't test the FRR in the current working + directory, but the one at the given path. + + TOPOTEST_LOGS If set, don't use \`/tmp/topotest_logs\` directory + but use the provided path instead. + + TOPOTEST_OPTIONS These options are appended to the docker-run + command for starting the tests. + + TOPOTEST_PULL If set to 0, don't try to pull the most recent + version of the docker image from dockerhub. + + TOPOTEST_SANITIZER Controls whether to use the address sanitizer. + Enabled by default, set to 0 to disable. + + TOPOTEST_VERBOSE Show detailed build output. + Enabled by default, set to 0 to disable. + + EOF + exit 1 +fi + +# +# These two modules are needed to run the MPLS tests. +# They are often not automatically loaded. +# +# We cannot load them from the container since we don't +# have host kernel modules available there. If we load +# them from the host however, they can be used just fine. +# + +export PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" + +if [ "$TOPOTEST_NOLOAD" != "1" ]; then + for module in mpls-router mpls-iptunnel; do + if modprobe -n $module 2> /dev/null; then + : + else + # If the module doesn't exist, we cannot do anything about it + continue + fi + + if [ $(grep -c ${module/-/_} /proc/modules) -ne 0 ]; then + # If the module is loaded, we don't have to do anything + continue + fi + + if [ "$TOPOTEST_AUTOLOAD" != "1" ]; then + echo "To run all the possible tests, we need to load $module." + echo -n "Do you want to proceed? [y/n] " + read answer + if [ x"$answer" != x"y" ]; then + echo "Not loading." + continue + fi + fi + + if [ x"$(whoami)" = x"root" ]; then + modprobe $module + else + sudo modprobe $module + fi + done +fi + +if [ -z "$TOPOTEST_LOGS" ]; then + mkdir -p /tmp/topotest_logs + TOPOTEST_LOGS="/tmp/topotest_logs" +fi + +if [ -z "$TOPOTEST_FRR" ]; then + TOPOTEST_FRR="$(git rev-parse --show-toplevel || true)" + if [ -z "$TOPOTEST_FRR" ]; then + echo "Could not determine base of FRR tree." >&2 + echo "frr-topotests only works if you have your tree in git." >&2 + exit 1 + fi + git -C "$TOPOTEST_FRR" ls-files -z > "${TOPOTEST_LOGS}/git-ls-files" +fi + +if [ -z "$TOPOTEST_BUILDCACHE" ]; then + TOPOTEST_BUILDCACHE=topotest-buildcache + docker volume inspect "${TOPOTEST_BUILDCACHE}" &> /dev/null \ + || docker volume create "${TOPOTEST_BUILDCACHE}" +fi + +if [ "${TOPOTEST_PULL:-1}" = "1" ]; then + docker pull frrouting/topotests:latest +fi + +if [[ -n "$TMUX" ]]; then + TMUX_OPTIONS="-v $(dirname $TMUX):$(dirname $TMUX) -e TMUX=$TMUX -e TMUX_PANE=$TMUX_PANE" +fi + +if [[ -n "$STY" ]]; then + SCREEN_OPTIONS="-v /run/screen:/run/screen -e STY=$STY" +fi +set -- --rm -i \ + -v "$HOME:$HOME:ro" \ + -v "$TOPOTEST_LOGS:/tmp" \ + -v "$TOPOTEST_FRR:/root/host-frr:ro" \ + -v "$TOPOTEST_BUILDCACHE:/root/persist" \ + -e "TOPOTEST_CLEAN=$TOPOTEST_CLEAN" \ + -e "TOPOTEST_VERBOSE=$TOPOTEST_VERBOSE" \ + -e "TOPOTEST_DOC=$TOPOTEST_DOC" \ + -e "TOPOTEST_SANITIZER=$TOPOTEST_SANITIZER" \ + --privileged \ + $SCREEN_OPTINS \ + $TMUX_OPTIONS \ + $TOPOTEST_OPTIONS \ + frrouting/topotests:latest "$@" + +if [ -t 0 ]; then + set -- -t "$@" +fi + +exec docker run "$@" diff --git a/tests/topotests/docker/inner/compile_frr.sh b/tests/topotests/docker/inner/compile_frr.sh new file mode 100755 index 0000000..3b296a1 --- /dev/null +++ b/tests/topotests/docker/inner/compile_frr.sh @@ -0,0 +1,87 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT +# +# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF") + +set -e + +# Load shared functions +CDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +. $CDIR/funcs.sh + +# +# Script begin +# + +if [ "${TOPOTEST_CLEAN}" != "0" ]; then + log_info "Cleaning FRR builddir..." + rm -rf $FRR_BUILD_DIR &> /dev/null +fi + +log_info "Syncing FRR source with host..." +mkdir -p $FRR_BUILD_DIR +rsync -a --info=progress2 \ + --from0 --files-from=/tmp/git-ls-files \ + --chown root:root \ + $FRR_HOST_DIR/. $FRR_BUILD_DIR/ + +cd "$FRR_BUILD_DIR" || \ + log_fatal "failed to find frr directory" + +if [ "${TOPOTEST_VERBOSE}" != "0" ]; then + exec 3>&1 +else + exec 3>/dev/null +fi + +log_info "Building FRR..." + +if [ ! -e configure ]; then + bash bootstrap.sh >&3 || \ + log_fatal "failed to bootstrap configuration" +fi + +if [ "${TOPOTEST_DOC}" != "0" ]; then + EXTRA_CONFIGURE+=" --enable-doc " +else + EXTRA_CONFIGURE+=" --disable-doc " +fi + +if [ ! -e Makefile ]; then + if [ "${TOPOTEST_SANITIZER}" != "0" ]; then + export CC="gcc" + export CFLAGS="-O1 -g -fsanitize=address -fno-omit-frame-pointer" + export LDFLAGS="-g -fsanitize=address -ldl" + touch .address_sanitizer + else + rm -f .address_sanitizer + fi + + bash configure >&3 \ + --enable-static-bin \ + --enable-static \ + --enable-shared \ + --enable-dev-build \ + --with-moduledir=/usr/lib/frr/modules \ + --prefix=/usr \ + --localstatedir=/var/run/frr \ + --sbindir=/usr/lib/frr \ + --sysconfdir=/etc/frr \ + --enable-multipath=0 \ + --enable-fpm \ + --enable-sharpd \ + $EXTRA_CONFIGURE \ + --with-pkg-extra-version=-topotests \ + || log_fatal "failed to configure the sources" +fi + +# if '.address_sanitizer' file exists it means we are using address sanitizer. +if [ -f .address_sanitizer ]; then + make -C lib CFLAGS="-g -O2" LDFLAGS="-g" clippy >&3 +fi + +make -j$(cpu_count) >&3 || \ + log_fatal "failed to build the sources" + +make install >/dev/null || \ + log_fatal "failed to install frr" diff --git a/tests/topotests/docker/inner/entrypoint.sh b/tests/topotests/docker/inner/entrypoint.sh new file mode 100755 index 0000000..44e16db --- /dev/null +++ b/tests/topotests/docker/inner/entrypoint.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT +# +# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF") + +# Load shared functions +CDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +. $CDIR/funcs.sh + +set -e + +# +# Script begin +# +"${CDIR}/compile_frr.sh" +"${CDIR}/openvswitch.sh" + +cd "${FRR_BUILD_DIR}/tests/topotests" + +log_info "Setting permissions on /tmp so we can generate logs" +chmod 1777 /tmp + +if [ $# -eq 0 ] || ([[ "$1" != /* ]] && [[ "$1" != ./* ]]); then + export TOPOTESTS_CHECK_MEMLEAK=/tmp/memleak_ + export TOPOTESTS_CHECK_STDERR=Yes + set -- pytest \ + --junitxml /tmp/topotests.xml \ + "$@" +fi + +exec "$@" diff --git a/tests/topotests/docker/inner/funcs.sh b/tests/topotests/docker/inner/funcs.sh new file mode 100755 index 0000000..4ebacbb --- /dev/null +++ b/tests/topotests/docker/inner/funcs.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT +# +# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF") + +FRR_HOST_DIR=/root/host-frr +FRR_BUILD_DIR=/root/persist/frr-build + +if [ ! -L "/root/frr" ]; then + ln -s $FRR_BUILD_DIR /root/frr +fi + +[ -z $TOPOTEST_CLEAN ] && TOPOTEST_CLEAN=0 +[ -z $TOPOTEST_VERBOSE ] && TOPOTEST_VERBOSE=1 +[ -z $TOPOTEST_DOC ] && TOPOTEST_DOC=0 +[ -z $TOPOTEST_SANITIZER ] && TOPOTEST_SANITIZER=1 + +log_info() { + local msg=$1 + + echo -e "=> $msg" +} + +log_error() { + local msg=$1 + + echo -e "E: $msg" 2>&1 +} + +log_warning() { + local msg=$1 + + echo -e "W: $msg" 2>&1 +} + +log_fatal() { + local msg=$1 + + echo -e "F: $msg" 2>&1 + + exit 1 +} + +cpu_count() { + local cpu_count + + cpu_count=$(cat /proc/cpuinfo | grep -w processor | wc -l) + if [ $? -eq 0 ]; then + echo -n $cpu_count + else + echo -n 2 + fi +} diff --git a/tests/topotests/docker/inner/motd.txt b/tests/topotests/docker/inner/motd.txt new file mode 100644 index 0000000..1e2f34f --- /dev/null +++ b/tests/topotests/docker/inner/motd.txt @@ -0,0 +1,15 @@ +Welcome to the topotests container. + +Here are some useful tips: +* After changing the FRR/Topotests sources, you may rebuild them + using the command `compile_frr.sh`. The build command has the + following environment variables: + - TOPOTEST_CLEAN: whether we should distclean or not (disabled by default) + - TOPOTEST_VERBOSE: show build messages (enabled by default) + - TOPOTEST_DOC: whether we should build docs or not (disabled by default) + - TOPOTEST_SANITIZER: whether we should use the address sanitizer (enabled by default) + + Usage example: env TOPOTEST_CLEAN=1 compile_frr.sh + +* The topotests log directory can be found on your host machine on + `/tmp/topotests_logs`. diff --git a/tests/topotests/docker/inner/openvswitch.sh b/tests/topotests/docker/inner/openvswitch.sh new file mode 100755 index 0000000..926a25c --- /dev/null +++ b/tests/topotests/docker/inner/openvswitch.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT +# +# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF") + +# Load shared functions +CDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +. $CDIR/funcs.sh + +# +# Script begin +# + +log_info "Configuring OpenvSwitch...." + +# Configure OpenvSwitch so we are able to run mininet +mkdir -p /var/run/openvswitch +ovsdb-tool create /etc/openvswitch/conf.db \ + /usr/share/openvswitch/vswitch.ovsschema +ovsdb-server /etc/openvswitch/conf.db \ + --remote=punix:/var/run/openvswitch/db.sock \ + --remote=ptcp:6640 --pidfile=ovsdb-server.pid >/dev/null 2>/dev/null & \ + disown +ovs-vswitchd >/dev/null 2>/dev/null & disown + +sleep 2 + +ovs-vsctl --no-wait -- init +ovs_version=$(ovs-vsctl -V | grep ovs-vsctl | awk '{print $4}') +ovs_db_version=$(\ + ovsdb-tool schema-version /usr/share/openvswitch/vswitch.ovsschema) +ovs-vsctl --no-wait -- set Open_vSwitch . db-version="${ovs_db_version}" +ovs-vsctl --no-wait -- set Open_vSwitch . ovs-version="${ovs_version}" +ovs-vsctl --no-wait -- set Open_vSwitch . system-type="docker-ovs" +ovs-vsctl --no-wait -- set Open_vSwitch . system-version="0.1" +ovs-vsctl --no-wait -- \ + set Open_vSwitch . external-ids:system-id=`cat /proc/sys/kernel/random/uuid` +ovs-vsctl --no-wait -- set-manager ptcp:6640 +ovs-appctl -t ovsdb-server \ + ovsdb-server/add-remote db:Open_vSwitch,Open_vSwitch,manager_options diff --git a/tests/topotests/eigrp_topo1/r1/eigrpd.conf b/tests/topotests/eigrp_topo1/r1/eigrpd.conf new file mode 100644 index 0000000..6c800ab --- /dev/null +++ b/tests/topotests/eigrp_topo1/r1/eigrpd.conf @@ -0,0 +1,8 @@ +log file eigrpd.log +! +router eigrp 1 + network 0.0.0.0/0 +! +line vty +! + diff --git a/tests/topotests/eigrp_topo1/r1/show_ip_eigrp.json b/tests/topotests/eigrp_topo1/r1/show_ip_eigrp.json new file mode 100644 index 0000000..be0fdcf --- /dev/null +++ b/tests/topotests/eigrp_topo1/r1/show_ip_eigrp.json @@ -0,0 +1,32 @@ +{ + "P": { + "192.168.1.0/24": { + "fd": "28160", + "interface": " r1-eth0", + "serno": "0", + "successors": "1", + "via": "Connected" + }, + "192.168.3.0/24": { + "fd": "33280", + "interface": " r1-eth1", + "serno": "0", + "successors": "1", + "via": "193.1.1.2 (33280/30720)" + }, + "193.1.1.0/26": { + "fd": "28160", + "interface": " r1-eth1", + "serno": "0", + "successors": "1", + "via": "Connected" + }, + "193.1.2.0/24": { + "fd": "30720", + "interface": " r1-eth1", + "serno": "0", + "successors": "1", + "via": "193.1.1.2 (30720/28160)" + } + } +} diff --git a/tests/topotests/eigrp_topo1/r1/show_ip_eigrp.ref b/tests/topotests/eigrp_topo1/r1/show_ip_eigrp.ref new file mode 100644 index 0000000..a2d7b33 --- /dev/null +++ b/tests/topotests/eigrp_topo1/r1/show_ip_eigrp.ref @@ -0,0 +1,14 @@ + +EIGRP Topology Table for AS(1)/ID(193.1.1.1) + +Codes: P - Passive, A - Active, U - Update, Q - Query, R - Reply + r - reply Status, s - sia Status + +P 193.1.1.0/26, 1 successors, FD is 28160, serno: 0 + via Connected, r1-eth1 +P 192.168.1.0/24, 1 successors, FD is 28160, serno: 0 + via Connected, r1-eth0 +P 193.1.2.0/24, 1 successors, FD is 30720, serno: 0 + via 193.1.1.2 (30720/28160), r1-eth1 +P 192.168.3.0/24, 1 successors, FD is 33280, serno: 0 + via 193.1.1.2 (33280/30720), r1-eth1 diff --git a/tests/topotests/eigrp_topo1/r1/show_ip_route.json_ref b/tests/topotests/eigrp_topo1/r1/show_ip_route.json_ref new file mode 100644 index 0000000..26fa7ca --- /dev/null +++ b/tests/topotests/eigrp_topo1/r1/show_ip_route.json_ref @@ -0,0 +1,90 @@ +{ + "192.168.1.0/24":[ + { + "prefix":"192.168.1.0/24", + "protocol":"eigrp", + "metric":28160, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + }, + { + "prefix":"192.168.1.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "192.168.3.0/24":[ + { + "prefix":"192.168.3.0/24", + "protocol":"eigrp", + "selected":true, + "metric":33280, + "nexthops":[ + { + "fib":true, + "ip":"193.1.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "193.1.1.0/26":[ + { + "prefix":"193.1.1.0/26", + "protocol":"eigrp", + "metric":28160, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r1-eth1", + "active":true + } + ] + }, + { + "prefix":"193.1.1.0/26", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "193.1.2.0/24":[ + { + "prefix":"193.1.2.0/24", + "protocol":"eigrp", + "selected":true, + "metric":30720, + "nexthops":[ + { + "fib":true, + "ip":"193.1.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/eigrp_topo1/r1/zebra.conf b/tests/topotests/eigrp_topo1/r1/zebra.conf new file mode 100644 index 0000000..51579a7 --- /dev/null +++ b/tests/topotests/eigrp_topo1/r1/zebra.conf @@ -0,0 +1,20 @@ +log file zebra.log +! debug zebra rib detail +! +hostname r1 +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +interface r1-eth1 + description to sw2 - RIPv2 interface + ip address 193.1.1.1/26 + no link-detect +! +ip forwarding +ipv6 forwarding +! +! +line vty +! + diff --git a/tests/topotests/eigrp_topo1/r2/eigrpd.conf b/tests/topotests/eigrp_topo1/r2/eigrpd.conf new file mode 100644 index 0000000..56c747d --- /dev/null +++ b/tests/topotests/eigrp_topo1/r2/eigrpd.conf @@ -0,0 +1,7 @@ +log file eigrpd.log +! +! +router eigrp 1 + network 193.1.1.0/26 + network 193.1.2.0/24 + diff --git a/tests/topotests/eigrp_topo1/r2/show_ip_eigrp.json b/tests/topotests/eigrp_topo1/r2/show_ip_eigrp.json new file mode 100644 index 0000000..ae9f441 --- /dev/null +++ b/tests/topotests/eigrp_topo1/r2/show_ip_eigrp.json @@ -0,0 +1,32 @@ +{ + "P": { + "192.168.1.0/24": { + "fd": "30720", + "interface": " r2-eth0", + "serno": "0", + "successors": "1", + "via": "193.1.1.1 (30720/28160)" + }, + "192.168.3.0/24": { + "fd": "30720", + "interface": " r2-eth1", + "serno": "0", + "successors": "1", + "via": "193.1.2.2 (30720/28160)" + }, + "193.1.1.0/26": { + "fd": "28160", + "interface": " r2-eth0", + "serno": "0", + "successors": "1", + "via": "Connected" + }, + "193.1.2.0/24": { + "fd": "28160", + "interface": " r2-eth1", + "serno": "0", + "successors": "1", + "via": "Connected" + } + } +} diff --git a/tests/topotests/eigrp_topo1/r2/show_ip_eigrp.ref b/tests/topotests/eigrp_topo1/r2/show_ip_eigrp.ref new file mode 100644 index 0000000..cce49cd --- /dev/null +++ b/tests/topotests/eigrp_topo1/r2/show_ip_eigrp.ref @@ -0,0 +1,14 @@ + +EIGRP Topology Table for AS(1)/ID(193.1.2.1) + +Codes: P - Passive, A - Active, U - Update, Q - Query, R - Reply + r - reply Status, s - sia Status + +P 193.1.1.0/26, 1 successors, FD is 28160, serno: 0 + via Connected, r2-eth0 +P 192.168.1.0/24, 1 successors, FD is 30720, serno: 0 + via 193.1.1.1 (30720/28160), r2-eth0 +P 193.1.2.0/24, 1 successors, FD is 28160, serno: 0 + via Connected, r2-eth1 +P 192.168.3.0/24, 1 successors, FD is 30720, serno: 0 + via 193.1.2.2 (30720/28160), r2-eth1 diff --git a/tests/topotests/eigrp_topo1/r2/show_ip_route.json_ref b/tests/topotests/eigrp_topo1/r2/show_ip_route.json_ref new file mode 100644 index 0000000..71c931b --- /dev/null +++ b/tests/topotests/eigrp_topo1/r2/show_ip_route.json_ref @@ -0,0 +1,90 @@ +{ + "192.168.1.0/24":[ + { + "prefix":"192.168.1.0/24", + "protocol":"eigrp", + "selected":true, + "metric":30720, + "nexthops":[ + { + "fib":true, + "ip":"193.1.1.1", + "afi":"ipv4", + "interfaceName":"r2-eth0", + "active":true + } + ] + } + ], + "192.168.3.0/24":[ + { + "prefix":"192.168.3.0/24", + "protocol":"eigrp", + "selected":true, + "metric":30720, + "nexthops":[ + { + "fib":true, + "ip":"193.1.2.2", + "afi":"ipv4", + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "193.1.1.0/26":[ + { + "prefix":"193.1.1.0/26", + "protocol":"eigrp", + "metric":28160, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r2-eth0", + "active":true + } + ] + }, + { + "prefix":"193.1.1.0/26", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r2-eth0", + "active":true + } + ] + } + ], + "193.1.2.0/24":[ + { + "prefix":"193.1.2.0/24", + "protocol":"eigrp", + "metric":28160, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r2-eth1", + "active":true + } + ] + }, + { + "prefix":"193.1.2.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/eigrp_topo1/r2/zebra.conf b/tests/topotests/eigrp_topo1/r2/zebra.conf new file mode 100644 index 0000000..c440f3a --- /dev/null +++ b/tests/topotests/eigrp_topo1/r2/zebra.conf @@ -0,0 +1,21 @@ +log file zebra.log +! +hostname r2 +! +interface r2-eth0 + description to sw2 - RIPv2 interface + ip address 193.1.1.2/26 + no link-detect +! +interface r2-eth1 + description to sw3 - RIPv1 interface + ip address 193.1.2.1/24 + no link-detect +! +ip forwarding +ipv6 forwarding +! +! +line vty +! + diff --git a/tests/topotests/eigrp_topo1/r3/eigrpd.conf b/tests/topotests/eigrp_topo1/r3/eigrpd.conf new file mode 100644 index 0000000..53ad1bb --- /dev/null +++ b/tests/topotests/eigrp_topo1/r3/eigrpd.conf @@ -0,0 +1,6 @@ +log file eigrpd.log +! +! +router eigrp 1 + network 0.0.0.0/0 + diff --git a/tests/topotests/eigrp_topo1/r3/show_ip_eigrp.json b/tests/topotests/eigrp_topo1/r3/show_ip_eigrp.json new file mode 100644 index 0000000..83db66c --- /dev/null +++ b/tests/topotests/eigrp_topo1/r3/show_ip_eigrp.json @@ -0,0 +1,32 @@ +{ + "P": { + "192.168.1.0/24": { + "fd": "33280", + "interface": " r3-eth1", + "serno": "0", + "successors": "1", + "via": "193.1.2.1 (33280/30720)" + }, + "192.168.3.0/24": { + "fd": "28160", + "interface": " r3-eth0", + "serno": "0", + "successors": "1", + "via": "Connected" + }, + "193.1.1.0/26": { + "fd": "30720", + "interface": " r3-eth1", + "serno": "0", + "successors": "1", + "via": "193.1.2.1 (30720/28160)" + }, + "193.1.2.0/24": { + "fd": "28160", + "interface": " r3-eth1", + "serno": "0", + "successors": "1", + "via": "Connected" + } + } +} diff --git a/tests/topotests/eigrp_topo1/r3/show_ip_eigrp.ref b/tests/topotests/eigrp_topo1/r3/show_ip_eigrp.ref new file mode 100644 index 0000000..70f3d3f --- /dev/null +++ b/tests/topotests/eigrp_topo1/r3/show_ip_eigrp.ref @@ -0,0 +1,14 @@ + +EIGRP Topology Table for AS(1)/ID(193.1.2.2) + +Codes: P - Passive, A - Active, U - Update, Q - Query, R - Reply + r - reply Status, s - sia Status + +P 193.1.1.0/26, 1 successors, FD is 30720, serno: 0 + via 193.1.2.1 (30720/28160), r3-eth1 +P 192.168.1.0/24, 1 successors, FD is 33280, serno: 0 + via 193.1.2.1 (33280/30720), r3-eth1 +P 193.1.2.0/24, 1 successors, FD is 28160, serno: 0 + via Connected, r3-eth1 +P 192.168.3.0/24, 1 successors, FD is 28160, serno: 0 + via Connected, r3-eth0 diff --git a/tests/topotests/eigrp_topo1/r3/show_ip_route.json_ref b/tests/topotests/eigrp_topo1/r3/show_ip_route.json_ref new file mode 100644 index 0000000..5e0b79d --- /dev/null +++ b/tests/topotests/eigrp_topo1/r3/show_ip_route.json_ref @@ -0,0 +1,108 @@ +{ + "192.168.1.0/24":[ + { + "prefix":"192.168.1.0/24", + "protocol":"eigrp", + "selected":true, + "metric":33280, + "nexthops":[ + { + "fib":true, + "ip":"193.1.2.1", + "afi":"ipv4", + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ], + "192.168.2.0/24":[ + { + "prefix":"192.168.2.0/24", + "protocol":"static", + "selected":true, + "distance":1, + "metric":0, + "nexthops":[ + { + "fib":true, + "ip":"192.168.3.10", + "afi":"ipv4", + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ], + "192.168.3.0/24":[ + { + "prefix":"192.168.3.0/24", + "protocol":"eigrp", + "metric":28160, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r3-eth0", + "active":true + } + ] + }, + { + "prefix":"192.168.3.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ], + "193.1.1.0/26":[ + { + "prefix":"193.1.1.0/26", + "protocol":"eigrp", + "selected":true, + "metric":30720, + "nexthops":[ + { + "fib":true, + "ip":"193.1.2.1", + "afi":"ipv4", + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ], + "193.1.2.0/24":[ + { + "prefix":"193.1.2.0/24", + "protocol":"eigrp", + "metric":28160, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r3-eth1", + "active":true + } + ] + }, + { + "prefix":"193.1.2.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/eigrp_topo1/r3/zebra.conf b/tests/topotests/eigrp_topo1/r3/zebra.conf new file mode 100644 index 0000000..7f145b4 --- /dev/null +++ b/tests/topotests/eigrp_topo1/r3/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname r3 +! +interface r3-eth0 + description to sw4 - Stub interface + ip address 192.168.3.1/24 + no link-detect +! +interface r3-eth1 + description to sw3 - RIPv2 interface + ip address 193.1.2.2/24 + no link-detect +! +ip route 192.168.2.0/24 192.168.3.10 +! +ip forwarding +ipv6 forwarding +! +! +line vty +! diff --git a/tests/topotests/eigrp_topo1/test_eigrp_topo1.dot b/tests/topotests/eigrp_topo1/test_eigrp_topo1.dot new file mode 100644 index 0000000..ca3a0fe --- /dev/null +++ b/tests/topotests/eigrp_topo1/test_eigrp_topo1.dot @@ -0,0 +1,62 @@ +## GraphViz file for test_eigrp_topo1 +## +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## EIGRP: #696969 +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph test_eigrp_topo1 { + overlap=false; + constraint=false; + + // title + labelloc="t"; + label="Test Topologoy EIGRP Topo1"; + + ###################### + # Routers + ###################### + + # Main FRR Router with all protocols + R1 [shape=doubleoctagon, label="R1 FRR\nMain Router", fillcolor="#f08080", style=filled]; + + # EIGRP Routers + R2 [shape=doubleoctagon, label="R2 FRR\nEIGRP Router", fillcolor="#19e3d9", style=filled]; + R3 [shape=doubleoctagon, label="R3 FRR\nEIGRP Router", fillcolor="#19e3d9", style=filled]; + + ###################### + # Network Lists + ###################### + + SW1_R1_stub [label="SW1\n192.168.1.0/24", fillcolor="#d0e0d0", style=filled]; + + # EIGRP Networks + SW2_R1_R2 [label="SW2\nEIGRPv2\n193.1.1.0/26", fillcolor="#d0e0d0", style=filled]; + SW3_R2_R3 [label="SW3\nEIGRPv1\n193.1.2.0/24", fillcolor="#d0e0d0", style=filled]; + SW4_R3 [label="SW4\n192.168.3.0/24", fillcolor="#d0e0d0", style=filled]; + Net_R3_remote [label="Static Net\n192.168.2.0/24"]; + + ###################### + # Network Connections + ###################### + R1 -- SW1_R1_stub [label = "eth0\n.1\n::1"]; + + # EIGRP Network + R1 -- SW2_R1_R2 [label = "eth1\n.1"]; + SW2_R1_R2 -- R2 [label = "eth0\n.2"]; + R2 -- SW3_R2_R3 [label = "eth1\n.1"]; + SW3_R2_R3 -- R3 [label = "eth1\n.2"]; + R3 -- SW4_R3 [label = "eth0\n.1"]; + SW4_R3 -- Net_R3_remote [label = ".10"]; + +} diff --git a/tests/topotests/eigrp_topo1/test_eigrp_topo1.py b/tests/topotests/eigrp_topo1/test_eigrp_topo1.py new file mode 100644 index 0000000..3c9392c --- /dev/null +++ b/tests/topotests/eigrp_topo1/test_eigrp_topo1.py @@ -0,0 +1,279 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_eigrp_topo1.py +# +# Copyright (c) 2017 by +# Cumulus Networks, Inc. +# Donald Sharp +# + +""" +test_eigrp_topo1.py: Testing EIGRP + +""" + +import os +import re +import sys +import pytest +import json + +pytestmark = [pytest.mark.eigrpd] + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + # On main router + # First switch is for a dummy interface (for local network) + switch = tgen.add_switch("sw1") + switch.add_link(tgen.gears["r1"]) + + # Switches for EIGRP + # switch 2 switch is for connection to EIGRP router + switch = tgen.add_switch("sw2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # switch 4 is stub on remote EIGRP router + switch = tgen.add_switch("sw4") + switch.add_link(tgen.gears["r3"]) + + # switch 3 is between EIGRP routers + switch = tgen.add_switch("sw3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + "Setup topology" + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_EIGRP, os.path.join(CWD, "{}/eigrpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_converge_protocols(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + topotest.sleep(5, "Waiting for EIGRP convergence") + + +def test_eigrp_routes(): + "Test EIGRP 'show ip eigrp'" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Verify EIGRP Status + logger.info("Verifying EIGRP routes") + + router_list = tgen.routers().values() + for router in router_list: + refTableFile = "{}/{}/show_ip_eigrp.json".format(CWD, router.name) + + # Read expected result from file + expected = json.loads(open(refTableFile).read()) + + # Actual output from router + actual = ip_eigrp_topo(router) + + assertmsg = '"show ip eigrp topo" mismatches on {}'.format(router.name) + assert topotest.json_cmp(actual, expected) is None, assertmsg + + +def test_zebra_ipv4_routingTable(): + "Test 'show ip route'" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + failures = 0 + router_list = tgen.routers().values() + for router in router_list: + output = router.vtysh_cmd("show ip route json", isjson=True) + refTableFile = "{}/{}/show_ip_route.json_ref".format(CWD, router.name) + expected = json.loads(open(refTableFile).read()) + + assertmsg = "Zebra IPv4 Routing Table verification failed for router {}".format( + router.name + ) + assert topotest.json_cmp(output, expected) is None, assertmsg + + +def test_shut_interface_and_recover(): + "Test shutdown of an interface and recovery of the interface" + + tgen = get_topogen() + router = tgen.gears["r1"] + router.run("ip link set r1-eth1 down") + topotest.sleep(5, "Waiting for EIGRP convergence") + router.run("ip link set r1-eth1 up") + + +def test_shutdown_check_stderr(): + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + pytest.skip("Skipping test for Stderr output and memory leaks") + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Verifying unexpected STDERR output from daemons") + + router_list = tgen.routers().values() + for router in router_list: + router.stop() + + log = tgen.net[router.name].getStdErr("eigrpd") + if log: + logger.error("EIGRPd StdErr Log:" + log) + log = tgen.net[router.name].getStdErr("zebra") + if log: + logger.error("Zebra StdErr Log:" + log) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) + +# +# Auxiliary Functions +# +def ip_eigrp_topo(node): + """ + Parse 'show ip eigrp topo' from `node` and returns a dict with the + result. + + Example: + { + 'P': { + '192.168.1.0/24': { + 'sucessors': 1, + 'fd': 112233, + 'serno': 0, + 'via': 'Connected', + 'interface': 'eth0', + }, + '192.168.2.0/24': { + 'sucessors': 1, + 'fd': 112234, + 'serno': 0, + 'via': 'Connected', + 'interface': 'eth1', + } + } + } + """ + output = topotest.normalize_text(node.vtysh_cmd("show ip eigrp topo")).splitlines() + result = {} + for idx, line in enumerate(output): + columns = line.split(" ", 1) + + # Parse the following format into python dicts + # code A.B.C.D/E, X successors, FD is Y, serno: Z + # via FOO, interface-name + code = columns[0] + if code not in ["P", "A", "U", "Q", "R", "r", "s"]: + continue + + if code not in result: + result[code] = {} + + # Split network from the rest + columns = columns[1].split(",") + + # Parse first line data + network = columns[0] + result[code][network] = {} + for column in columns: + # Skip the network column + if column == columns[0]: + continue + + match = re.search(r"(\d+) successors", column) + if match is not None: + result[code][network]["successors"] = match.group(1) + continue + + match = re.search(r"FD is (\d+)", column) + if match is not None: + result[code][network]["fd"] = match.group(1) + continue + + match = re.search(r"serno: (\d+)", column) + if match is not None: + result[code][network]["serno"] = match.group(1) + continue + + # Parse second line data + nextline = output[idx + 1] + columns = topotest.normalize_text(nextline).split(",") + for column in columns: + match = re.search(r"via (.+)", column) + if match is not None: + result[code][network]["via"] = match.group(1) + continue + + match = re.search(r"(.+)", column) + if match is not None: + result[code][network]["interface"] = match.group(1) + continue + + return result diff --git a/tests/topotests/evpn_pim_1/host1/bgpd.conf b/tests/topotests/evpn_pim_1/host1/bgpd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/evpn_pim_1/host1/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/evpn_pim_1/host1/pimd.conf b/tests/topotests/evpn_pim_1/host1/pimd.conf new file mode 100644 index 0000000..63a44c1 --- /dev/null +++ b/tests/topotests/evpn_pim_1/host1/pimd.conf @@ -0,0 +1,4 @@ +int lo +! + + diff --git a/tests/topotests/evpn_pim_1/host1/zebra.conf b/tests/topotests/evpn_pim_1/host1/zebra.conf new file mode 100644 index 0000000..45ad031 --- /dev/null +++ b/tests/topotests/evpn_pim_1/host1/zebra.conf @@ -0,0 +1,5 @@ +int host1-eth0 + ip addr 192.168.3.4/24 + +int lo + ip addr 192.168.100.4/32 diff --git a/tests/topotests/evpn_pim_1/host2/bgpd.conf b/tests/topotests/evpn_pim_1/host2/bgpd.conf new file mode 100644 index 0000000..cdf4cb4 --- /dev/null +++ b/tests/topotests/evpn_pim_1/host2/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/evpn_pim_1/host2/pimd.conf b/tests/topotests/evpn_pim_1/host2/pimd.conf new file mode 100644 index 0000000..63a44c1 --- /dev/null +++ b/tests/topotests/evpn_pim_1/host2/pimd.conf @@ -0,0 +1,4 @@ +int lo +! + + diff --git a/tests/topotests/evpn_pim_1/host2/zebra.conf b/tests/topotests/evpn_pim_1/host2/zebra.conf new file mode 100644 index 0000000..bfae530 --- /dev/null +++ b/tests/topotests/evpn_pim_1/host2/zebra.conf @@ -0,0 +1,5 @@ +int host-eth0 + ip addr 192.168.4.5/24 + +int lo + ip addr 192.168.100.5/32 diff --git a/tests/topotests/evpn_pim_1/leaf1/bgpd.conf b/tests/topotests/evpn_pim_1/leaf1/bgpd.conf new file mode 100644 index 0000000..97fd866 --- /dev/null +++ b/tests/topotests/evpn_pim_1/leaf1/bgpd.conf @@ -0,0 +1,11 @@ + +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 3 10 + redistribute connected + address-family l2vpn evpn + neighbor 192.168.1.1 activate + advertise-all-vni + ! +! diff --git a/tests/topotests/evpn_pim_1/leaf1/pimd.conf b/tests/topotests/evpn_pim_1/leaf1/pimd.conf new file mode 100644 index 0000000..b54aada --- /dev/null +++ b/tests/topotests/evpn_pim_1/leaf1/pimd.conf @@ -0,0 +1,16 @@ +! debug pim events +! debug pim nht +! debug pim zebra +ip pim rp 192.168.100.1 +ip pim join-prune-interval 5 +! +int lo + ip pim +! +int leaf1-eth0 + ip pim +! +int leaf1-eth1 + ip pim + ip igmp + diff --git a/tests/topotests/evpn_pim_1/leaf1/zebra.conf b/tests/topotests/evpn_pim_1/leaf1/zebra.conf new file mode 100644 index 0000000..581cc6e --- /dev/null +++ b/tests/topotests/evpn_pim_1/leaf1/zebra.conf @@ -0,0 +1,6 @@ +int leaf1-eth0 + ip addr 192.168.1.2/24 +int leaf1-eth1 + ip addr 192.168.3.2/24 +int lo + ip addr 192.168.100.2/32 diff --git a/tests/topotests/evpn_pim_1/leaf2/bgpd.conf b/tests/topotests/evpn_pim_1/leaf2/bgpd.conf new file mode 100644 index 0000000..91d9bd8 --- /dev/null +++ b/tests/topotests/evpn_pim_1/leaf2/bgpd.conf @@ -0,0 +1,11 @@ + +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 3 10 + redistribute connected + address-family l2vpn evpn + neighbor 192.168.2.1 activate + advertise-all-vni + ! +! diff --git a/tests/topotests/evpn_pim_1/leaf2/pimd.conf b/tests/topotests/evpn_pim_1/leaf2/pimd.conf new file mode 100644 index 0000000..d775b80 --- /dev/null +++ b/tests/topotests/evpn_pim_1/leaf2/pimd.conf @@ -0,0 +1,14 @@ +ip pim rp 192.168.100.1 +ip pim join-prune-interval 5 +! +int lo + ip pim +! +int leaf2-eth0 + ip pim +! +int leaf2-eth1 + ip pim + ip igmp +! + diff --git a/tests/topotests/evpn_pim_1/leaf2/zebra.conf b/tests/topotests/evpn_pim_1/leaf2/zebra.conf new file mode 100644 index 0000000..1bcf8e1 --- /dev/null +++ b/tests/topotests/evpn_pim_1/leaf2/zebra.conf @@ -0,0 +1,6 @@ +int leaf2-eth0 + ip addr 192.168.2.3/24 +int leaf2-eth1 + ip addr 192.168.4.3/24 +int lo + ip addr 192.168.100.3/32 diff --git a/tests/topotests/evpn_pim_1/spine/bgp.summ.json b/tests/topotests/evpn_pim_1/spine/bgp.summ.json new file mode 100644 index 0000000..7f37ced --- /dev/null +++ b/tests/topotests/evpn_pim_1/spine/bgp.summ.json @@ -0,0 +1,39 @@ +{ + "routerId":"192.168.100.1", + "as":65001, + "vrfName":"default", + "tableVersion":7, + "peerCount":2, + "peers":{ + "192.168.1.2":{ + "remoteAs":65002, + "version":4, + "outq":0, + "inq":0, + "pfxRcd":3, + "pfxSnt":7, + "state":"Established", + "connectionsEstablished":1, + "connectionsDropped":0, + "idType":"ipv4" + }, + "192.168.2.3":{ + "remoteAs":65003, + "version":4, + "outq":0, + "inq":0, + "pfxRcd":3, + "pfxSnt":7, + "state":"Established", + "connectionsEstablished":1, + "connectionsDropped":0, + "idType":"ipv4" + } + }, + "failedPeers":0, + "totalPeers":2, + "dynamicPeers":0, + "bestPath":{ + "multiPathRelax":"false" + } +} diff --git a/tests/topotests/evpn_pim_1/spine/bgpd.conf b/tests/topotests/evpn_pim_1/spine/bgpd.conf new file mode 100644 index 0000000..81ab802 --- /dev/null +++ b/tests/topotests/evpn_pim_1/spine/bgpd.conf @@ -0,0 +1,13 @@ + +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 3 10 + neighbor 192.168.2.3 remote-as external + neighbor 192.168.2.3 timers 3 10 + redistribute connected + address-family l2vpn evpn + neighbor 192.168.1.2 activate + neighbor 192.168.2.3 activate + exit-address-family +! diff --git a/tests/topotests/evpn_pim_1/spine/join-info.json b/tests/topotests/evpn_pim_1/spine/join-info.json new file mode 100644 index 0000000..3d135fb --- /dev/null +++ b/tests/topotests/evpn_pim_1/spine/join-info.json @@ -0,0 +1,34 @@ +{ + "spine-eth0":{ + "name":"spine-eth0", + "state":"up", + "address":"192.168.1.1", + "flagMulticast":true, + "flagBroadcast":true, + "lanDelayEnabled":true, + "239.1.1.1":{ + "*":{ + "source":"*", + "group":"239.1.1.1", + "prune":"--:--", + "channelJoinName":"JOIN" + } + } + }, + "spine-eth1":{ + "name":"spine-eth1", + "state":"up", + "address":"192.168.2.1", + "flagMulticast":true, + "flagBroadcast":true, + "lanDelayEnabled":true, + "239.1.1.1":{ + "*":{ + "source":"*", + "group":"239.1.1.1", + "prune":"--:--", + "channelJoinName":"JOIN" + } + } + } +} diff --git a/tests/topotests/evpn_pim_1/spine/pimd.conf b/tests/topotests/evpn_pim_1/spine/pimd.conf new file mode 100644 index 0000000..12c6d6f --- /dev/null +++ b/tests/topotests/evpn_pim_1/spine/pimd.conf @@ -0,0 +1,14 @@ +ip pim rp 192.168.100.1 +ip pim join-prune-interval 5 +! +int lo + ip pim +! +int spine-eth0 + ip pim +! +int spine-eth1 + ip pim +! + + diff --git a/tests/topotests/evpn_pim_1/spine/zebra.conf b/tests/topotests/evpn_pim_1/spine/zebra.conf new file mode 100644 index 0000000..2cb7194 --- /dev/null +++ b/tests/topotests/evpn_pim_1/spine/zebra.conf @@ -0,0 +1,8 @@ +int spine-eth0 + ip addr 192.168.1.1/24 +! +int spine-eth1 + ip addr 192.168.2.1/24 +! +int lo + ip addr 192.168.100.1/32 diff --git a/tests/topotests/evpn_pim_1/test_evpn_pim_topo1.py b/tests/topotests/evpn_pim_1/test_evpn_pim_topo1.py new file mode 100644 index 0000000..c0621d7 --- /dev/null +++ b/tests/topotests/evpn_pim_1/test_evpn_pim_topo1.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_evpn-pim_topo1.py +# +# Copyright (c) 2017 by +# Cumulus Networks, Inc. +# Donald Sharp +# + +""" +test_evpn_pim_topo1.py: Testing evpn-pim + +""" + +import os +import sys +import pytest +import json +from functools import partial + +pytestmark = [pytest.mark.pimd] + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.bgpd, pytest.mark.bgpd] + + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +def build_topo(tgen): + tgen.add_router("spine") + tgen.add_router("leaf1") + tgen.add_router("leaf2") + tgen.add_router("host1") + tgen.add_router("host2") + + # On main router + # First switch is for a dummy interface (for local network) + # spine-eth0 is connected to leaf1-eth0 + switch = tgen.add_switch("sw1") + switch.add_link(tgen.gears["spine"]) + switch.add_link(tgen.gears["leaf1"]) + + # spine-eth1 is connected to leaf2-eth0 + switch = tgen.add_switch("sw2") + switch.add_link(tgen.gears["spine"]) + switch.add_link(tgen.gears["leaf2"]) + + # leaf1-eth1 is connected to host1-eth0 + switch = tgen.add_switch("sw3") + switch.add_link(tgen.gears["leaf1"]) + switch.add_link(tgen.gears["host1"]) + + # leaf2-eth1 is connected to host2-eth0 + switch = tgen.add_switch("sw4") + switch.add_link(tgen.gears["leaf2"]) + switch.add_link(tgen.gears["host2"]) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + "Setup topology" + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + leaf1 = tgen.gears["leaf1"] + leaf2 = tgen.gears["leaf2"] + + leaf1.run("brctl addbr brleaf1") + leaf2.run("brctl addbr brleaf2") + leaf1.run("ip link set dev brleaf1 up") + leaf2.run("ip link set dev brleaf2 up") + leaf1.run( + "ip link add vxlan0 type vxlan id 42 group 239.1.1.1 dev leaf1-eth1 dstport 4789" + ) + leaf2.run( + "ip link add vxlan0 type vxlan id 42 group 239.1.1.1 dev leaf2-eth1 dstport 4789" + ) + leaf1.run("brctl addif brleaf1 vxlan0") + leaf2.run("brctl addif brleaf2 vxlan0") + leaf1.run("ip link set up dev vxlan0") + leaf2.run("ip link set up dev vxlan0") + # tgen.mininet_cli() + # This is a sample of configuration loading. + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_PIM, os.path.join(CWD, "{}/pimd.conf".format(rname)) + ) + tgen.start_router() + # tgen.mininet_cli() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_converge_protocols(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + spine = tgen.gears["spine"] + json_file = "{}/{}/bgp.summ.json".format(CWD, spine.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, spine, "show bgp ipv4 uni summ json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=125, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(spine.name) + assert result is None, assertmsg + # tgen.mininet_cli() + + +def test_multicast_groups_on_rp(): + "Ensure the multicast groups show up on the spine" + # This test implicitly tests the auto mcast groups + # of the created vlans and then the auto-joins that + # pim will do to the RP( spine ) + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + spine = tgen.gears["spine"] + json_file = "{}/{}/join-info.json".format(CWD, spine.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, spine, "show ip pim join json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(spine.name) + assert result is None, assertmsg + # tgen.mininet_cli() + + +def test_shutdown_check_stderr(): + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + pytest.skip("Skipping test for Stderr output and memory leaks") + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Verifying unexpected STDERR output from daemons") + + router_list = tgen.routers().values() + for router in router_list: + router.stop() + + log = tgen.net[router.name].getStdErr("pimd") + if log: + logger.error("PIMd StdErr Log:" + log) + log = tgen.net[router.name].getStdErr("bgpd") + if log: + logger.error("BGPd StdErr Log:" + log) + log = tgen.net[router.name].getStdErr("zebra") + if log: + logger.error("Zebra StdErr Log:" + log) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/evpn_type5_test_topo1/__init__.py b/tests/topotests/evpn_type5_test_topo1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json b/tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json new file mode 100644 index 0000000..14842da --- /dev/null +++ b/tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json @@ -0,0 +1,887 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "e1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "1", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network":"10.1.1.1/32", + "next_hop":"Null0", + "vrf": "RED" + }, + { + "network":"10::1/128", + "next_hop":"Null0", + "vrf": "RED" + } + ] + }, + "r2": { + "links": { + "e1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "e1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "BLUE", + "id": "1" + }, + { + "name": "GREEN", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "2", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "2", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link2": {} + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network":"20.1.1.1/32", + "next_hop":"Null0", + "vrf": "BLUE" + }, + { + "network":"20::1/128", + "next_hop":"Null0", + "vrf": "BLUE" + }, + { + "network":"30.1.1.1/32", + "next_hop":"Null0", + "vrf": "GREEN" + }, + { + "network":"30::1/128", + "next_hop":"Null0", + "vrf": "GREEN" + } + ] + }, + "e1": { + "links": { + "r1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "d1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "d2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1", + "vni": 75100 + }, + { + "name": "BLUE", + "id": "2", + "vni": 75200 + }, + { + "name": "GREEN", + "id": "3", + "vni": 75300 + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "e1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "e1": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link1": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link2": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "e1-link1": { + "deactivate": "ipv4" + } + } + }, + "d2": { + "dest_link": { + "e1-link1": { + "deactivate": "ipv4" + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "neighbor": { + "d1": { + "ipv4":{ + "e1-link1": "activate" + } + }, + "d2": { + "ipv4":{ + "e1-link1": "activate" + } + } + }, + "advertise-all-vni": true + } + } + } + } + ] + }, + "d1": { + "links": { + "e1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1", + "vni": 75100 + }, + { + "name": "BLUE", + "id": "2", + "vni": 75200 + }, + { + "name": "GREEN", + "id": "3", + "vni": 75300 + } + ], + "bgp": + [ + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "e1": { + "dest_link": { + "d1-link1": { + "deactivate": "ipv4" + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "neighbor": { + "e1": { + "ipv4":{ + "d1-link1": "activate" + } + } + }, + "advertise-all-vni": true + } + } + } + }, + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d1": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link1": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link2": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + } + ] + }, + "d2": { + "links": { + "e1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1", + "vni": 75100 + }, + { + "name": "BLUE", + "id": "2", + "vni": 75200 + }, + { + "name": "GREEN", + "id": "3", + "vni": 75300 + } + ], + "bgp": + [ + { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "e1": { + "dest_link": { + "d2-link1": { + "deactivate": "ipv4" + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "neighbor": { + "e1": { + "ipv4":{ + "d2-link1": "activate" + } + } + }, + "advertise-all-vni": true + } + } + } + }, + { + "local_as": "200", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d2": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link1": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link2": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + } + ] + }, + "r3": { + "links": { + "d1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "d2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "3", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r3": {} + } + }, + "d2": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r3": {} + } + }, + "d2": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + ] + }, + "r4": { + "links": { + "d1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "d1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "d2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "d2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "BLUE", + "id": "1" + }, + { + "name": "GREEN", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "4", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link1": {} + } + }, + "d2": { + "dest_link": { + "r4-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link1": {} + } + }, + "d2": { + "dest_link": { + "r4-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "4", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link2": {} + } + }, + "d2": { + "dest_link": { + "r4-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link2": {} + } + }, + "d2": { + "dest_link": { + "r4-link2": {} + } + } + } + } + } + } + } + ] + } + } +} diff --git a/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json b/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json new file mode 100644 index 0000000..dd41270 --- /dev/null +++ b/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json @@ -0,0 +1,1004 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "e1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "1", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network":"10.1.1.1/32", + "next_hop":"Null0", + "vrf": "RED" + }, + { + "network":"10::1/128", + "next_hop":"Null0", + "vrf": "RED" + } + ] + }, + "r2": { + "links": { + "e1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "e1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "BLUE", + "id": "1" + }, + { + "name": "GREEN", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "2", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "2", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network":"20.1.1.1/32", + "next_hop":"Null0", + "vrf": "BLUE" + }, + { + "network":"20::1/128", + "next_hop":"Null0", + "vrf": "BLUE" + }, + { + "network":"30.1.1.1/32", + "next_hop":"Null0", + "vrf": "GREEN" + }, + { + "network":"30::1/128", + "next_hop":"Null0", + "vrf": "GREEN" + } + ] + }, + "e1": { + "links": { + "r1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "d1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "d2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1", + "vni": 75100 + }, + { + "name": "BLUE", + "id": "2", + "vni": 75200 + }, + { + "name": "GREEN", + "id": "3", + "vni": 75300 + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "e1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "e1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "e1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "deactivate": "ipv4" + } + } + }, + "d2": { + "dest_link": { + "e1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "deactivate": "ipv4" + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "neighbor": { + "d1": { + "ipv4":{ + "e1-link1": "activate" + } + }, + "d2": { + "ipv4":{ + "e1-link1": "activate" + } + } + }, + "advertise-all-vni": true + } + } + } + } + ] + }, + "d1": { + "links": { + "e1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1", + "vni": 75100 + }, + { + "name": "BLUE", + "id": "2", + "vni": 75200 + }, + { + "name": "GREEN", + "id": "3", + "vni": 75300 + } + ], + "bgp": + [ + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "e1": { + "dest_link": { + "d1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "deactivate": "ipv4" + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "neighbor": { + "e1": { + "ipv4":{ + "d1-link1": "activate" + } + } + }, + "advertise-all-vni": true + } + } + } + }, + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + } + ] + }, + "d2": { + "links": { + "e1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1", + "vni": 75100 + }, + { + "name": "BLUE", + "id": "2", + "vni": 75200 + }, + { + "name": "GREEN", + "id": "3", + "vni": 75300 + } + ], + "bgp": + [ + { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "e1": { + "dest_link": { + "d2-link1": { + "deactivate": "ipv4", + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "neighbor": { + "e1": { + "ipv4":{ + "d2-link1": "activate" + } + } + }, + "advertise-all-vni": true + } + } + } + }, + { + "local_as": "200", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + } + ] + }, + "r3": { + "links": { + "d1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "d2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "3", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "d2": { + "dest_link": { + "r3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "d2": { + "dest_link": { + "r3": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ] + }, + "r4": { + "links": { + "d1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "d1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "d2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "d2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "BLUE", + "id": "1" + }, + { + "name": "GREEN", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "4", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "d2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "d2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + }, + { + "local_as": "4", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "d2": { + "dest_link": { + "r4-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + }, + "d2": { + "dest_link": { + "r4-link2": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + } + } + } + ] + } + } +} + diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py new file mode 100644 index 0000000..4586866 --- /dev/null +++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py @@ -0,0 +1,1007 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test EVPN-Type5 functionality: +1. In absence of an overlay index all IP-Prefixes(RT-5) + are advertised with default values for below parameters: + --> Ethernet Tag ID = GW IP address = ESI=0 +2. EVPN CLI output and JSON format validation. +3. RT verification(auto) +""" + +import os +import sys +import time +import pytest +import platform +from copy import deepcopy + + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topotest import version_cmp +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + create_static_routes, + create_vrf_cfg, + check_router_status, + configure_vxlan, + configure_brctl, + verify_vrf_vni, + verify_cli_json, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_attributes_for_evpn_routes, +) +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Reading the data from JSON File for topology creation +# Global variables +TCPDUMP_FILE = "evpn_log.txt" +NETWORK1_1 = {"ipv4": "10.1.1.1/32", "ipv6": "10::1/128"} +NETWORK1_2 = {"ipv4": "40.1.1.1/32", "ipv6": "40::1/128"} +NETWORK1_3 = {"ipv4": "40.1.1.2/32", "ipv6": "40::2/128"} +NETWORK1_4 = {"ipv4": "40.1.1.3/32", "ipv6": "40::3/128"} +NETWORK2_1 = {"ipv4": "20.1.1.1/32", "ipv6": "20::1/128"} +NETWORK3_1 = {"ipv4": "30.1.1.1/32", "ipv6": "30::1/128"} +NETWORK4_1 = {"ipv4": "100.1.1.1/32 ", "ipv6": "100::100/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} +VNI_1 = 75100 +VNI_2 = 75200 +VNI_3 = 75300 +MAC_1 = "00:80:48:ba:d1:00" +MAC_2 = "00:80:48:ba:d1:01" +MAC_3 = "00:80:48:ba:d1:02" +BRCTL_1 = "br100" +BRCTL_2 = "br200" +BRCTL_3 = "br300" +VXLAN_1 = "vxlan75100" +VXLAN_2 = "vxlan75200" +VXLAN_3 = "vxlan75300" +BRIDGE_INTF1 = "120.0.0.1" +BRIDGE_INTF2 = "120.0.0.2" +BRIDGE_INTF3 = "120.0.0.3" +MULTICAST_MAC1 = "01:00:5e:00:52:02" + +VXLAN = { + "vxlan_name": [VXLAN_1, VXLAN_2, VXLAN_3], + "vxlan_id": [75100, 75200, 75300], + "dstport": 4789, + "local_addr": {"e1": BRIDGE_INTF1, "d1": BRIDGE_INTF2, "d2": BRIDGE_INTF3}, + "learning": "no", +} +BRCTL = { + "brctl_name": [BRCTL_1, BRCTL_2, BRCTL_3], + "addvxlan": [VXLAN_1, VXLAN_2, VXLAN_3], + "vrf": ["RED", "BLUE", "GREEN"], + "stp": [0, 0, 0], +} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/evpn_type5_chaos_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + if version_cmp(platform.release(), "4.19") < 0: + error_msg = ( + 'EVPN tests will not run (have kernel "{}", ' + "but it requires >= 4.19)".format(platform.release()) + ) + pytest.skip(error_msg) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Pre-requisite config for testsuite") + prerequisite_config_for_test_suite(tgen) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def prerequisite_config_for_test_suite(tgen): + """ + API to do prerequisite config for testsuite + + parameters: + ----------- + * `tgen`: topogen object + """ + + step("Configure vxlan, bridge interface") + for dut in ["e1", "d1", "d2"]: + step("[DUT: ]Configure vxlan") + vxlan_input = { + dut: { + "vxlan": [ + { + "vxlan_name": VXLAN["vxlan_name"], + "vxlan_id": VXLAN["vxlan_id"], + "dstport": VXLAN["dstport"], + "local_addr": VXLAN["local_addr"][dut], + "learning": VXLAN["learning"], + } + ] + } + } + + result = configure_vxlan(tgen, vxlan_input) + assert result is True, "Testcase :Failed \n Error: {}".format(result) + + step("Configure bridge interface") + brctl_input = { + dut: { + "brctl": [ + { + "brctl_name": BRCTL["brctl_name"], + "addvxlan": BRCTL["addvxlan"], + "vrf": BRCTL["vrf"], + "stp": BRCTL["stp"], + } + ] + } + } + result = configure_brctl(tgen, topo, brctl_input) + assert result is True, "Testcase :Failed \n Error: {}".format(result) + + step("Configure default routes") + add_default_routes(tgen) + + +def add_default_routes(tgen): + """ + API to do prerequisite config for testsuite + + parameters: + ----------- + * `tgen`: topogen object + """ + + step("Add default routes..") + + default_routes = { + "e1": { + "static_routes": [ + { + "network": "{}/32".format(VXLAN["local_addr"]["d1"]), + "next_hop": topo["routers"]["d1"]["links"]["e1-link1"][ + "ipv4" + ].split("/")[0], + }, + { + "network": "{}/32".format(VXLAN["local_addr"]["d2"]), + "next_hop": topo["routers"]["d2"]["links"]["e1-link1"][ + "ipv4" + ].split("/")[0], + }, + ] + }, + "d1": { + "static_routes": [ + { + "network": "{}/32".format(VXLAN["local_addr"]["e1"]), + "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][ + "ipv4" + ].split("/")[0], + }, + { + "network": "{}/32".format(VXLAN["local_addr"]["d2"]), + "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][ + "ipv4" + ].split("/")[0], + }, + ] + }, + "d2": { + "static_routes": [ + { + "network": "{}/32".format(VXLAN["local_addr"]["d1"]), + "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][ + "ipv4" + ].split("/")[0], + }, + { + "network": "{}/32".format(VXLAN["local_addr"]["e1"]), + "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][ + "ipv4" + ].split("/")[0], + }, + ] + }, + } + + result = create_static_routes(tgen, default_routes) + assert result is True, "Testcase :Failed \n Error: {}".format(result) + + +def test_verify_overlay_index_p1(request): + """ + In absence of an overlay index all IP-Prefixes(RT-5) + are advertised with default values for below parameters: + --> Ethernet Tag ID = GW IP address = ESI=0 + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Following steps are taken care in base config:") + step( + "Configure BGP neighborship for both address families" + "(IPv4 & IPv6) between Edge-1 and VFN routers(R1 and R2)" + ) + step( + "Advertise prefixes from VNF routers R1 and R2 in associated " + "VRFs for both address-family." + ) + step("Advertise VRF routes as in EVPN address family from Edge-1 " "router.") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK2_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK3_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify: Prefixes are received in all VRFs on Edge-1 router.") + + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_rib(tgen, addr_type, "e1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r2"]} + result = verify_rib(tgen, addr_type, "e1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that EVPN routes, received on DCG-1 and DCG-2 do not " + "carry any overlay index and these indexes are set to default " + "value=0. " + ) + + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + + result = verify_attributes_for_evpn_routes( + tgen, topo, "d1", input_routes, ethTag=0 + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_attributes_for_evpn_routes( + tgen, topo, "d2", input_routes, ethTag=0 + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_evpn_cli_json_available_p1(request): + """ + EVPN CLI output and JSON format validation. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Need to verify below CLIs and associated JSON format " "outputs:") + + input_dict = { + "e1": { + "cli": [ + "show evpn vni detail", + "show bgp l2vpn evpn all overlay", + "show bgp l2vpn evpn vni", + ] + } + } + + result = verify_cli_json(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_RT_verification_auto_p0(request): + """ + RT verification(auto) + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise overlapping prefixes from VNFs R1 and R2 in all VRFs " + "RED, GREEN and BLUE 100.1.1.1/32 and 100::100/128" + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that Edge-1 receives same prefixes in all 3 VRFs via " + "corresponding next-hop in associated VRF sh bgp vrf all" + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r1": { + "static_routes": [ + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = verify_rib(tgen, addr_type, "e1", input_routes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure 4-byte local AS number on Edge-1 and establish EVPN " + "neighborship with DCG-1 & DCG-2." + ) + + topo_local = deepcopy(topo) + + step("Delete BGP config for vrf RED.") + + input_dict_vni = { + "e1": { + "vrfs": [ + {"name": "RED", "no_vni": VNI_1}, + {"name": "BLUE", "no_vni": VNI_2}, + {"name": "GREEN", "no_vni": VNI_3}, + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict_2 = {} + for dut in ["e1"]: + temp = {dut: {"bgp": []}} + input_dict_2.update(temp) + + INDEX = [0, 1, 2, 3] + VRFS = ["RED", "BLUE", "GREEN", None] + AS_NUM = [100, 100, 100, 100] + + for index, vrf, as_num in zip(INDEX, VRFS, AS_NUM): + topo_local["routers"][dut]["bgp"][index]["local_as"] = 4294967293 + if vrf: + temp[dut]["bgp"].append( + {"local_as": as_num, "vrf": vrf, "delete": True} + ) + else: + temp[dut]["bgp"].append({"local_as": as_num, "delete": True}) + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + result = create_router_bgp(tgen, topo_local["routers"]) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict_vni = { + "e1": { + "vrfs": [ + {"name": "RED", "vni": VNI_1}, + {"name": "BLUE", "vni": VNI_2}, + {"name": "GREEN", "vni": VNI_3}, + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that all overlapping prefixes across different VRFs are " + "advertised in EVPN with unique RD value(auto derived)." + ) + step( + "Verify that FRR uses only the lower 2 bytes of ASN+VNI for auto " + "derived RT value." + ) + + for addr_type in ADDR_TYPES: + input_routes_1 = { + "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} + } + input_routes_2 = { + "r2": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "BLUE"}]} + } + input_routes_3 = { + "r2": { + "static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "GREEN"}] + } + } + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes_1, rd="auto", rd_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes_1, rt="auto", rt_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes_2, rd="auto", rd_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes_2, rt="auto", rt_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes_3, rd="auto", rd_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes_3, rt="auto", rt_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that DCG-1(iBGP peer) automatically imports the prefixes" + " from EVPN address-family to respective VRFs." + ) + step( + "Verify if DCG-2(eBGP peer) automatically imports the prefixes " + "from EVPN address-family to respective VRFs or not." + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r1": { + "static_routes": [ + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = verify_rib(tgen, addr_type, "d1", input_routes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Change the VNI number for all 3 VRFs on Edge-1 as:" + "RED : 75400, GREEN: 75500, BLUE: 75600" + ) + + input_dict_vni = { + "e1": { + "vrfs": [ + {"name": "RED", "no_vni": VNI_1}, + {"name": "BLUE", "no_vni": VNI_2}, + {"name": "GREEN", "no_vni": VNI_3}, + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict_vni = { + "e1": { + "vrfs": [ + {"name": "RED", "vni": 75400}, + {"name": "BLUE", "vni": 75500}, + {"name": "GREEN", "vni": 75600}, + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Delete configured vxlan") + dut = "e1" + vxlan_input = { + dut: { + "vxlan": [ + { + "vxlan_name": VXLAN["vxlan_name"], + "vxlan_id": VXLAN["vxlan_id"], + "dstport": VXLAN["dstport"], + "local_addr": VXLAN["local_addr"][dut], + "learning": VXLAN["learning"], + "delete": True, + } + ] + } + } + + result = configure_vxlan(tgen, vxlan_input) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configured vxlan") + VXLAN["vxlan_id"] = [75400, 75500, 75600] + vxlan_input = { + dut: { + "vxlan": [ + { + "vxlan_name": VXLAN["vxlan_name"], + "vxlan_id": VXLAN["vxlan_id"], + "dstport": VXLAN["dstport"], + "local_addr": VXLAN["local_addr"][dut], + "learning": VXLAN["learning"], + } + ] + } + } + + result = configure_vxlan(tgen, vxlan_input) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure bridge interface") + brctl_input = { + dut: { + "brctl": [ + { + "brctl_name": BRCTL["brctl_name"], + "addvxlan": BRCTL["addvxlan"], + "vrf": BRCTL["vrf"], + "stp": BRCTL["stp"], + } + ] + } + } + result = configure_brctl(tgen, topo, brctl_input) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on Edge-1 that auto derived RT value has changed for " + "each VRF based on VNI number.." + ) + + input_dict = { + "e1": { + "vrfs": [ + {"RED": {"vni": 75400}}, + {"BLUE": {"vni": 75500}}, + {"GREEN": {"vni": 75600}}, + ] + } + } + + result = verify_vrf_vni(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on Edge-1 that auto derived RT value has changed for " + "each VRF based on VNI number." + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} + } + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes, rt="auto", rt_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify on DCG-2 that prefixes are not imported from EVPN " + "address-family to VRFs as RT values are different on sending(" + "edge-1) and receiving(DCG-2) end." + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} + } + + result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} RIB \n " + "Found: {}".format(tc_name, "d2", result) + ) + + step( + "Revert back to original VNI number for all 3 VRFs on Edge-1 " + "as: RED : 75100, GREEN: 75200, BLUE: 75300" + ) + + input_dict_vni = { + "e1": { + "vrfs": [ + {"name": "RED", "no_vni": 75400}, + {"name": "BLUE", "no_vni": 75500}, + {"name": "GREEN", "no_vni": 75600}, + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict_vni = { + "e1": { + "vrfs": [ + {"name": "RED", "vni": VNI_1}, + {"name": "BLUE", "vni": VNI_2}, + {"name": "GREEN", "vni": VNI_3}, + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Delete configured vxlan") + dut = "e1" + vxlan_input = { + dut: { + "vxlan": [ + { + "vxlan_name": VXLAN["vxlan_name"], + "vxlan_id": VXLAN["vxlan_id"], + "dstport": VXLAN["dstport"], + "local_addr": VXLAN["local_addr"][dut], + "learning": VXLAN["learning"], + "delete": True, + } + ] + } + } + result = configure_vxlan(tgen, vxlan_input) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configured vxlan") + VXLAN["vxlan_id"] = [75100, 75200, 75300] + vxlan_input = { + dut: { + "vxlan": [ + { + "vxlan_name": VXLAN["vxlan_name"], + "vxlan_id": VXLAN["vxlan_id"], + "dstport": VXLAN["dstport"], + "local_addr": VXLAN["local_addr"][dut], + "learning": VXLAN["learning"], + } + ] + } + } + result = configure_vxlan(tgen, vxlan_input) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure bridge interface") + brctl_input = { + dut: { + "brctl": [ + { + "brctl_name": BRCTL["brctl_name"], + "addvxlan": BRCTL["addvxlan"], + "vrf": BRCTL["vrf"], + "stp": BRCTL["stp"], + } + ] + } + } + result = configure_brctl(tgen, topo, brctl_input) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on Edge-1 that auto derived RT value has changed for " + "each VRF based on VNI number." + ) + step( + "Verify that DCG-1(iBGP peer) automatically imports the prefixes" + " from EVPN address-family to respective VRFs." + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} + } + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes, rt="auto", rt_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "d1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Test with smaller VNI numbers (1-75000)") + + input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": VNI_1}]}} + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "vni": 111}]}} + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that DCG-2 receives EVPN prefixes along with auto " + "derived RT values(based on smaller VNI numbers)" + ) + + for addr_type in ADDR_TYPES: + input_routes_1 = { + "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} + } + + result = verify_attributes_for_evpn_routes( + tgen, topo, "d2", input_routes_1, rt="auto", rt_peer="e1", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Malformed Auto-RT value should not be accepted in {} \n " + "Found: {}".format(tc_name, "d2", result) + ) + + step("Configure VNI number more than boundary limit (16777215)") + + input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": 111}]}} + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "vni": 16777215}]}} + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("CLI error for malformed VNI.") + input_dict = { + "e1": { + "vrfs": [{"RED": {"vni": 16777215, "routerMac": "None", "state": "Down"}}] + } + } + + result = verify_vrf_vni(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_routes_1 = { + "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} + } + + result = verify_attributes_for_evpn_routes( + tgen, topo, "d2", input_routes_1, rt="auto", rt_peer="e1", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Malformed Auto-RT value should not be accepted in {} \n " + "Found: {}".format(tc_name, "d2", result) + ) + + step("Un-configure VNI number more than boundary limit (16777215)") + + input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": 16777215}]}} + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py new file mode 100644 index 0000000..10d216a --- /dev/null +++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py @@ -0,0 +1,2322 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test EVPN-Type5 functionality: + +1. RD verification (manual/auto). +2. RT verification(manual) +3. In an active/standby EVPN implementation, if active DCG goes down, + secondary takes over. +4. EVPN routes are advertised/withdrawn, based on VNFs + advertising/withdrawing IP prefixes. +5. Route-map operations for EVPN address family. +6. BGP attributes for EVPN address-family. +""" + +import os +import sys +import json +import time +import pytest +import platform +from copy import deepcopy + + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topotest import version_cmp +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + create_route_maps, + create_static_routes, + create_vrf_cfg, + check_router_status, + apply_raw_config, + configure_vxlan, + configure_brctl, + create_interface_in_kernel, + kill_router_daemons, + start_router_daemons, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_best_path_as_per_bgp_attribute, + verify_attributes_for_evpn_routes, + verify_evpn_routes, +) +from lib.topojson import build_topo_from_json, build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK1_1 = {"ipv4": "10.1.1.1/32", "ipv6": "10::1/128"} +NETWORK1_2 = {"ipv4": "40.1.1.1/32", "ipv6": "40::1/128"} +NETWORK1_3 = {"ipv4": "40.1.1.2/32", "ipv6": "40::2/128"} +NETWORK1_4 = {"ipv4": "40.1.1.3/32", "ipv6": "40::3/128"} +NETWORK2_1 = {"ipv4": "20.1.1.1/32", "ipv6": "20::1/128"} +NETWORK3_1 = {"ipv4": "30.1.1.1/32", "ipv6": "30::1/128"} +NETWORK4_1 = {"ipv4": "100.1.1.1/32 ", "ipv6": "100::100/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} +VNI_1 = 75100 +VNI_2 = 75200 +VNI_3 = 75300 +MAC_1 = "00:80:48:ba:d1:00" +MAC_2 = "00:80:48:ba:d1:01" +MAC_3 = "00:80:48:ba:d1:02" +BRCTL_1 = "br100" +BRCTL_2 = "br200" +BRCTL_3 = "br300" +VXLAN_1 = "vxlan75100" +VXLAN_2 = "vxlan75200" +VXLAN_3 = "vxlan75300" +BRIDGE_INTF1 = "120.0.0.1" +BRIDGE_INTF2 = "120.0.0.2" +BRIDGE_INTF3 = "120.0.0.3" + +VXLAN = { + "vxlan_name": [VXLAN_1, VXLAN_2, VXLAN_3], + "vxlan_id": [75100, 75200, 75300], + "dstport": 4789, + "local_addr": {"e1": BRIDGE_INTF1, "d1": BRIDGE_INTF2, "d2": BRIDGE_INTF3}, + "learning": "no", +} +BRCTL = { + "brctl_name": [BRCTL_1, BRCTL_2, BRCTL_3], + "addvxlan": [VXLAN_1, VXLAN_2, VXLAN_3], + "vrf": ["RED", "BLUE", "GREEN"], + "stp": [0, 0, 0], +} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/evpn_type5_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + topo = tgen.json_topo + + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + if version_cmp(platform.release(), "4.19") < 0: + error_msg = ( + 'EVPN tests will not run (have kernel "{}", ' + "but it requires >= 4.19)".format(platform.release()) + ) + pytest.skip(error_msg) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Pre-requisite config for testsuite") + prerequisite_config_for_test_suite(tgen) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def prerequisite_config_for_test_suite(tgen): + """ + API to do prerequisite config for testsuite + + parameters: + ----------- + * `tgen`: topogen object + """ + + step("Configure vxlan, bridge interface") + for dut in ["e1", "d1", "d2"]: + step("[DUT: ]Configure vxlan") + vxlan_input = { + dut: { + "vxlan": [ + { + "vxlan_name": VXLAN["vxlan_name"], + "vxlan_id": VXLAN["vxlan_id"], + "dstport": VXLAN["dstport"], + "local_addr": VXLAN["local_addr"][dut], + "learning": VXLAN["learning"], + } + ] + } + } + + result = configure_vxlan(tgen, vxlan_input) + assert result is True, "Testcase :Failed \n Error: {}".format(result) + + step("Configure bridge interface") + brctl_input = { + dut: { + "brctl": [ + { + "brctl_name": BRCTL["brctl_name"], + "addvxlan": BRCTL["addvxlan"], + "vrf": BRCTL["vrf"], + "stp": BRCTL["stp"], + } + ] + } + } + result = configure_brctl(tgen, topo, brctl_input) + assert result is True, "Testcase :Failed \n Error: {}".format(result) + + step("Configure default routes") + add_default_routes(tgen) + + +def add_default_routes(tgen): + """ + API to do prerequisite config for testsuite + + parameters: + ----------- + * `tgen`: topogen object + """ + + step("Add default routes..") + + default_routes = { + "e1": { + "static_routes": [ + { + "network": "{}/32".format(VXLAN["local_addr"]["d1"]), + "next_hop": topo["routers"]["d1"]["links"]["e1-link1"][ + "ipv4" + ].split("/")[0], + }, + { + "network": "{}/32".format(VXLAN["local_addr"]["d2"]), + "next_hop": topo["routers"]["d2"]["links"]["e1-link1"][ + "ipv4" + ].split("/")[0], + }, + ] + }, + "d1": { + "static_routes": [ + { + "network": "{}/32".format(VXLAN["local_addr"]["e1"]), + "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][ + "ipv4" + ].split("/")[0], + }, + { + "network": "{}/32".format(VXLAN["local_addr"]["d2"]), + "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][ + "ipv4" + ].split("/")[0], + }, + ] + }, + "d2": { + "static_routes": [ + { + "network": "{}/32".format(VXLAN["local_addr"]["d1"]), + "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][ + "ipv4" + ].split("/")[0], + }, + { + "network": "{}/32".format(VXLAN["local_addr"]["e1"]), + "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][ + "ipv4" + ].split("/")[0], + }, + ] + }, + } + + result = create_static_routes(tgen, default_routes) + assert result is True, "Testcase :Failed \n Error: {}".format(result) + + +def test_RD_verification_manual_and_auto_p0(request): + """ + RD verification (manual/auto). + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise prefixes from VNF routers R1 and R2 in associated " + "VRFs for both address-family." + ) + step( + "Advertise vrf RED's routes in EVPN address family from Edge-1 router" + ", without manual configuration of RD." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK2_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK3_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify on DCG-1 and DCG-2:") + step("EVPN route for 10.1.1.1/32 has auto-assigned RD value.") + + for dut in ["d1", "d2"]: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_attributes_for_evpn_routes( + tgen, topo, dut, input_routes, rd="auto", rd_peer="e1" + ) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step( + "Configure RD for vrf RED manually as 50.50.50.50:50 and " + "advertise vrf RED's routes in EVPN address family from " + "Edge-1 router." + ) + + input_dict_rd = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": {"l2vpn": {"evpn": {"rd": "50.50.50.50:50"}}}, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rd) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("EVPN route for vrf RED has RD value as 50.50.50.50:50") + for dut in ["d1", "d2"]: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_attributes_for_evpn_routes( + tgen, topo, dut, input_routes, rd="50.50.50.50:50" + ) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step( + "Configure RD for vrf RED manually as 100.100.100.100:100 and " + "advertise vrf RED's routes in EVPN address family from Edge-1 " + "router." + ) + input_dict_rd = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "l2vpn": {"evpn": {"rd": "100.100.100.100:100"}} + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rd) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "EVPN route for vrf RED is overridden with RD value as " "100.100.100.100:100." + ) + + for dut in ["d1", "d2"]: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_attributes_for_evpn_routes( + tgen, topo, dut, input_routes, rd="100.100.100.100:100" + ) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step( + "Configure RD for vrf BLUE manually same as vrf RED " + "(100.100.100.100:100) and advertise vrf RED and BLUE's routes " + "in EVPN address family from Edge-1 router." + ) + + input_dict_rd = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "BLUE", + "address_family": { + "l2vpn": {"evpn": {"rd": "100.100.100.100:100"}} + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rd) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Delete manually configured RD and advertise vrf RED's routes " + "in EVPN address family from Edge-1 router." + ) + + input_dict_rd = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "l2vpn": {"evpn": {"no rd": "100.100.100.100:100"}} + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rd) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Configure same RD value for vrf GREEN, as auto generated RD " + "value for vrf RED on Edge-1 router." + ) + + input_dict_rd = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "GREEN", + "address_family": {"l2vpn": {"evpn": {"rd": "10.0.0.33:1"}}}, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rd) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Delete auto configured RD value from vrf RED in EVPN " "address family.") + + input_dict_rd = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "GREEN", + "address_family": {"l2vpn": {"evpn": {"no rd": "10.0.0.33:1"}}}, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rd) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure RD value as 100.100.100:100") + + input_dict_rd = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "GREEN", + "address_family": {"l2vpn": {"evpn": {"rd": "100.100.100:100"}}}, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rd) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_RT_verification_manual_p0(request): + """ + RT verification(manual) + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise prefixes from VNF routers R1 and R2 in associated " + "VRFs for both address-family." + ) + step("Advertise VRF routes as in EVPN address family from Edge-1 " "router.") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK2_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK3_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure RT for vrf RED manually as export 100:100 " + "and advertise vrf RED's routes in EVPN address family" + " from Edge-1 router." + ) + + input_dict_rt = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": {"neighbor": {"r1": {"dest_link": {"e1": {}}}}} + }, + "ipv6": { + "unicast": {"neighbor": {"r1": {"dest_link": {"e1": {}}}}} + }, + "l2vpn": { + "evpn": {"route-target": {"export": [{"value": "100:100"}]}} + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rt) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on dcg-1 and dcg-2, EVPN route for 10.1.1.1/32" + " and 10::1/128 have RT value as 100:100." + ) + + for dut in ["d1", "d2"]: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_attributes_for_evpn_routes( + tgen, topo, dut, input_routes, rt="100:100" + ) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step( + "Configure RT for vrf RED manually as export 500:500 and" + " advertise vrf RED's routes in EVPN address family from" + " e1 router." + ) + + input_dict_rt = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "l2vpn": { + "evpn": {"route-target": {"export": [{"value": "500:500"}]}} + } + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rt) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on dcg-1 and dcg-2, EVPN route for 10.1.1.1/32" + " and 10::1/128 have RT value as 500:500." + ) + + for dut in ["d1", "d2"]: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_attributes_for_evpn_routes( + tgen, topo, dut, input_routes, rt=["100:100", "500:500"] + ) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step( + "Import RT value 100:100 and 500:500 in vrf BLUE manually on" + " peer router DCG-1 and DCG-2." + ) + + input_dict_rt = { + "d1": { + "bgp": [ + { + "local_as": "100", + "vrf": "BLUE", + "address_family": { + "l2vpn": { + "evpn": { + "route-target": { + "import": [ + {"value": "100:100"}, + {"value": "500:500"}, + ] + } + } + } + }, + } + ] + }, + "d2": { + "bgp": [ + { + "local_as": "200", + "vrf": "BLUE", + "address_family": { + "l2vpn": { + "evpn": { + "route-target": { + "import": [ + {"value": "100:100"}, + {"value": "500:500"}, + ] + } + } + } + }, + } + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_rt) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "EVPN route for 10.1.1.1/32 and 10::1 should be installed " + "in vrf BLUE on DCG-1 and DCG-2 and further advertised to " + "VNF router." + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r1": { + "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "BLUE"}] + } + } + result = verify_rib(tgen, addr_type, "d1", input_routes) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step( + "Delete import RT value 500:500 in vrf BLUE manually on " + "peer router DCG-1 and DCG-2." + ) + + input_dict_rt = { + "d1": { + "bgp": [ + { + "local_as": "100", + "vrf": "BLUE", + "address_family": { + "l2vpn": { + "evpn": { + "route-target": { + "import": [{"value": "500:500", "delete": True}] + } + } + } + }, + } + ] + }, + "d2": { + "bgp": [ + { + "local_as": "200", + "vrf": "BLUE", + "address_family": { + "l2vpn": { + "evpn": { + "route-target": { + "import": [{"value": "500:500", "delete": True}] + } + } + } + }, + } + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_rt) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + for dut in ["d1", "d2"]: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_attributes_for_evpn_routes( + tgen, topo, dut, input_routes, rt=["100:100", "500:500"] + ) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step("Delete RT export value 100:100 for vrf RED on Edge-1") + + input_dict_rt = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "l2vpn": { + "evpn": { + "route-target": { + "export": [{"value": "100:100", "delete": True}] + } + } + } + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rt) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "EVPN route for 10.1.1.1/32 and 10::1 should be withdrawn " + "from vrf BLUE on DCG-1,DCG-2 and VNF router." + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r1": { + "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "BLUE"}] + } + } + result = verify_rib(tgen, addr_type, "d1", input_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} RIB \n " + "Found: {}".format(tc_name, "d1", result) + ) + + result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} RIB \n " + "Found: {}".format(tc_name, "d2", result) + ) + + step( + "Configure RT value as 100:100000010000010000101010 to check " + "the boundary value." + ) + + input_dict_rt = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "l2vpn": { + "evpn": { + "route-target": { + "export": [ + {"value": "100:100000010000010000101010"} + ] + } + } + } + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rt) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "CLI error: RT value: 100:100000010000010000101010 should not " "be configured" + ) + + dut = "e1" + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_attributes_for_evpn_routes( + tgen, topo, dut, input_routes, rt="100:100000010000010000101010", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: RT value out of boundary error in {} \n " + "Found: {}".format(tc_name, dut, result) + ) + + write_test_footer(tc_name) + + +def test_active_standby_evpn_implementation_p1(request): + """ + In an active/standby EVPN implementation, if active DCG goes down, + secondary takes over. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Taken care in base config: Configure BGP neighborship for both " + "address families(IPv4 & IPv6) between DCG-1/DCG-2 and VFN routers" + "(R3 and R4)." + ) + + step( + "BGP neighborships come up within defined VRFs. Please use below " + "command: sh bgp vrf all summary" + ) + + result = verify_bgp_convergence(tgen, topo, "d1") + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + result = verify_bgp_convergence(tgen, topo, "d2") + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Advertise prefixes from VNF routers R3 and R4 in associated " + "VRFs for both address-families." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Redistribute static in (IPv4 and IPv6) address-family " + "on Edge-1 for all VRFs." + ) + + input_dict_2 = {} + for dut in ["r3", "r4"]: + temp = {dut: {"bgp": []}} + input_dict_2.update(temp) + + if dut == "r3": + VRFS = ["RED"] + AS_NUM = [3] + if dut == "r4": + VRFS = ["BLUE", "GREEN"] + AS_NUM = [4, 4] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Prefixes are received in respective VRFs on DCG-1/DCG-2.") + + for addr_type in ADDR_TYPES: + input_routes = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = verify_rib(tgen, addr_type, "d1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Taken care in base config: Advertise VRF routes in EVPN " + "address-family from DCG-1 and DCG-2 router." + ) + + step("Verify on Edge-1 that EVPN routes are installed via next-hop " "as DCG-2.") + + for addr_type in ADDR_TYPES: + input_routes = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + if addr_type == "ipv4": + result = verify_rib( + tgen, addr_type, "e1", input_routes, next_hop=BRIDGE_INTF2 + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + else: + result = verify_rib(tgen, addr_type, "e1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure 'next-hop self' on DCG-1 for peer Edge-1 in EVPN " "address-family." + ) + + input_dict_3 = { + "d1": { + "bgp": [ + { + "local_as": "100", + "address_family": { + "l2vpn": { + "evpn": { + "neighbor": { + "e1": { + "ipv4": {"d1-link1": {"next_hop_self": True}} + } + } + } + } + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + logger.info( + "Creating route-map so ipv6 glpbal ip wpuld be preferred " "as next-hop" + ) + + step( + "Verify on Edge-1 that EVPN routes are now preferred via " + "next-hop as DCG-1(iBGP) due to shortest AS-Path." + ) + + for addr_type in ADDR_TYPES: + + logger.info("Verifying only ipv4 routes") + if addr_type != "ipv4": + continue + + input_routes = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + next_hop = topo["routers"]["d1"]["links"]["e1-link1"]["ipv4"].split("/")[0] + + result = verify_rib(tgen, addr_type, "e1", input_routes, next_hop=next_hop) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_evpn_routes_from_VNFs_p1(request): + """ + EVPN routes are advertised/withdrawn, based on VNFs + advertising/withdrawing IP prefixes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise prefixes from VNF routers R1 and R2 in associated " + "VRFs for both address-family." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK2_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK3_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Taken care in base config: Advertise VNFs'(R1 and R2) " + "originated routes in EVPN address-family from Edge-1 to " + "DCG-1 and DCG-2 routers." + ) + step( + "Taken care in base config: Advertise IPv4 and IPv6 routes " + "from default vrf in EVPN address-family from Edge-1." + ) + + step( + "Verify on DCG-2 that VNF routes are received in respective " + "VRFs along with auto derived RD/RT values 'show bgp l2vpn evpn'" + ) + for dut in ["d1", "d2"]: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_evpn_routes(tgen, topo, dut, input_routes) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + input_routes = {key: topo["routers"][key] for key in ["r2"]} + result = verify_evpn_routes(tgen, topo, dut, input_routes) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step( + "Verify on R3 and R4 that DCG-2 further advertises all EVPN " + "routes to corresponding VRFs." + ) + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_rib(tgen, addr_type, "r3", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r2"]} + result = verify_rib(tgen, addr_type, "r4", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that DCG-2 receives EVPN routes associated to default " + "VRF and install in default IP routing table as well." + ) + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r2"]} + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Withdraw the IP prefixes from VFN(R1).") + dut = "r1" + input_dict_2 = {} + static_routes = topo["routers"][dut]["static_routes"] + for static_route in static_routes: + static_route["delete"] = True + temp = {dut: {"static_routes": [static_route]}} + input_dict_2.update(temp) + + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that DCG-2 removes EVPN routes corresponding to vrf RED and " + "send an withdraw to VNF(R3) as well." + ) + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} RIB \n " + "Found: {}".format(tc_name, "d2", result) + ) + + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_rib(tgen, addr_type, "r3", input_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} RIB \n " + "Found: {}".format(tc_name, "r3", result) + ) + + step("Re-advertise IP prefixes from VFN(R1).") + step( + "Advertise prefixes from VNF routers R1 and R2 in associated " + "VRFs for both address-family." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK2_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK3_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that DCG-2 receives EVPN routes corresponding to vrf RED " + "again and send an update to VNF(R3) as well." + ) + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_rib(tgen, addr_type, "r3", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Delete vrf BLUE from router Edge-1") + input_dict_3 = {"e1": {"vrfs": [{"name": "BLUE", "id": "2", "delete": True}]}} + + result = create_vrf_cfg(tgen, input_dict_3) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that DCG-2 removes EVPN routes corresponding to " + "vrf BLUE and send an withdraw to VNF(R4) as well." + ) + for addr_type in ADDR_TYPES: + input_routes = { + "r2": {"static_routes": [{"network": NETWORK2_1[addr_type], "vrf": "BLUE"}]} + } + + result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} RIB \n " + "Found: {}".format(tc_name, "d2", result) + ) + + result = verify_rib(tgen, addr_type, "r4", input_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} RIB \n " + "Found: {}".format(tc_name, "r4", result) + ) + + step("Add vrf BLUE on router Edge-1 again.") + interface = topo["routers"]["e1"]["links"]["r2-link1"]["interface"] + input_dict_3 = { + "e1": { + "links": { + "r2-link1": { + "interface": interface, + "ipv4": "auto", + "ipv6": "auto", + "vrf": "BLUE", + } + }, + "vrfs": [{"name": "BLUE", "id": "2"}], + } + } + result = create_vrf_cfg(tgen, input_dict_3) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + logger.info( + "After deleting VRFs ipv6 addresses wil be deleted " + "from kernel Adding back ipv6 addresses" + ) + dut = "e1" + vrfs = ["BLUE"] + + for vrf in vrfs: + for c_link, c_data in topo["routers"][dut]["links"].items(): + if "vrf" in c_data: + if c_data["vrf"] != vrf: + continue + + intf_name = c_data["interface"] + intf_ipv6 = c_data["ipv6"] + + create_interface_in_kernel( + tgen, dut, intf_name, intf_ipv6, vrf, create=False + ) + + result = verify_bgp_convergence(tgen, topo, dut) + assert result is True, "Failed to converge on {}".format(dut) + + step( + "Verify that DCG-2 receives EVPN routes corresponding to " + "vrf BLUE again and send an update to VNF(R4) as well." + ) + for addr_type in ADDR_TYPES: + input_routes = { + "r2": {"static_routes": [{"network": NETWORK2_1[addr_type], "vrf": "BLUE"}]} + } + + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r4", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Withdraw IPv6 address-family in EVPN advertisements for " "VRF GREEN") + addr_type = "ipv6" + input_dict_4 = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "GREEN", + "address_family": { + "l2vpn": { + "evpn": { + "advertise": {addr_type: {"unicast": {"delete": True}}} + } + } + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that EVPN routes (IPv6)associated with vrf GREEN are " + "withdrawn from DCG-2 and VNF R4." + ) + input_routes = { + "r2": {"static_routes": [{"network": NETWORK3_1[addr_type], "vrf": "GREEN"}]} + } + + result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} RIB \n " + "Found: {}".format(tc_name, "d2", result) + ) + + result = verify_rib(tgen, addr_type, "r4", input_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} RIB \n " + "Found: {}".format(tc_name, "r4", result) + ) + + step("Advertise IPv6 address-family in EVPN advertisements " "for VRF GREEN.") + addr_type = "ipv6" + input_dict_4 = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "GREEN", + "address_family": { + "l2vpn": {"evpn": {"advertise": {addr_type: {"unicast": {}}}}} + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_routes = { + "r2": { + "static_routes": [{"network": NETWORK3_1[addr_type], "vrf": "GREEN"}] + } + } + + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r4", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize( + "attribute", [{"route-type": "prefix"}, {"vni": VNI_1}, {"rt": "300:300"}] +) +def test_route_map_operations_for_evpn_address_family_p1(request, attribute): + """ + Route-map operations for EVPN address family. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + step( + "Advertise prefixes from VNF routers R1 and R2 in associated " + "VRFs for both address-family." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK2_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK3_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise VRF routes in EVPN address family from Edge-1 router." + " Configure a route-map on e1 to filter EVPN routes based on" + " below keywords: route-type: prefix" + ) + + for key, value in attribute.items(): + if key == "rt": + logger.info("Creating extcommunity using raw_config") + raw_config = { + "d2": { + "raw_config": [ + "bgp extcommunity-list standard ECOMM300 permit {} {}".format( + key, value + ) + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + input_dict_1 = { + "e1": { + "route_maps": { + "rmap_route_type": [ + {"action": "permit", "set": {"extcommunity": {key: value}}} + ] + } + }, + "d2": { + "route_maps": { + "rmap_route_type": [ + {"action": "permit", "match": {"extcommunity": "ECOMM300"}} + ] + } + }, + } + + else: + input_dict_1 = { + "e1": { + "route_maps": { + "rmap_route_type": [ + {"action": "permit", "match": {"evpn": {key: value}}} + ] + } + }, + "d2": { + "route_maps": { + "rmap_route_type": [ + {"action": "permit", "match": {"evpn": {key: value}}} + ] + } + }, + } + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "e1": { + "bgp": [ + { + "local_as": "100", + "address_family": { + "l2vpn": { + "evpn": { + "neighbor": { + "d2": { + "ipv4": { + "e1-link1": { + "route_maps": [ + { + "name": "rmap_route_type", + "direction": "out", + } + ] + } + } + } + } + } + } + }, + } + ] + }, + "d2": { + "bgp": [ + { + "local_as": "200", + "address_family": { + "l2vpn": { + "evpn": { + "neighbor": { + "e1": { + "ipv4": { + "d2-link1": { + "route_maps": [ + { + "name": "rmap_route_type", + "direction": "in", + } + ] + } + } + } + } + } + } + }, + } + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on router DCG-2 that EVPN routes corresponding to all " + "VRFs are received. As all EVPN routes are type-5 only." + ) + + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_evpn_routes(tgen, topo, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_routes = {key: topo["routers"][key] for key in ["r2"]} + result = verify_evpn_routes(tgen, topo, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_evpn_address_family_with_graceful_restart_p0(request): + """ + Verify Graceful-restart function for EVPN address-family. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Redistribute static in (IPv4 and IPv6) address-family " + "on Edge-1 for all VRFs." + ) + + input_dict_2 = {} + for dut in ["r3", "r4"]: + temp = {dut: {"bgp": []}} + input_dict_2.update(temp) + + if dut == "r3": + VRFS = ["RED"] + AS_NUM = [3] + if dut == "r4": + VRFS = ["BLUE", "GREEN"] + AS_NUM = [4, 4] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on router Edge-1 that EVPN routes corresponding to " + "all VRFs are received from both routers DCG-1 and DCG-2" + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = verify_rib(tgen, addr_type, "e1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure DCG-2 as GR restarting node for EVPN session between" + " DCG-2 and EDGE-1, following by a session reset using 'clear bgp *'" + " command." + ) + + input_dict_gr = { + "d2": { + "bgp": [ + { + "local_as": "200", + "graceful-restart": { + "graceful-restart": True, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_gr) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that DCG-2 changes it's role to GR-restarting router " + "and EDGE-1 becomes the GR-helper." + ) + + step("Kill BGPd daemon on DCG-2.") + kill_router_daemons(tgen, "d2", ["bgpd"]) + + step( + "Verify that EDGE-1 keep stale entries for EVPN RT-5 routes " + "received from DCG-2 before the restart." + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + } + } + result = verify_evpn_routes(tgen, topo, "e1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that DCG-2 keeps BGP routes in Zebra until BGPd " + "comes up or end of 'rib-stale-time'" + ) + + step("Start BGPd daemon on DCG-2.") + start_router_daemons(tgen, "d2", ["bgpd"]) + + step("Verify that EDGE-1 removed all the stale entries.") + for addr_type in ADDR_TYPES: + input_routes = { + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + } + } + result = verify_evpn_routes(tgen, topo, "e1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that DCG-2 refresh zebra with EVPN routes. " + "(no significance of 'rib-stale-time'" + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + } + } + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize("attribute", ["locPrf", "weight", "path"]) +def test_bgp_attributes_for_evpn_address_family_p1(request, attribute): + """ + BGP attributes for EVPN address-family. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise prefixes from VNF routers R1 and R2 in associated " + "VRFs for both address-family." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK2_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK3_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + topo_local = deepcopy(topo) + + logger.info("Modifying topology b/w e1 and d1 from iBGP to eBGP") + step("Delete BGP config for vrf RED.") + + if attribute == "locPrf": + input_dict_vni = { + "d1": { + "vrfs": [ + {"name": "RED", "no_vni": VNI_1}, + {"name": "BLUE", "no_vni": VNI_2}, + {"name": "GREEN", "no_vni": VNI_3}, + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = {} + for dut in ["d1"]: + temp = {dut: {"bgp": []}} + input_dict_2.update(temp) + + INDEX = [0, 1, 2, 3] + VRFS = ["RED", "BLUE", "GREEN", None] + AS_NUM = [100, 100, 100, 100] + + for index, vrf, as_num in zip(INDEX, VRFS, AS_NUM): + topo_local["routers"][dut]["bgp"][index]["local_as"] = 200 + if vrf: + temp[dut]["bgp"].append( + {"local_as": as_num, "vrf": vrf, "delete": True} + ) + else: + temp[dut]["bgp"].append({"local_as": as_num, "delete": True}) + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} on d1 :Failed \n Error: {}".format( + tc_name, result + ) + + result = create_router_bgp(tgen, topo_local["routers"]) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Advertise VRF routes in EVPN address-family from DCG-1 " "and DCG-2 routers.") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Redistribute static in (IPv4 and IPv6) address-family " + "on Edge-1 for all VRFs." + ) + + input_dict_2 = {} + for dut in ["r3", "r4"]: + temp = {dut: {"bgp": []}} + input_dict_2.update(temp) + + if dut == "r3": + VRFS = ["RED"] + AS_NUM = [3] + if dut == "r4": + VRFS = ["BLUE", "GREEN"] + AS_NUM = [4, 4] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on router Edge-1 that EVPN routes corresponding to " + "all VRFs are received from both routers DCG-1 and DCG-2" + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = verify_rib(tgen, addr_type, "e1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure a route-map on Edge-1 to modify below BGP attributes " + "for EVPN address-family:" + ) + + if attribute == "path": + input_dict_1 = { + "e1": { + "route_maps": { + "rmap_d1": [ + { + "action": "permit", + "set": { + attribute: { + "as_num": "123 231 321", + "as_action": "prepend", + } + }, + } + ], + "rmap_d2": [ + { + "action": "permit", + "set": { + attribute: {"as_num": "121", "as_action": "prepend"} + }, + } + ], + } + } + } + else: + input_dict_1 = { + "e1": { + "route_maps": { + "rmap_d1": [{"action": "permit", "set": {attribute: 120}}], + "rmap_d2": [{"action": "permit", "set": {attribute: 150}}], + } + } + } + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_2 = { + "e1": { + "bgp": [ + { + "local_as": "100", + "address_family": { + "l2vpn": { + "evpn": { + "neighbor": { + "d1": { + "ipv4": { + "e1-link1": { + "route_maps": [ + { + "name": "rmap_d1", + "direction": "in", + } + ] + } + } + }, + "d2": { + "ipv4": { + "e1-link1": { + "route_maps": [ + { + "name": "rmap_d2", + "direction": "in", + } + ] + } + } + }, + } + } + } + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on router Edge-1 that EVPN routes are preferred via" + " DCG-1 or DCG-2 based on best path selection criteria " + "(according to the configured BGP attribute values in route-map)." + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, "e1", input_routes, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/example_munet/munet.yaml b/tests/topotests/example_munet/munet.yaml new file mode 100644 index 0000000..34e1470 --- /dev/null +++ b/tests/topotests/example_munet/munet.yaml @@ -0,0 +1,17 @@ +version: 1 +topology: + ipv6-enable: true + networks-autonumber: true + networks: + - name: net1 + - name: net2 + nodes: + - name: r1 + kind: frr + connections: ["net1"] + - name: r2 + kind: frr + connections: ["net1", "net2"] + - name: r3 + kind: frr + connections: ["net2"] diff --git a/tests/topotests/example_munet/r1/daemons b/tests/topotests/example_munet/r1/daemons new file mode 100644 index 0000000..a454c95 --- /dev/null +++ b/tests/topotests/example_munet/r1/daemons @@ -0,0 +1,6 @@ +zebra=1 +staticd=1 +vtysh_enable=1 +watchfrr_enable=1 +zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log" +staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log" diff --git a/tests/topotests/example_munet/r1/frr.conf b/tests/topotests/example_munet/r1/frr.conf new file mode 100644 index 0000000..468bda5 --- /dev/null +++ b/tests/topotests/example_munet/r1/frr.conf @@ -0,0 +1,7 @@ +log file /var/log/frr/frr.log +service integrated-vtysh-config + +interface eth0 + ip address 10.0.1.1/24 + +ip route 10.0.0.0/8 blackhole diff --git a/tests/topotests/example_munet/r1/vtysh.conf b/tests/topotests/example_munet/r1/vtysh.conf new file mode 100644 index 0000000..f863f56 --- /dev/null +++ b/tests/topotests/example_munet/r1/vtysh.conf @@ -0,0 +1 @@ +service integrated-vtysh-config \ No newline at end of file diff --git a/tests/topotests/example_munet/r2/daemons b/tests/topotests/example_munet/r2/daemons new file mode 100644 index 0000000..a454c95 --- /dev/null +++ b/tests/topotests/example_munet/r2/daemons @@ -0,0 +1,6 @@ +zebra=1 +staticd=1 +vtysh_enable=1 +watchfrr_enable=1 +zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log" +staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log" diff --git a/tests/topotests/example_munet/r2/frr.conf b/tests/topotests/example_munet/r2/frr.conf new file mode 100644 index 0000000..77d9892 --- /dev/null +++ b/tests/topotests/example_munet/r2/frr.conf @@ -0,0 +1,10 @@ +log file /var/log/frr/frr.log +service integrated-vtysh-config + +interface eth0 + ip address 10.0.1.2/24 + +interface eth1 + ip address 10.0.2.2/24 + +ip route 10.0.0.0/8 blackhole diff --git a/tests/topotests/example_munet/r2/vtysh.conf b/tests/topotests/example_munet/r2/vtysh.conf new file mode 100644 index 0000000..f863f56 --- /dev/null +++ b/tests/topotests/example_munet/r2/vtysh.conf @@ -0,0 +1 @@ +service integrated-vtysh-config \ No newline at end of file diff --git a/tests/topotests/example_munet/r3/daemons b/tests/topotests/example_munet/r3/daemons new file mode 100644 index 0000000..a454c95 --- /dev/null +++ b/tests/topotests/example_munet/r3/daemons @@ -0,0 +1,6 @@ +zebra=1 +staticd=1 +vtysh_enable=1 +watchfrr_enable=1 +zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log" +staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log" diff --git a/tests/topotests/example_munet/r3/frr.conf b/tests/topotests/example_munet/r3/frr.conf new file mode 100644 index 0000000..e0839e6 --- /dev/null +++ b/tests/topotests/example_munet/r3/frr.conf @@ -0,0 +1,7 @@ +log file /var/log/frr/frr.log +service integrated-vtysh-config + +interface eth0 + ip address 10.0.2.3/24 + +ip route 10.0.0.0/8 blackhole diff --git a/tests/topotests/example_munet/r3/vtysh.conf b/tests/topotests/example_munet/r3/vtysh.conf new file mode 100644 index 0000000..f863f56 --- /dev/null +++ b/tests/topotests/example_munet/r3/vtysh.conf @@ -0,0 +1 @@ +service integrated-vtysh-config \ No newline at end of file diff --git a/tests/topotests/example_munet/test_munet.py b/tests/topotests/example_munet/test_munet.py new file mode 100644 index 0000000..0d9599f --- /dev/null +++ b/tests/topotests/example_munet/test_munet.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 23 2023, Christian Hopps +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +async def test_native_test(unet): + o = unet.hosts["r1"].cmd_nostatus("ip addr") + print(o) diff --git a/tests/topotests/example_test/__init__.py b/tests/topotests/example_test/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/tests/topotests/example_test/r1/zebra.conf b/tests/topotests/example_test/r1/zebra.conf new file mode 100644 index 0000000..b733b7b --- /dev/null +++ b/tests/topotests/example_test/r1/zebra.conf @@ -0,0 +1,8 @@ +interface r1-eth0 + ip address 192.168.1.1/24 + +interface r1-eth1 + ip address 192.168.2.1/24 + +interface r1-eth2 + ip address 192.168.3.1/24 \ No newline at end of file diff --git a/tests/topotests/example_test/r2/zebra.conf b/tests/topotests/example_test/r2/zebra.conf new file mode 100644 index 0000000..c0921f5 --- /dev/null +++ b/tests/topotests/example_test/r2/zebra.conf @@ -0,0 +1,4 @@ +interface r2-eth0 + ip address 192.168.1.2/24 +interface r2-eth1 + ip address 192.168.3.2/24 diff --git a/tests/topotests/example_test/test_example.py b/tests/topotests/example_test/test_example.py new file mode 100755 index 0000000..30c3d24 --- /dev/null +++ b/tests/topotests/example_test/test_example.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python + +import subprocess +import sys +import os +import time + +import pytest + +fatal_error = "" + + +def setup_module(module): + print("setup_module module:%s" % module.__name__) + + +def teardown_module(module): + print("teardown_module module:%s" % module.__name__) + + +def setup_function(function): + print("setup_function function:%s" % function.__name__) + + +def teardown_function(function): + print("teardown_function function:%s" % function.__name__) + + +def test_numbers_compare(): + a = 12 + print("Dummy Output") + assert a == 12 + + +def test_fail_example(): + assert True, "Some Text with explaination in case of failure" + + +@pytest.mark.xfail +def test_ls_exits_zero(): + "Tests for ls command on invalid file" + + global fatal_error + + proc = subprocess.Popen( + ["ls", "/some/nonexistant/file"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + stdout, stderr = proc.communicate() + + if proc.returncode != 0: + # Mark this as a fatal error which skips some other tests on failure + fatal_error = "test_fail_example failed" + assert proc.returncode == 0, "Return Code is non-Zero:\n%s" % stderr + + +def test_skipped_on_fatalerror(): + global fatal_error + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + assert True, "Some Text with explaination in case of failure" + + +if __name__ == "__main__": + retval = pytest.main(["-s"]) + sys.exit(retval) diff --git a/tests/topotests/example_test/test_template.dot b/tests/topotests/example_test/test_template.dot new file mode 100644 index 0000000..b5e1202 --- /dev/null +++ b/tests/topotests/example_test/test_template.dot @@ -0,0 +1,51 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="template"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + s1 [ + shape=oval, + label="s1\n192.168.0.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + shape=oval, + label="s2\n192.168.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- s1 [label="eth0\n.1"]; + + r1 -- s2 [label="eth1\n.100"]; + r2 -- s2 [label="eth0\n.1"]; +} diff --git a/tests/topotests/example_test/test_template.jpg b/tests/topotests/example_test/test_template.jpg new file mode 100644 index 0000000..b01ef73 Binary files /dev/null and b/tests/topotests/example_test/test_template.jpg differ diff --git a/tests/topotests/example_test/test_template.py b/tests/topotests/example_test/test_template.py new file mode 100644 index 0000000..2797548 --- /dev/null +++ b/tests/topotests/example_test/test_template.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +#