Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions bgpd/bgp_attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -473,21 +473,16 @@ extern enum bgp_attr_parse_ret bgp_attr_nexthop_valid(struct peer *peer,

extern uint32_t bgp_attr_get_color(struct attr *attr);

static inline bool bgp_rmap_nhop_changed(uint32_t out_rmap_flags,
uint32_t in_rmap_flags)
static inline bool bgp_rmap_nhop_changed(uint32_t out_rmap_flags)
{
return ((CHECK_FLAG(out_rmap_flags, BATTR_RMAP_NEXTHOP_PEER_ADDRESS) ||
CHECK_FLAG(out_rmap_flags, BATTR_RMAP_NEXTHOP_UNCHANGED) ||
CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV4_NHOP_CHANGED) ||
CHECK_FLAG(out_rmap_flags, BATTR_RMAP_VPNV4_NHOP_CHANGED) ||
CHECK_FLAG(out_rmap_flags,
BATTR_RMAP_VPNV6_GLOBAL_NHOP_CHANGED) ||
CHECK_FLAG(out_rmap_flags,
BATTR_RMAP_IPV6_GLOBAL_NHOP_CHANGED) ||
CHECK_FLAG(out_rmap_flags,
BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED) ||
CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV6_LL_NHOP_CHANGED) ||
CHECK_FLAG(in_rmap_flags, BATTR_RMAP_NEXTHOP_UNCHANGED))
CHECK_FLAG(out_rmap_flags, BATTR_RMAP_VPNV6_GLOBAL_NHOP_CHANGED) ||
CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV6_GLOBAL_NHOP_CHANGED) ||
CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED) ||
CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV6_LL_NHOP_CHANGED))
? true
: false);
}
Expand Down
7 changes: 2 additions & 5 deletions bgpd/bgp_route.c
Original file line number Diff line number Diff line change
Expand Up @@ -2706,11 +2706,8 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
* example,
* it is configured as 'peer-address'.
*/
if (!bgp_rmap_nhop_changed(attr->rmap_change_flags,
piattr->rmap_change_flags)
&& !transparent
&& !CHECK_FLAG(peer->af_flags[afi][safi],
PEER_FLAG_NEXTHOP_UNCHANGED)) {
if (!bgp_rmap_nhop_changed(attr->rmap_change_flags) && !transparent &&
!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)) {
/* We can reset the nexthop, if setting (or forcing) it to
* 'self' */
if (CHECK_FLAG(peer->af_flags[afi][safi],
Expand Down
Empty file.
14 changes: 14 additions & 0 deletions tests/topotests/bgp_nexthop_unchanged_outbound/r1/frr.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
!
interface lo
ip address 172.16.1.1/32
!
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
address-family ipv4 unicast
redistribute connected
exit-address-family
!
19 changes: 19 additions & 0 deletions tests/topotests/bgp_nexthop_unchanged_outbound/r2/frr.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
!
interface r2-eth0
ip address 192.168.1.2/24
!
interface r2-eth1
ip address 10.0.0.2/24
!
route-map r1 permit 10
set ip next-hop unchanged
!
router bgp 65002
no bgp ebgp-requires-policy
neighbor 192.168.1.1 remote-as external
neighbor 10.0.0.3 remote-as internal
address-family ipv4 unicast
neighbor 192.168.1.1 route-map r1 in
neighbor 10.0.0.3 next-hop-self
exit-address-family
!
8 changes: 8 additions & 0 deletions tests/topotests/bgp_nexthop_unchanged_outbound/r3/frr.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
!
interface r3-eth0
ip address 10.0.0.3/24
!
router bgp 65002
no bgp ebgp-requires-policy
neighbor 10.0.0.2 remote-as internal
!
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env python
# SPDX-License-Identifier: ISC

# Copyright (c) 2026 by
# Donatas Abraitis <donatas@opensourcerouting.org>
#

"""
Test that an inbound route-map with "set ip next-hop unchanged" does NOT
prevent next-hop-self from taking effect on an iBGP peer.
"""

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 import topotest
from lib.topogen import Topogen, get_topogen


def build_topo(tgen):
for routern in range(1, 4):
tgen.add_router("r{}".format(routern))

# R1 -- R2 link
switch = tgen.add_switch("s1")
switch.add_link(tgen.gears["r1"])
switch.add_link(tgen.gears["r2"])

# R2 -- R3 link
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_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_nexthop_unchanged_outbound():
tgen = get_topogen()

if tgen.routers_have_failure():
pytest.skip(tgen.errors)

r3 = tgen.gears["r3"]

def _bgp_converge():
output = json.loads(r3.vtysh_cmd("show ip bgp summary json"))
expected = {
"ipv4Unicast": {
"peers": {
"10.0.0.2": {"state": "Established"},
}
}
}
return topotest.json_cmp(output, expected)

_, result = topotest.run_and_expect(_bgp_converge, None, count=60, wait=0.5)
assert result is None, "BGP did not converge on r3"

def _bgp_nexthop_is_r2():
output = json.loads(r3.vtysh_cmd("show ip bgp 172.16.1.1/32 json"))
expected = {"paths": [{"nexthops": [{"ip": "10.0.0.2"}]}]}
return topotest.json_cmp(output, expected)

_, result = topotest.run_and_expect(_bgp_nexthop_is_r2, None, count=60, wait=0.5)
assert result is None, (
"Nexthop on r3 for 172.16.1.1/32 is not R2's address (10.0.0.2). "
"Inbound 'set ip next-hop unchanged' on R2 must not prevent "
"next-hop-self from taking effect on the iBGP session to R3."
)


if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
Loading