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",