Commit a3734e45da for frr
commit a3734e45daf2ce99216cf56e4696c5324ac951d3
Author: Sougata Barik <sougatab@nvidia.com>
Date: Wed Nov 4 13:12:17 2026 +0530
bgpd: key a single-hop BFD session on the longest connected match
Problem:
With overlapping connected subnets, a single-hop BGP BFD session can be
keyed on a different interface than the one BGP uses. With 10.0.0.1/16
on eth0 and 10.0.1.1/24 on eth1, a peer at 10.0.1.2 is reached over
eth1, but the session is installed on eth0. bfdd binds the session to
eth0, nobody answers there, and BFD stays Down; with bfd strict BGP
never comes up.
RCA:
bgp_peer_bfd_egress_ifp() resolves the egress interface from the
connected routes with if_lookup_by_ipv4()/if_lookup_by_ipv6(), which
return the first interface whose connected prefix holds the address, not
the most specific one. The result is preferred over p->nexthop.ifp, so
it is not corrected when TCP connects.
Fix:
Use if_lookup_address(), which returns the longest match. It still
only depends on the connected routes, so the interface is the same
before and after TCP connects.
Ticket: #5391569
Signed-off-by: Sougata Barik <sougatab@nvidia.com>
diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c
index a4601ceb68..2d13ba58c4 100644
--- a/bgpd/bgp_bfd.c
+++ b/bgpd/bgp_bfd.c
@@ -207,10 +207,17 @@ static struct interface *bgp_peer_bfd_egress_ifp(struct peer *p)
ifp = if_lookup_by_name(p->update_if, vrf_id);
else if (ifname)
ifp = if_lookup_by_name(ifname, vrf_id);
- else if (su->sa.sa_family == AF_INET)
- ifp = if_lookup_by_ipv4(&su->sin.sin_addr, vrf_id);
- else
- ifp = if_lookup_by_ipv6(&su->sin6.sin6_addr, 0, vrf_id);
+ else {
+ /* Longest match: with overlapping subnets the first one can be the wrong link. */
+ struct connected *c;
+
+ if (su->sa.sa_family == AF_INET)
+ c = if_lookup_address(&su->sin.sin_addr, AF_INET, vrf_id);
+ else
+ c = if_lookup_address(&su->sin6.sin6_addr, AF_INET6, vrf_id);
+ if (c)
+ ifp = c->ifp;
+ }
return ifp ? ifp : p->nexthop.ifp;
}