From 07dbfff6690ebb4ed5adb8b57b943bcfb71e2b4d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 29 Mar 2024 14:07:45 +0100 Subject: [PATCH] topotests: add bgp_nhg_topo2 test to check for bgp nhg cases When not used by default, bgp nexthop group are not tested against the following route entry added: - route entry resolved over default route - route entry resolved over itself - route entry resolved over blackhole route - route entry connected (case mplsvpn) - route entry using ipv4 mapped ipv6 address This test suite covers the above use cases. Signed-off-by: Philippe Guibert --- tests/topotests/bgp_nhg_topo2/__init__.py | 0 .../topotests/bgp_nhg_topo2/r1/bgp_ipv4.json | 141 +++++++ tests/topotests/bgp_nhg_topo2/r1/bgpd.conf | 42 ++ .../r1/ip_route_192_168_1_0.json | 37 ++ .../r1/ip_route_192_168_2_0.json | 34 ++ .../r1/ip_route_192_168_3_0.json | 44 +++ .../r1/ip_route_192_168_4_0.json | 24 ++ .../bgp_nhg_topo2/r1/ipv6_route_1001_64.json | 33 ++ tests/topotests/bgp_nhg_topo2/r1/zebra.conf | 20 + tests/topotests/bgp_nhg_topo2/r2/bgpd.conf | 10 + tests/topotests/bgp_nhg_topo2/r2/zebra.conf | 8 + tests/topotests/bgp_nhg_topo2/r3/bgpd.conf | 23 ++ tests/topotests/bgp_nhg_topo2/r3/zebra.conf | 11 + tests/topotests/bgp_nhg_topo2/r4/bgpd.conf | 38 ++ tests/topotests/bgp_nhg_topo2/r4/zebra.conf | 12 + .../bgp_nhg_topo2/test_bgp_nhg_topo2.py | 374 ++++++++++++++++++ 16 files changed, 851 insertions(+) create mode 100644 tests/topotests/bgp_nhg_topo2/__init__.py create mode 100644 tests/topotests/bgp_nhg_topo2/r1/bgp_ipv4.json create mode 100644 tests/topotests/bgp_nhg_topo2/r1/bgpd.conf create mode 100644 tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_1_0.json create mode 100644 tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_2_0.json create mode 100644 tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_3_0.json create mode 100644 tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_4_0.json create mode 100644 tests/topotests/bgp_nhg_topo2/r1/ipv6_route_1001_64.json create mode 100644 tests/topotests/bgp_nhg_topo2/r1/zebra.conf create mode 100644 tests/topotests/bgp_nhg_topo2/r2/bgpd.conf create mode 100644 tests/topotests/bgp_nhg_topo2/r2/zebra.conf create mode 100644 tests/topotests/bgp_nhg_topo2/r3/bgpd.conf create mode 100644 tests/topotests/bgp_nhg_topo2/r3/zebra.conf create mode 100644 tests/topotests/bgp_nhg_topo2/r4/bgpd.conf create mode 100644 tests/topotests/bgp_nhg_topo2/r4/zebra.conf create mode 100644 tests/topotests/bgp_nhg_topo2/test_bgp_nhg_topo2.py diff --git a/tests/topotests/bgp_nhg_topo2/__init__.py b/tests/topotests/bgp_nhg_topo2/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_nhg_topo2/r1/bgp_ipv4.json b/tests/topotests/bgp_nhg_topo2/r1/bgp_ipv4.json new file mode 100644 index 000000000000..85f02489b899 --- /dev/null +++ b/tests/topotests/bgp_nhg_topo2/r1/bgp_ipv4.json @@ -0,0 +1,141 @@ +{ + "vrfId": 0, + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 65500, + "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", + "metric": 0, + "weight": 0, + "peerId": "172.31.0.2", + "path": "65501", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.31.0.2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.1.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "172.31.1.0", + "prefixLen": 24, + "network": "172.31.1.0/24", + "metric": 0, + "weight": 0, + "peerId": "172.31.0.2", + "path": "65501", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.31.0.2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "192.168.1.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "192.168.1.0", + "prefixLen": 24, + "network": "192.168.1.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "172.31.0.3", + "path": "", + "origin": "IGP", + "nexthops": [ + { + "ip": "172.31.2.4", + "afi": "ipv4", + "used": true + } + ] + } + ], + "192.168.2.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "internal", + "prefix": "192.168.2.0", + "prefixLen": 24, + "network": "192.168.2.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "172.31.0.3", + "path": "", + "origin": "IGP", + "nexthops": [ + { + "ip":"172.31.3.4", + "afi": "ipv4", + "used": true + } + ] + } + ], + "192.168.3.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "internal", + "prefix": "192.168.3.0", + "prefixLen": 24, + "network": "192.168.3.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "172.31.0.3", + "path": "", + "origin": "IGP", + "nexthops": [ + { + "ip": "192.168.3.3", + "afi": "ipv4", + "used": true + } + ] + } + ], + "192.168.4.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "192.168.4.0", + "prefixLen": 24, + "network": "192.168.4.0/24", + "metric": 0, + "weight": 32768, + "path": "", + "announceNexthopSelf": true, + "nhVrfName": "vrf1", + "nexthops": [ + { + "ip": "0.0.0.0", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_nhg_topo2/r1/bgpd.conf b/tests/topotests/bgp_nhg_topo2/r1/bgpd.conf new file mode 100644 index 000000000000..b01d651764f3 --- /dev/null +++ b/tests/topotests/bgp_nhg_topo2/r1/bgpd.conf @@ -0,0 +1,42 @@ +bgp nexthop-group +route-map rmap_ipv6 permit 1 + set ipv6 next-hop prefer-global +exit +router bgp 65500 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + bgp labeled-unicast ipv4-explicit-null + neighbor 172.31.0.2 remote-as 65501 + neighbor 172.31.0.3 remote-as 65500 + neighbor 172.31.0.4 remote-as 65500 + neighbor 192.0.2.4 remote-as 65500 + neighbor 192.0.2.4 update-source lo + neighbor 192.0.2.4 capability extended-nexthop + neighbor 192.0.2.4 timers connect 1 + address-family ipv4 unicast + import vrf vrf1 + import vpn + no neighbor 192.0.2.4 activate + no neighbor 172.31.0.4 activate + network 192.0.2.1/32 + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 172.31.0.4 activate + exit-address-family + ! + address-family ipv6 unicast + neighbor 192.0.2.4 activate + neighbor 192.0.2.4 route-map rmap_ipv6 in + exit-address-family + ! +exit +router bgp 65500 vrf vrf1 + address-family ipv4 unicast + redistribute connected + export vpn + exit-address-family + ! +exit + + diff --git a/tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_1_0.json b/tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_1_0.json new file mode 100644 index 000000000000..60f52033f511 --- /dev/null +++ b/tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_1_0.json @@ -0,0 +1,37 @@ +{ + "192.168.1.0/24":[ + { + "prefix":"192.168.1.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalNextHopNum":2, + "internalNextHopActiveNum":2, + "nexthops":[ + { + "ip":"172.31.2.4", + "afi":"ipv4", + "active":true, + "recursive":true, + "weight":1 + }, + { + "fib":true, + "ip":"172.31.0.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "resolver":true, + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_2_0.json b/tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_2_0.json new file mode 100644 index 000000000000..0260003e6808 --- /dev/null +++ b/tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_2_0.json @@ -0,0 +1,34 @@ +{ + "192.168.2.0/24":[ + { + "prefix":"192.168.2.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "ip":"172.31.3.4", + "afi":"ipv4", + "active":true, + "recursive":true, + "weight":1 + }, + { + "fib":true, + "unreachable":true, + "blackhole":true, + "resolver":true, + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_3_0.json b/tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_3_0.json new file mode 100644 index 000000000000..102a18da32ce --- /dev/null +++ b/tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_3_0.json @@ -0,0 +1,44 @@ +{ + "192.168.3.0/24":[ + { + "prefix":"192.168.3.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":0, + "vrfName":"default", + "distance":200, + "metric":0, + "table":254, + "nexthops":[ + { + "ip":"192.168.3.3", + "afi":"ipv4", + "weight":1 + } + ] + }, + { + "prefix":"192.168.3.0/24", + "prefixLen":24, + "protocol":"static", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":1, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "fib":true, + "ip":"192.168.5.10", + "afi":"ipv4", + "interfaceName":"r1-eth2", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_4_0.json b/tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_4_0.json new file mode 100644 index 000000000000..b8fcb7e478ca --- /dev/null +++ b/tests/topotests/bgp_nhg_topo2/r1/ip_route_192_168_4_0.json @@ -0,0 +1,24 @@ +{ + "192.168.4.0/24":[ + { + "prefix":"192.168.4.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"vrf1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_nhg_topo2/r1/ipv6_route_1001_64.json b/tests/topotests/bgp_nhg_topo2/r1/ipv6_route_1001_64.json new file mode 100644 index 000000000000..b70916351a4d --- /dev/null +++ b/tests/topotests/bgp_nhg_topo2/r1/ipv6_route_1001_64.json @@ -0,0 +1,33 @@ +{ + "1001::/64":[ + { + "prefix":"1001::/64", + "prefixLen":64, + "protocol":"bgp", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "table":254, + "nexthops":[ + { + "ip":"::ffff:c000:204", + "afi":"ipv6", + "active":true, + "recursive":true, + "weight":1 + }, + { + "ip":"172.31.0.4", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "resolver":true, + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_nhg_topo2/r1/zebra.conf b/tests/topotests/bgp_nhg_topo2/r1/zebra.conf new file mode 100644 index 000000000000..0dda9d1b0ef4 --- /dev/null +++ b/tests/topotests/bgp_nhg_topo2/r1/zebra.conf @@ -0,0 +1,20 @@ +ip route 172.31.3.4/32 blackhole +ip route 192.168.3.0/24 192.168.5.10 +ipv6 route 1010::/64 1003::10 +interface r1-eth0 + ip address 172.31.0.1/24 + ipv6 address ::ffff:172.31.0.1/24 +! +interface r1-eth1 + ip address 192.168.0.1/24 +! +interface r1-eth2 + ip address 192.168.5.1/24 + ipv6 address 1003::1/64 +! +interface r1-eth3 vrf vrf1 + ip address 192.168.4.1/24 +! +interface lo + ip address 192.0.2.1/32 +! diff --git a/tests/topotests/bgp_nhg_topo2/r2/bgpd.conf b/tests/topotests/bgp_nhg_topo2/r2/bgpd.conf new file mode 100644 index 000000000000..6e12f0c3d94d --- /dev/null +++ b/tests/topotests/bgp_nhg_topo2/r2/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65501 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + neighbor 172.31.0.1 remote-as 65500 + address-family ipv4 unicast + neighbor 172.31.0.1 activate + neighbor 172.31.0.1 default-originate + network 172.31.1.0/24 + exit-address-family +! diff --git a/tests/topotests/bgp_nhg_topo2/r2/zebra.conf b/tests/topotests/bgp_nhg_topo2/r2/zebra.conf new file mode 100644 index 000000000000..7a29412b392d --- /dev/null +++ b/tests/topotests/bgp_nhg_topo2/r2/zebra.conf @@ -0,0 +1,8 @@ +log stdout +interface r2-eth0 + ip address 172.31.0.2/24 +! +interface r2-eth1 + ip address 172.31.1.2/24 +! + diff --git a/tests/topotests/bgp_nhg_topo2/r3/bgpd.conf b/tests/topotests/bgp_nhg_topo2/r3/bgpd.conf new file mode 100644 index 000000000000..38d6d509107b --- /dev/null +++ b/tests/topotests/bgp_nhg_topo2/r3/bgpd.conf @@ -0,0 +1,23 @@ +access-list acl1 seq 1 permit 192.168.1.0/24 +access-list acl2 seq 1 permit 192.168.2.0/24 +access-list acl3 seq 1 permit 192.168.3.0/24 +route-map rmap permit 1 + match ip address acl1 + set ip next-hop 172.31.2.4 +exit +route-map rmap permit 2 + match ip address acl2 + set ip next-hop 172.31.3.4 +exit +route-map rmap permit 3 + match ip address acl3 + set ip next-hop 192.168.3.3 +exit +router bgp 65500 + bgp router-id 192.0.2.3 + neighbor 172.31.0.1 remote-as 65500 + address-family ipv4 unicast + network 192.168.1.0/24 route-map rmap + network 192.168.2.0/24 route-map rmap + network 192.168.3.0/24 route-map rmap + exit-address-family diff --git a/tests/topotests/bgp_nhg_topo2/r3/zebra.conf b/tests/topotests/bgp_nhg_topo2/r3/zebra.conf new file mode 100644 index 000000000000..db1e9b6f3ba4 --- /dev/null +++ b/tests/topotests/bgp_nhg_topo2/r3/zebra.conf @@ -0,0 +1,11 @@ +log stdout +interface r3-eth0 + ip address 172.31.0.3/24 +! +interface r3-eth1 + ip address 192.168.1.3/24 + ip address 192.168.2.3/24 + ip address 192.168.3.3/24 + ip address 192.168.4.3/24 + ipv6 address 1001::3/64 +! diff --git a/tests/topotests/bgp_nhg_topo2/r4/bgpd.conf b/tests/topotests/bgp_nhg_topo2/r4/bgpd.conf new file mode 100644 index 000000000000..f4508be8a4e4 --- /dev/null +++ b/tests/topotests/bgp_nhg_topo2/r4/bgpd.conf @@ -0,0 +1,38 @@ +route-map rmap_ipv6 permit 1 + set ipv6 next-hop prefer-global +exit +ipv6 access-list acl1 seq 1 permit 1002::/64 +ipv6 access-list acl2 seq 1 permit 1001::/64 +route-map rmap_out permit 1 + match ipv6 address acl1 + set ipv6 next-hop global 1010::10 +exit +route-map rmap_out permit 2 + match ipv6 address acl2 +exit +router bgp 65500 + bgp router-id 192.0.2.4 + bgp labeled-unicast ipv4-explicit-null + neighbor 172.31.0.1 remote-as 65500 + neighbor 192.0.2.1 remote-as 65500 + neighbor 192.0.2.1 capability extended-nexthop + neighbor 192.0.2.1 update-source lo + neighbor 192.0.2.1 timers connect 1 + address-family ipv4 unicast + network 192.0.2.4/32 + no neighbor 192.0.2.1 activate + no neighbor 172.31.0.1 activate + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 172.31.0.1 activate + exit-address-family + ! + address-family ipv6 unicast + neighbor 192.0.2.1 activate + neighbor 192.0.2.1 route-map rmap_ipv6 in + neighbor 192.0.2.1 route-map rmap_out out + network 1001::/64 + network 1002::/64 + exit-address-family + ! diff --git a/tests/topotests/bgp_nhg_topo2/r4/zebra.conf b/tests/topotests/bgp_nhg_topo2/r4/zebra.conf new file mode 100644 index 000000000000..b37689a40fed --- /dev/null +++ b/tests/topotests/bgp_nhg_topo2/r4/zebra.conf @@ -0,0 +1,12 @@ +log stdout +interface r4-eth0 + ip address 172.31.0.4/24 + ipv6 address ::ffff:172.31.0.4/120 +! +interface r4-eth1 + ipv6 address 1001::4/64 + ipv6 address 1002::4/64 +! +interface lo + ip address 192.0.2.4/32 +! diff --git a/tests/topotests/bgp_nhg_topo2/test_bgp_nhg_topo2.py b/tests/topotests/bgp_nhg_topo2/test_bgp_nhg_topo2.py new file mode 100644 index 000000000000..23cb1c6d557e --- /dev/null +++ b/tests/topotests/bgp_nhg_topo2/test_bgp_nhg_topo2.py @@ -0,0 +1,374 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_nhg_topo2.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2024 by 6WIND +# + +""" + test_bgp_nhg_topo2.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.bgpcheck import ( + check_show_bgp_vpn_prefix_found, + check_show_bgp_vpn_prefix_not_found, +) +from lib.common_config import step +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") + tgen.add_router("r4") + + 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"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["r4"]) + + switch = tgen.add_switch("s8") + switch.add_link(tgen.gears["r1"]) + + +def _populate_iface(): + tgen = get_topogen() + cmds_list_r1 = [ + "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}-eth3 master vrf1", + "echo 1 > /proc/sys/net/mpls/conf/{0}-eth3/input", + "echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input", + ] + + for cmd in cmds_list_r1: + input = cmd.format("r1") + logger.info("input: " + cmd.format("r1")) + output = tgen.net["r1"].cmd(cmd.format("r1")) + logger.info("output: " + output) + + cmds_list_r4 = [ + "echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input", + ] + for cmd in cmds_list_r4: + input = cmd.format("r4") + logger.info("input: " + cmd.format("r4")) + output = tgen.net["r4"].cmd(cmd.format("r4")) + logger.info("output: " + output) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + _populate_iface() + 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)), + "--v6-with-v4-nexthops", + ) + 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_ipv4(): + """ + Assert that BGP R1 has converged with IPv4 prefixes + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Check that BGP R1 has converged with IPv4") + router = tgen.gears["r1"] + expected = json.loads(open("{}/r1/bgp_ipv4.json".format(CWD)).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp ipv4 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertmsg = ( + '"{}" JSON output mismatches, BGP R1 has not converged with IPv4'.format( + router.name + ) + ) + assert result is None, assertmsg + + +def check_bgp_nexthop_group_disabled_for_prefix(prefix, router_name): + """ + Assert that the prefix does not use BGP NHG + because it is resolved over blackhole + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + donna = tgen.gears["r1"].vtysh_cmd( + "show bgp nexthop-group detail json", isjson=True + ) + nhg_found = False + nhg_id = 0 + nb_paths = 0 + for nhg_ctx in donna: + if "paths" not in nhg_ctx.keys(): + continue + for path_ctx in nhg_ctx["paths"]: + if "prefix" not in path_ctx.keys(): + continue + nb_paths = nb_paths + 1 + if path_ctx["prefix"] == "192.168.2.0/24": + nhg_found = True + nhg_id = nhg_ctx["nhgId"] + + assertmsg = '"{}" no NHG paths found'.format(router_name) + assert nb_paths != 0, assertmsg + + assertmsg = '"{}" 192.168.2.0 prefix found used with NHG {}'.format( + router_name, nhg_id + ) + assert nhg_found == False, assertmsg + + +def test_bgp_nexthop_group_disabled_for_blackhole(): + """ + Assert that the 192.168.2.0/24 prefix does not use BGP NHG + because it is resolved over blackhole + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Assert that the 192.168.2.0/24 is resolved over blackhole route") + router = tgen.gears["r1"] + expected = json.loads(open("{}/r1/ip_route_192_168_2_0.json".format(CWD)).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip route 192.168.2.0/24 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = '"{}" JSON output mismatches, 192.168.2.0/24 is not resolved over blackhole route'.format( + router.name + ) + assert result is None, assertmsg + + step("Assert that the 192.168.2.0/24 prefix does not use BGP NHG") + check_bgp_nexthop_group_disabled_for_prefix("192.168.2.0/24", "r1") + + +def test_bgp_nexthop_group_disabled_for_routes_resolving_over_default_route(): + """ + Assert that the 192.168.1.0/24 prefix does not use BGP NHG + because it is resolved over default route + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Assert that the 192.168.1.0/24 is resolved over 172.31.0.2 (default route)") + router = tgen.gears["r1"] + expected = json.loads(open("{}/r1/ip_route_192_168_1_0.json".format(CWD)).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip route 192.168.1.0/24 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = '"{}" JSON output mismatches, 192.168.1.0/24 is not resolved over 172.31.0.2 (default route)'.format( + router.name + ) + assert result is None, assertmsg + + step("Assert that the 192.168.1.0/24 prefix does not use BGP NHG") + check_bgp_nexthop_group_disabled_for_prefix("192.168.1.0/24", "r1") + + +def test_bgp_nexthop_group_disabled_for_routes_resolving_over_same_prefix(): + """ + Assert that the 192.168.3.0/24 prefix does not use BGP NHG + because it is resolved over an already present exact same prefix + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Assert that the 192.168.3.0/24 has 2 entries in ZEBRA") + router = tgen.gears["r1"] + expected = json.loads(open("{}/r1/ip_route_192_168_3_0.json".format(CWD)).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip route 192.168.3.0/24 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = ( + '"{}" JSON output mismatches, 192.168.3.0/24 has not 2 entries in ZEBRA'.format( + router.name + ) + ) + assert result is None, assertmsg + + step("Assert that the 192.168.3.0/24 has a nexthop") + donna = router.vtysh_cmd("show bgp ipv4 192.168.3.0/24 json", isjson=True) + # look for first available nexthop + nexthop_to_check = "" + for path in donna["paths"]: + if "nexthops" not in path.keys(): + continue + for nh in path["nexthops"]: + if "ip" in nh.keys(): + nexthop_to_check = nh["ip"] + break + + assert ( + nexthop_to_check != "" + ), '"{}", 192.168.3.0/24 prefix has no valid nexthop'.format(router.name) + + step( + f"Check that nexthop {nexthop_to_check} is resolved over 192.168.3.0/24 prefix" + ) + donna = router.vtysh_cmd(f"show bgp nexthop {nexthop_to_check} json", isjson=True) + if "ipv4" not in donna.keys() or "resolvedPrefix" not in donna["ipv4"].keys(): + assert 0, '"{}", {} nexthop is invalid'.format(router.name, nexthop_to_check) + + assertmsg = '"{}", 192.168.3.0/24 prefix is not resolving over itself'.format( + router.name + ) + assert donna["ipv4"]["resolvedPrefix"] == "192.168.3.0/24", assertmsg + + step("Assert that the 192.168.3.0/24 prefix does not use BGP NHG") + check_bgp_nexthop_group_disabled_for_prefix("192.168.3.0/24", "r1") + + +def test_bgp_nexthop_group_disabled_for_imported_routes(): + """ + Assert that the 192.168.4.0/24 prefix does not use BGP NHG + because it is directly imported, connected over an interface + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Assert that the 192.168.4.0/24 is installed") + router = tgen.gears["r1"] + expected = json.loads(open("{}/r1/ip_route_192_168_4_0.json".format(CWD)).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip route 192.168.4.0/24 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = '"{}" JSON output mismatches, 192.168.4.0/24 is not installed'.format( + router.name + ) + assert result is None, assertmsg + + step("Assert that the 192.168.4.0/24 prefix does not use BGP NHG") + check_bgp_nexthop_group_disabled_for_prefix("192.168.4.0/24", "r1") + + +def test_bgp_nexthop_group_disabled_for_6pe_routes(): + """ + Assert that the 1001::/64 prefix does not use BGP NHG + because its nexthop is an IPv4 mapped IPv6 address + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Assert that the 1001::/64 is present on ZEBRA") + router = tgen.gears["r1"] + expected = json.loads(open("{}/r1/ipv6_route_1001_64.json".format(CWD)).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ipv6 route 1001::/64 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = '"{}" JSON output mismatches, 192.168.4.0/24 is not installed'.format( + router.name + ) + assert result is None, assertmsg + + step("Assert that the 1001::/64 prefix does not use BGP NHG") + check_bgp_nexthop_group_disabled_for_prefix("1001::/64", "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))