From 80ed4b8d79f27f5c1711cce0e7ec40a40a38a17b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Parisot?= <sparisot@free-mobile.fr>
Date: Tue, 10 Feb 2026 07:53:11 +0000
Subject: [PATCH] bgp: suppress link-local next hop based on RFC 2545
 shared-subnet rule

Only include the link-local IPv6 address in the next hop field if the
global next hop address is on the subnet shared with the peer, as
required by RFC 2545 Section 3 and RFC 4659 Section 3.2.1.1.

Previously the link-local was included unconditionally whenever it was
available (single-hop sessions). This caused some routers (e.g. Cisco
IOS XR) to close the BGP session when receiving a VPN route with a
link-local next hop, since in typical PE-to-PE iBGP the next hop is a
loopback address not on the shared subnet.
---
 proto/bgp/packets.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index fcaff74..b5d241d 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -1277,7 +1277,11 @@ bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
     else
     {
       ip_addr nh[2] = { s->channel->next_hop_addr, s->channel->link_addr };
-      bgp_set_attr_data(to, s->pool, BA_NEXT_HOP, 0, nh, ipa_nonzero(nh[1]) ? 32 : 16);
+
+      /* Include link-local next hop iff the global next hop address is on
+       * the subnet shared with the peer (RFC 2545 Section 3, RFC 4659 Section 3.2.1.1) */
+      int shared_subnet = s->proto->neigh && s->proto->neigh->ifa && ipa_in_netX(nh[0], &s->proto->neigh->ifa->prefix);
+      bgp_set_attr_data(to, s->pool, BA_NEXT_HOP, 0, nh, (ipa_nonzero(nh[1]) && shared_subnet) ? 32 : 16);
       s->local_next_hop = 1;
 
       if (s->mpls)
-- 
2.47.3

