diff --git a/lib/zclient.c b/lib/zclient.c index 1aab7b48ba..ca4880da43 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -2134,6 +2134,7 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh) n->ifindex = znh->ifindex; n->gate = znh->gate; n->srte_color = znh->srte_color; + n->weight = znh->weight; /* * This function currently handles labels diff --git a/tests/topotests/bgp_weighted_ecmp_recursive/__init__.py b/tests/topotests/bgp_weighted_ecmp_recursive/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_weighted_ecmp_recursive/r1/frr.conf b/tests/topotests/bgp_weighted_ecmp_recursive/r1/frr.conf new file mode 100644 index 0000000000..f7c591f75f --- /dev/null +++ b/tests/topotests/bgp_weighted_ecmp_recursive/r1/frr.conf @@ -0,0 +1,16 @@ +! +int r1-eth0 + ip address 192.168.12.1/24 +! +int r1-eth1 + ip address 192.168.13.1/24 +! +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.12.2 remote-as internal + neighbor 192.168.12.2 timers 1 3 + neighbor 192.168.12.2 timers connect 1 + neighbor 192.168.13.3 remote-as internal + neighbor 192.168.13.3 timers 1 3 + neighbor 192.168.13.3 timers connect 1 +! diff --git a/tests/topotests/bgp_weighted_ecmp_recursive/r2/frr.conf b/tests/topotests/bgp_weighted_ecmp_recursive/r2/frr.conf new file mode 100644 index 0000000000..dc25d4781c --- /dev/null +++ b/tests/topotests/bgp_weighted_ecmp_recursive/r2/frr.conf @@ -0,0 +1,20 @@ +! +int r2-eth0 + ip address 192.168.12.2/24 +! +int r2-eth1 + ip address 192.168.24.2/24 +! +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.12.1 remote-as internal + neighbor 192.168.12.1 timers 1 3 + neighbor 192.168.12.1 timers connect 1 + neighbor 192.168.24.4 remote-as internal + neighbor 192.168.24.4 timers 1 3 + neighbor 192.168.24.4 timers connect 1 + address-family ipv4 unicast + redistribute connected + neighbor 192.168.12.1 route-reflector-client + exit-address-family +! diff --git a/tests/topotests/bgp_weighted_ecmp_recursive/r3/frr.conf b/tests/topotests/bgp_weighted_ecmp_recursive/r3/frr.conf new file mode 100644 index 0000000000..bdeb35b50f --- /dev/null +++ b/tests/topotests/bgp_weighted_ecmp_recursive/r3/frr.conf @@ -0,0 +1,20 @@ +! +int r3-eth0 + ip address 192.168.13.3/24 +! +int r3-eth1 + ip address 192.168.35.3/24 +! +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.13.1 remote-as internal + neighbor 192.168.13.1 timers 1 3 + neighbor 192.168.13.1 timers connect 1 + neighbor 192.168.35.5 remote-as internal + neighbor 192.168.35.5 timers 1 3 + neighbor 192.168.35.5 timers connect 1 + address-family ipv4 unicast + redistribute connected + neighbor 192.168.13.1 route-reflector-client + exit-address-family +! diff --git a/tests/topotests/bgp_weighted_ecmp_recursive/r4/frr.conf b/tests/topotests/bgp_weighted_ecmp_recursive/r4/frr.conf new file mode 100644 index 0000000000..0662a4dc49 --- /dev/null +++ b/tests/topotests/bgp_weighted_ecmp_recursive/r4/frr.conf @@ -0,0 +1,17 @@ +! +int r4-eth0 + ip address 192.168.24.4/24 +! +router bgp 65000 + 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 + address-family ipv4 unicast + network 10.10.10.10/32 route-map rmap + exit-address-family +! +route-map rmap permit 10 + set extcommunity bandwidth 4000 +exit diff --git a/tests/topotests/bgp_weighted_ecmp_recursive/r5/frr.conf b/tests/topotests/bgp_weighted_ecmp_recursive/r5/frr.conf new file mode 100644 index 0000000000..bc24a96b51 --- /dev/null +++ b/tests/topotests/bgp_weighted_ecmp_recursive/r5/frr.conf @@ -0,0 +1,17 @@ +! +int r5-eth0 + ip address 192.168.35.5/24 +! +router bgp 65000 + 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 + address-family ipv4 unicast + network 10.10.10.10/32 route-map rmap + exit-address-family +! +route-map rmap permit 10 + set extcommunity bandwidth 5000 +exit diff --git a/tests/topotests/bgp_weighted_ecmp_recursive/test_bgp_weighted_ecmp_recursive.py b/tests/topotests/bgp_weighted_ecmp_recursive/test_bgp_weighted_ecmp_recursive.py new file mode 100644 index 0000000000..7a6d068e56 --- /dev/null +++ b/tests/topotests/bgp_weighted_ecmp_recursive/test_bgp_weighted_ecmp_recursive.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2024 by +# Donatas Abraitis +# + +""" +Test if weighted ECMP works and recursive weight (link bandwidth) is +inherited to non-recursive next-hops. +""" + +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 + + +def setup_module(mod): + topodef = { + "s1": ("r1", "r2"), + "s2": ("r1", "r3"), + "s3": ("r2", "r4"), + "s4": ("r3", "r5"), + } + 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_weighted_ecmp_recursive(): + 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 ip route 10.10.10.10/32 json")) + expected = { + "10.10.10.10/32": [ + { + "selected": True, + "installed": True, + "nexthops": [ + { + "ip": "192.168.24.4", + "active": True, + "recursive": True, + "weight": 204, + }, + { + "ip": "192.168.12.2", + "active": True, + "resolver": True, + "weight": 204, + }, + { + "ip": "192.168.35.5", + "active": True, + "recursive": True, + "weight": 255, + }, + { + "ip": "192.168.13.3", + "active": True, + "resolver": True, + "weight": 255, + }, + ], + } + ] + } + 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" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 1246e4dba2..55920102bb 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1747,6 +1747,12 @@ static struct nexthop *nexthop_set_resolved(afi_t afi, SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); resolved_hop->vrf_id = nexthop->vrf_id; + + /* Using weighted ECMP, we should respect the weight and use + * the same value for non-recursive next-hop. + */ + resolved_hop->weight = nexthop->weight; + switch (newhop->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: