Commit e3a9adf641 for frr

commit e3a9adf6413ca9c0cf128f3ee43edf90648d0629
Author: Sougata Barik <sougatab@nvidia.com>
Date:   Fri Jul 17 15:59:10 2026 +0530

    lib: fix BFD sessions stuck Down after BGP churn on shared link-local subifs

    Problem
    =======
    On leaf11 with 25 single-hop unnumbered eBGP+BFD neighbours (one per VLAN
    sub-interface towards the same peer box), after a few iterations of
    unconfigure/reconfigure of BGP+BFD, BGP reaches Established on all 25 peers but
    most of the BFD sessions stay Down (only 2-3 come Up). `Support/bfd.peers` shows
    bfdd genuinely has no session for the broken peers.

    RCA
    ===
    All 25 unnumbered peers derive the same EUI-64 link-local address, so every BFD
    session shares the identical (src, dst) link-local pair; the outgoing interface
    (ifindex) is the ONLY key that distinguishes them in bfdd.

    During rapid re-add, bgp_peer_bfd_update_source() fires the IPv6-address setter
    -> _bfd_sess_remove()/REGISTER while p->nexthop.ifp is still NULL, so
    bfd_sess_set_interface() is skipped. Those interface-less REGISTER/DEREGISTER/
    UPDATE operations all collapse onto a single bfdd key (src, dst, ifindex=0); the
    25 sessions stomp each other and most end up Down.

    Fix
    ===
    lib/bfd.c _bfd_sess_valid(): treat a single-hop IPv6 link-local session with no
    outgoing interface as not-yet-valid, so it is neither registered nor deregistered
    on the wire. Because _bfd_sess_remove() only sends a DEREGISTER when the session
    is installed, this also suppresses the bogus interface-less teardown. When the
    nexthop interface resolves, BGP re-drives bgp_peer_bfd_update_source() and the
    session installs correctly with a unique per-interface key.

    Testing
    =======
    ssim repro (mlx-5610-32 hard DUT + VX peer), 5x delete/re-add churn over 25
    unnumbered eBGP+BFD sub-interfaces:
      Before: BFD stuck 2-3/25 after re-add -> FAIL
      After:  BFD recovers 25/25 within ~15s after every re-add, all 5 iterations
              -> PASS; trace shows 0 interface-less BFD sends.
    See UT log: 5131052_ut_bgp_bfd_subif_churn.log

    Ticket: #5131052
    Signed-off-by: Sougata Barik <sougatab@nvidia.com>

diff --git a/lib/bfd.c b/lib/bfd.c
index af6be58eff..f67e69c7ad 100644
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -460,6 +460,30 @@ static bool _bfd_sess_valid(const struct bfd_session_params *bsp)
 		return false;
 	}

+	/*
+	 * Single-hop IPv6 link-local sessions require an outgoing interface.
+	 *
+	 * A link-local (fe80::/10) peer/local pair is not globally unique: on a
+	 * box with many single-hop unnumbered neighbours (e.g. one BGP peer per
+	 * VLAN sub-interface towards the same remote box) every session shares
+	 * the identical link-local src/dst, so the interface (ifindex) is the
+	 * only key that distinguishes them in bfdd.  Sending a REGISTER (or
+	 * DEREGISTER) before the egress interface is known collapses all of
+	 * those sessions onto a single ifindex-less key, and siblings then tear
+	 * each other down - leaving most BFD sessions stuck Down even though BGP
+	 * is Established.  Treat such a session as not-yet-valid and defer the
+	 * install; the client re-drives us (bgp_peer_bfd_update_source) once the
+	 * nexthop interface is resolved.
+	 */
+	if (!bsp->args.mhop && bsp->args.family == AF_INET6 &&
+	    IN6_IS_ADDR_LINKLOCAL(&bsp->args.dst) &&
+	    bsp->args.ifnamelen == 0) {
+		if (bsglobal.debugging)
+			zlog_debug("%s: single-hop link-local session without interface; deferring install",
+				   __func__);
+		return false;
+	}
+
 	/* Check VRF ID. */
 	if (bsp->args.vrf_id == VRF_UNKNOWN) {
 		if (bsglobal.debugging)