Commit 20f0dd1307 for frr

commit 20f0dd13078929c92a5095a8c3dbb2da0afd836c
Author: Philippe Guibert <philippe.guibert@6wind.com>
Date:   Fri Oct 2 14:45:03 2026 +0200

    bgpd: same tie-break for paths imported from local and remote routes

    When a VRF route is exported to the VPN table with both an MPLS label
    and an SRv6 SID, two paths are imported for the same parent. Those paths
    are equal on every best path criteria, and the outcome depends on the
    origin of the parent route:

    - parent learnt from a remote peer: "nothing left to compare" makes the
      oldest path win, and the older path rule keeps it selected. The path
      exported first (MPLS) is selected.
    - parent locally originated (connected, static, network): the parent
      peer has no su_remote, and the "Locally configured route" check always
      makes the compared path lose. The head of the path list, which is the
      path exported last (SRv6), is selected.

    > show bgp ipv4 vpn prefix 11.11.10.155/32   (learnt from eBGP CE)
    >       ..., best (Older Path)                  -> Remote label: 19
    > show bgp ipv4 vpn prefix 11.11.11.1/32     (connected in vrfA)
    >       ..., best (Locally configured route)    -> Remote SID: uDT4

    The su_remote check was added to protect sockunion_cmp() against paths
    without su_remote; it was not meant to order two locally originated
    paths. When both paths are imported and both parent peers have no
    su_remote, skip that check and use the default tie-break, as for two
    paths imported from the same remote peer.

    Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>

diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 09af54aed3..2e67b3baac 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -1876,6 +1876,14 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
 		return 0;
 	}

+	/* Two paths imported from a locally originated route (redistribute,
+	 * network, aggregate) have no su_remote and can not be told apart by
+	 * neighbor address: use the default tie-break, as for two paths
+	 * imported from the same remote peer.
+	 */
+	if (peer_new->connection->su_remote == NULL && peer_exist->connection->su_remote == NULL)
+		goto bgp_path_info_cmp_done;
+
 	/* locally configured routes to advertise do not have su_remote */
 	if (peer_new->connection->su_remote == NULL) {
 		*reason = bgp_path_selection_local_configured;
@@ -1906,6 +1914,7 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
 		return 1;
 	}

+bgp_path_info_cmp_done:
 	*reason = bgp_path_selection_default;
 	if (debug)
 		zlog_debug("%s: %s wins over %s due to nothing left to compare",