Commit 197c580ba4 for frr
commit 197c580ba4b4163a6a77e43081e269e7e688c26d
Author: Philippe Guibert <philippe.guibert@6wind.com>
Date: Thu Oct 22 15:48:10 2026 +0200
bgpd: fix VPN SIDs after a zebra restart
When zebra restarts while bgpd keeps running, zebra loses the SIDs
allocated for the VRFs ('sid vpn export' per address-family or per VRF),
and their local SIDs. BGP keeps exporting the VPN paths with these SIDs,
while zebra is stopped and after it is started again, although these
SIDs are no longer allocated in zebra, and may be given to another
client. Before zebra is stopped on r1:
> r1# show segment-routing srv6 sid
> SID Behavior Context Daemon/Instance Locator AllocationType
> ---------------------------- ------------- ----------------- --------- ----------------
> 2002:db8:1:1:1:: End.DT4 VRF 'vrf10' bgp(0) loc2 dynamic
> 2002:db8:1:1:2:: End.DT6 VRF 'vrf10' bgp(0) loc2 dynamic
> 2002:db8:1:1:3:: End.DT6 VRF 'vrf30' bgp(0) loc2 dynamic
One minute after zebra is stopped on r1, its peer r2 still uses the SID
2002:db8:1:1:1:: of r1 for the VPN path 192.168.1.0/24 of RD 11:10.
After a restart of zebra:
> r1# show segment-routing srv6 sid
> SID Behavior Context Daemon/Instance Locator AllocationType
Nothing withdraws the VPN paths when the connection to zebra is lost.
And when the locator is received again, bgp does not request a SID, due
to ensure_vrf_tovpn_sid_per_af() and ensure_vrf_tovpn_sid_per_vrf(): the
last state of the SID is not reset.
Fix this by withdrawing the VPN paths exported with a SID, and by
forgetting the SIDs of the VRFs without releasing them, when the
connection to zebra is lost. A VPN path without its SID is not exported
again: vpn_leak_to_vpn_active() refuses the export until the SID is
allocated. The SIDs are requested again when the locator is received,
the VPN paths are exported again once the SIDs are allocated, and their
local SIDs are installed.
With this fix, two seconds after zebra is stopped on r1, r2 no longer
has the VPN paths of r1 (RD 1:10 and 1:30):
> r2# show bgp ipv6 vpn
> [..]
> Network Next Hop Metric LocPrf Weight Path
> Route Distinguisher: 2:10
> 2001:2::/64 ::@6< 0 32768 ?
> Route Distinguisher: 2:20
> *> 2001:4::/64 ::@7< 0 32768 ?
> ::@7< 0 32768 ?
> *> 2001:6::/64 ::@7< 0 32768 ?
> ::@7< 0 32768 ?
>
> Displayed 3 routes and 5 total paths
Once zebra is started again on r1, the SIDs are allocated again, and r2
gets the VPN paths of r1 again:
> r1# show segment-routing srv6 sid
> SID Behavior Context Daemon/Instance Locator AllocationType
> ---------------------------- ------------- ----------------- --------- ----------------
> 2002:db8:1:1:1:: End.DT4 VRF 'vrf10' bgp(0) loc2 dynamic
> 2002:db8:1:1:2:: End.DT6 VRF 'vrf10' bgp(0) loc2 dynamic
> 2002:db8:1:1:3:: End.DT6 VRF 'vrf30' bgp(0) loc2 dynamic
> r2# show bgp ipv6 vpn
> [..]
> Network Next Hop Metric LocPrf Weight Path
> Route Distinguisher: 1:10
> *> 2001:1::/64 2001::1 0 0 1 ?
> *> 2001:3::/64 2001::1 0 0 1 ?
> Route Distinguisher: 1:30
> *> 2001:8::/64 2001::1 0 0 1 ?
> *= 2001::1 0 0 1 ?
> Route Distinguisher: 2:10
> 2001:2::/64 ::@6< 0 32768 ?
> Route Distinguisher: 2:20
> *> 2001:4::/64 ::@7< 0 32768 ?
> ::@7< 0 32768 ?
> *> 2001:6::/64 ::@7< 0 32768 ?
> ::@7< 0 32768 ?
>
> Displayed 6 routes and 9 total paths
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 9d2e8caa87..5c3f8eacea 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -1101,6 +1101,46 @@ void delete_vrf_tovpn_sid_per_vrf(struct bgp *bgp_vpn, struct bgp *bgp_vrf)
bgp_vrf->tovpn_sid_locator = NULL;
}
+/*
+ * A stopped zebra has lost the SIDs of the VRF and their local SIDs: withdraw
+ * the VPN paths exported with them, and forget them without releasing them,
+ * so that they are requested again once the locator is received.
+ */
+void vpn_leak_zebra_vrf_sids_forget(struct bgp *bgp_vrf)
+{
+ struct bgp *bgp_vpn = bgp_get_default();
+ afi_t afi;
+
+ for (afi = AFI_IP; afi <= AFI_IP6; afi++)
+ if (bgp_vrf->vpn_policy[afi].tovpn_sid || bgp_vrf->tovpn_sid)
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, bgp_vpn, bgp_vrf);
+
+ for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
+ if (bgp_vrf->vpn_policy[afi].tovpn_sid) {
+ UNSET_FLAG(bgp_vrf->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_SID_FUNC_WIDE);
+ if (bgp_vpn)
+ sid_unregister(bgp_vpn, bgp_vrf->vpn_policy[afi].tovpn_sid);
+ XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->vpn_policy[afi].tovpn_sid);
+ }
+ XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent);
+ bgp_vrf->vpn_policy[afi].tovpn_sid_transpose_label = 0;
+ srv6_locator_free(bgp_vrf->vpn_policy[afi].tovpn_sid_locator);
+ bgp_vrf->vpn_policy[afi].tovpn_sid_locator = NULL;
+ }
+
+ if (bgp_vrf->tovpn_sid) {
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_TOVPN_SID_FUNC_WIDE);
+ if (bgp_vpn)
+ sid_unregister(bgp_vpn, bgp_vrf->tovpn_sid);
+ XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->tovpn_sid);
+ }
+ XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->tovpn_zebra_vrf_sid_last_sent);
+ bgp_vrf->tovpn_sid_transpose_label = 0;
+ srv6_locator_free(bgp_vrf->tovpn_sid_locator);
+ bgp_vrf->tovpn_sid_locator = NULL;
+}
+
void delete_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi)
{
delete_vrf_tovpn_sid_per_af(bgp_vpn, bgp_vrf, afi);
diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h
index 755a74b067..e08e581081 100644
--- a/bgpd/bgp_mplsvpn.h
+++ b/bgpd/bgp_mplsvpn.h
@@ -85,6 +85,7 @@ extern void delete_vrf_tovpn_sid(struct bgp *vpn, struct bgp *vrf, afi_t afi);
extern void delete_vrf_tovpn_sid_per_af(struct bgp *vpn, struct bgp *vrf,
afi_t afi);
extern void delete_vrf_tovpn_sid_per_vrf(struct bgp *vpn, struct bgp *vrf);
+extern void vpn_leak_zebra_vrf_sids_forget(struct bgp *bgp_vrf);
extern void ensure_vrf_tovpn_sid_per_af(struct bgp *vpn, struct bgp *vrf,
afi_t afi);
extern void ensure_vrf_tovpn_sid_per_vrf(struct bgp *vpn, struct bgp *vrf);
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 00c3e98cba..cc3a27c21b 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -3400,6 +3400,9 @@ static void bgp_zebra_disconnected(struct zclient *zclient)
struct listnode *node;
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
+ /* zebra has lost the SIDs of BGP */
+ vpn_leak_zebra_vrf_sids_forget(bgp);
+
/* the locators may have changed when zebra is started again */
srv6_locator_free(bgp->srv6_locator);
bgp->srv6_locator = NULL;