Commit 36add0f5da for frr
commit 36add0f5da76696f4864d0acee10c3aca001d67e
Author: Enke Chen <enchen@paloaltonetworks.com>
Date: Tue Nov 3 14:14:03 2026 -0800
bgpd: fix SUBGRP_STATUS_DEFAULT_ORIGINATE flag handling
Fix inconsistent handling of the SUBGRP_STATUS_DEFAULT_ORIGINATE flag
which tracks whether the synthesized default route has been sent to a
subgroup.
Problems fixed:
1. Spurious withdraws: SET_FLAG was called when the flag was NOT set
(in RMAP_DENYMATCH and undefined route-map cases), causing withdraws
to be sent for routes that were never advertised.
2. Missing withdraws: At the time of a policy change, we don't yet know
whether the new policy would result in a re-advertisement or a
withdraw. UNSET_FLAG was called in various places (route refresh,
policy update, soft clear, etc.) to "force re-announcement", but
clearing the flag prematurely can result in missing withdraws since
the flag is checked before sending withdraw.
3. Stale updates: When the table changed and the default-originate timer
fired, the flag check prevented sending an UPDATE with new attributes
if a default route had been previously advertised, resulting in stale
routing information.
The fix ensures:
- SET_FLAG is only called before sending UPDATE
- UNSET_FLAG is only called after sending WITHDRAW
- UPDATE is always sent in the else branch
With this fix, redundant UPDATEs can be sent upon a policy change or
table change when the resulting attributes remain the same. This issue
existed before, although to a lesser extent. We will address it in a
subsequent PR by comparing attributes to avoid redundant UPDATEs.
Signed-off-by: Enke Chen <enchen@paloaltonetworks.com>
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 461e23cbcf..2eda2afb0b 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -3009,7 +3009,6 @@ static int bgp_route_refresh_receive(struct peer_connection *connection, bgp_siz
iana_safi_t pkt_safi;
safi_t safi;
struct stream *s;
- struct peer_af *paf;
uint8_t subtype;
bgp_size_t msg_length =
size - (BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE);
@@ -3285,19 +3284,6 @@ static int bgp_route_refresh_receive(struct peer_connection *connection, bgp_siz
UNSET_FLAG(peer->af_sflags[afi][safi],
PEER_STATUS_ORF_WAIT_REFRESH);
- paf = peer_af_find(peer, afi, safi);
- if (paf && paf->subgroup) {
- /* If the peer is configured for default-originate clear the
- * SUBGRP_STATUS_DEFAULT_ORIGINATE flag so that we will
- * re-advertise the
- * default
- */
- if (CHECK_FLAG(paf->subgroup->sflags,
- SUBGRP_STATUS_DEFAULT_ORIGINATE))
- UNSET_FLAG(paf->subgroup->sflags,
- SUBGRP_STATUS_DEFAULT_ORIGINATE);
- }
-
if (bgp_debug_neighbor_events(peer))
zlog_debug("%pBP rcvd route-refresh (REQUEST) for %s/%s", peer, afi2str(afi),
safi2str(safi));
diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c
index d89c6bf46f..66edd3c561 100644
--- a/bgpd/bgp_updgrp.c
+++ b/bgpd/bgp_updgrp.c
@@ -1890,11 +1890,7 @@ static int updgrp_policy_update_walkcb(struct update_group *updgrp, void *arg)
* the route-map is still present. If it was deleted,
* subgroup_default_originate() detects the missing map
* and withdraws the default route.
- *
- * Clear the flag to force re-announcement with
- * potentially updated attributes from the route-map.
*/
- UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE);
subgroup_default_originate(subgrp);
}
update_subgroup_set_needs_refresh(subgrp, 0);
@@ -2300,14 +2296,8 @@ update_group_default_originate_route_map_walkcb(struct update_group *updgrp,
afi = SUBGRP_AFI(subgrp);
safi = SUBGRP_SAFI(subgrp);
- if (peer->default_rmap[afi][safi].name) {
- /*
- * Clear the flag to force re-announcement with
- * potentially updated attributes from the route-map.
- */
- UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE);
+ if (peer->default_rmap[afi][safi].name)
subgroup_default_originate(subgrp);
- }
}
return UPDWALK_CONTINUE;
diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c
index c2f2d4f435..b48484c095 100644
--- a/bgpd/bgp_updgrp_adv.c
+++ b/bgpd/bgp_updgrp_adv.c
@@ -1240,9 +1240,6 @@ void subgroup_default_originate(struct update_subgroup *subgrp)
* Route-map is configured but undefined or empty.
* Treat as implicit deny - withdraw the default.
*/
- if (!do_withdraw &&
- !CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE))
- SET_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE);
do_withdraw = true;
}
}
@@ -1322,18 +1319,14 @@ rmap_done:;
}
if (do_withdraw) {
- /* Withdraw the synthesized default. Always send the withdraw
- * packet - the caller may have cleared the flag before calling
- * to force re-evaluation.
- *
- * TBD: Track the synthesized route's attributes so we can check
- * the flag properly and avoid unnecessary withdraw packets.
- */
- subgroup_default_withdraw_packet(subgrp);
- UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE);
+ /* Withdraw the synthesized default if it was previously sent. */
+ if (CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE)) {
+ subgroup_default_withdraw_packet(subgrp);
+ UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE);
+ }
} else {
- if (!CHECK_FLAG(subgrp->sflags,
- SUBGRP_STATUS_DEFAULT_ORIGINATE)) {
+ /* TBD: compare attributes to avoid sending redundant UPDATEs. */
+ {
struct attr attr_copy;
/*
@@ -1356,17 +1349,13 @@ rmap_done:;
p.prefixlen = 0;
if (bgp_route_map_setting_only(bgp, rmap, PEER_RMAP_TYPE_DEFAULT,
&p, &attr_copy) == RMAP_DENYMATCH) {
- /*
- * Route-map denied - withdraw default.
- * The flag may have been cleared by callers,
- * so withdraw unconditionally. This may send
- * an unnecessary withdraw, but a deny-only
- * route-map is not a practical scenario.
- */
+ /* Route-map denied - withdraw default if previously sent. */
bgp_attr_flush(&attr_copy);
aspath_unintern(&aspath);
- subgroup_default_withdraw_packet(subgrp);
- UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE);
+ if (CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE)) {
+ subgroup_default_withdraw_packet(subgrp);
+ UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE);
+ }
return;
}
/* Clear reflection attributes - default-originate
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 723146905e..b091121137 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -6716,13 +6716,10 @@ int peer_default_originate_set(struct peer *peer, afi_t afi, safi_t safi,
{
struct peer *member;
struct listnode *node, *nnode;
- struct update_subgroup *subgrp;
/* Set flag and configuration on peer. */
peer_af_flag_set(peer, afi, safi, PEER_FLAG_DEFAULT_ORIGINATE);
- subgrp = peer_subgroup(peer, afi, safi);
-
if (rmap) {
if (!peer->bgp->rmap_def_originate_eval_timer)
peer->bgp->rmap_def_originate_eval_timer =
@@ -6739,17 +6736,6 @@ int peer_default_originate_set(struct peer *peer, afi_t afi, safi_t safi,
peer->default_rmap[afi][safi].name);
}
- /*
- * When there is a change in route-map policy,
- * this flow gets triggered. Since, the default
- * route is already originated, the flag is set.
- * The flag should be unset here,
- * to trigger the flow of sending update message.
- */
- if (subgrp)
- UNSET_FLAG(subgrp->sflags,
- SUBGRP_STATUS_DEFAULT_ORIGINATE);
-
route_map_counter_decrement(map);
peer->default_rmap[afi][safi].name =
XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap);
@@ -6766,15 +6752,6 @@ int peer_default_originate_set(struct peer *peer, afi_t afi, safi_t safi,
peer->default_rmap[afi][safi].name);
}
- /*
- * This is triggered in case of route-map deletion.
- * The flag needs to be unset, to trigger the flow
- * of sending an update message.
- */
- if (subgrp)
- UNSET_FLAG(subgrp->sflags,
- SUBGRP_STATUS_DEFAULT_ORIGINATE);
-
route_map_counter_decrement(map);
peer->default_rmap[afi][safi].name = NULL;
peer->default_rmap[afi][safi].map = NULL;
@@ -9484,8 +9461,6 @@ int peer_clear(struct peer *peer, struct listnode **nnode)
int peer_clear_soft(struct peer *peer, afi_t afi, safi_t safi,
enum bgp_clear_type stype)
{
- struct peer_af *paf;
-
if (!peer_established(peer->connection))
return 0;
@@ -9494,17 +9469,8 @@ int peer_clear_soft(struct peer *peer, afi_t afi, safi_t safi,
peer->rtt = sockopt_tcp_rtt(peer->connection->fd);
- if (stype == BGP_CLEAR_SOFT_OUT || stype == BGP_CLEAR_SOFT_BOTH) {
- /* Clear the "neighbor x.x.x.x default-originate" flag */
- paf = peer_af_find(peer, afi, safi);
- if (paf && paf->subgroup
- && CHECK_FLAG(paf->subgroup->sflags,
- SUBGRP_STATUS_DEFAULT_ORIGINATE))
- UNSET_FLAG(paf->subgroup->sflags,
- SUBGRP_STATUS_DEFAULT_ORIGINATE);
-
+ if (stype == BGP_CLEAR_SOFT_OUT || stype == BGP_CLEAR_SOFT_BOTH)
bgp_announce_route(peer, afi, safi, true);
- }
if (stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX) {
if (CHECK_FLAG(peer->af_cap[afi][safi],