From 49774a770e4b4767578b4b4d0de5a658bca39c6d Mon Sep 17 00:00:00 2001 From: Igor Putovny Date: Mon, 5 May 2025 15:47:10 +0200 Subject: [PATCH] BGP: Fix route refresh behavior On import filter reconfiguration, the route refresh capability is now honored and used instead of restarting the session. On export filter reconfiguration, the enhanced route refresh capability is used to indicate BoRR and EoRR, unless the export table is on. In such cases, only relevant changes are sent as proper updates. When route refresh request is received, the enhanced route refresh capability is now honored. Co-Authored-By: Maria Matejka --- nest/proto.c | 5 +- nest/protocol.h | 1 + proto/bgp/attrs.c | 16 +++++- proto/bgp/bgp.c | 133 ++++++++++++++++++++++++++++++++++---------- proto/bgp/bgp.h | 4 +- proto/bgp/packets.c | 6 +- 6 files changed, 127 insertions(+), 38 deletions(-) diff --git a/nest/proto.c b/nest/proto.c index 51b2561e1..3565f229d 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -57,6 +57,7 @@ static inline void channel_reimport(struct channel *c, struct rt_feeding_request static inline void channel_refeed(struct channel *c, struct rt_feeding_request *rfr) { + CALL(c->proto->refeed_begin, c, rfr); rt_export_refeed(&c->out_req, rfr); } @@ -1209,7 +1210,7 @@ channel_reconfigure(struct channel *c, struct channel_config *cf) channel_request_reload(c, NULL); if (export_changed) - channel_request_full_refeed(c); + channel_refeed(c, NULL); done: CD(c, "Reconfigured"); @@ -2844,7 +2845,7 @@ proto_cmd_reload(struct proto *p, uintptr_t _prr, int cnt UNUSED) if (prr->dir & CMD_RELOAD_OUT) if (c->out_req.name) - rt_export_refeed(&c->out_req, channel_create_reload_request(prr)); + channel_refeed(c, channel_create_reload_request(prr)); } cli_msg(-15, "%s: reloading", p->name); diff --git a/nest/protocol.h b/nest/protocol.h index cb506bf37..ae02ebb8e 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -201,6 +201,7 @@ struct proto { void (*rt_notify)(struct proto *, struct channel *, const net_addr *net, struct rte *new, const struct rte *old); int (*preexport)(struct channel *, struct rte *rt); void (*export_fed)(struct channel *); + void (*refeed_begin)(struct channel *, struct rt_feeding_request *rfr); int (*reload_routes)(struct channel *, struct rt_feeding_request *cir); /* diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 690ffd2a0..d5162bbd0 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -2000,9 +2000,15 @@ bgp_done_prefix(struct bgp_ptx_private *c, struct bgp_prefix *px, struct bgp_buc bgp_free_prefix(c, px); } -void +const char * bgp_tx_resend(struct bgp_proto *p, struct bgp_channel *bc) { + if (p->enhanced_refresh) + { + bc->feed_state = BFS_REFRESHING; + bgp_schedule_packet(p->conn, bc, PKT_BEGIN_REFRESH); + } + uint seen = 0; { BGP_PTX_LOCK(bc->tx, c); @@ -2031,8 +2037,14 @@ bgp_tx_resend(struct bgp_proto *p, struct bgp_channel *bc) bc->c.proto->name, bc->c.name, seen); } - if (seen) + + if (p->enhanced_refresh) + bc->feed_state = BFS_REFRESHED; + + if (seen || p->enhanced_refresh) bgp_schedule_packet(p->conn, bc, PKT_UPDATE); + + return p->enhanced_refresh ? "from export table by enhanced route refresh" : "from export table by simple refeed"; } /* diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index e5d0dba29..d12444ed1 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -1109,6 +1109,8 @@ bgp_stop(struct bgp_proto *p, int subcode, byte *data, uint len) bgp_graceful_close_conn(&p->outgoing_conn, subcode, data, len); bgp_graceful_close_conn(&p->incoming_conn, subcode, data, len); + p->p.reload_routes = NULL; + struct bgp_channel *c; BGP_WALK_CHANNELS(p, c) bgp_free_pending_tx(c); @@ -1204,7 +1206,7 @@ bgp_conn_enter_established_state(struct bgp_conn *conn) int active = loc->ready && rem->ready; c->c.disabled = !active; - c->c.reloadable = p->route_refresh || ((c->c.in_keep & RIK_PREFILTER) == RIK_PREFILTER); + c->c.reloadable = p->route_refresh; c->index = active ? num++ : 0; @@ -2186,6 +2188,51 @@ bgp_reload_in(struct proto *P, uintptr_t _ UNUSED, int __ UNUSED) cli_msg(-8006, "%s: not reloading, not up", P->name); } +struct bgp_enhanced_refresh_request { + struct rt_feeding_request rfr; + struct bgp_channel *c; +}; + +static void +bgp_done_route_refresh(struct rt_feeding_request *rfr) +{ + SKIP_BACK_DECLARE(struct bgp_enhanced_refresh_request, berr, rfr, rfr); + struct bgp_channel *c = berr->c; + SKIP_BACK_DECLARE(struct bgp_proto, p, p, c->c.proto); + + /* Schedule EoRR packet */ + ASSERT_DIE(c->feed_state == BFS_REFRESHING); + + c->feed_state = BFS_REFRESHED; + bgp_schedule_packet(p->conn, c, PKT_UPDATE); + + mb_free(berr); +} + +const char * +bgp_begin_route_refresh(struct bgp_proto *p, struct bgp_channel *c) +{ + if (c->tx_keep) + return bgp_tx_resend(p, c); + + if (!p->enhanced_refresh) { + rt_export_refeed(&c->c.out_req, NULL); + return "from table by simple refeed"; + } + + struct bgp_enhanced_refresh_request *berr = mb_alloc(p->p.pool, sizeof *berr); + *berr = (struct bgp_enhanced_refresh_request) { + .c = c, + .rfr.done = bgp_done_route_refresh, + }; + + c->feed_state = BFS_REFRESHING; + bgp_schedule_packet(p->conn, c, PKT_BEGIN_REFRESH); + + rt_export_refeed(&c->c.out_req, &berr->rfr); + return "from table by enhanced route refresh"; +} + void bgp_reload_out(struct proto *P, uintptr_t _ UNUSED, int __ UNUSED) { @@ -2196,40 +2243,59 @@ bgp_reload_out(struct proto *P, uintptr_t _ UNUSED, int __ UNUSED) struct bgp_channel *c; BGP_WALK_CHANNELS(p, c) if (&c->c != P->mpls_channel) - if (c->tx_keep) - { - bgp_tx_resend(p, c); - cli_msg(-15, "%s.%s: reloading", P->name, c->c.name); - } - else - { - rt_export_refeed(&c->c.out_req, NULL); - cli_msg(-15, "%s.%s: reloading by table refeed", P->name, c->c.name); - } + { + const char *info = bgp_begin_route_refresh(p, c); + cli_msg(-15, "%s.%s: reloading %s", P->name, c->c.name, info); + } } else cli_msg(-8006, "%s: not reloading, not up", P->name); } -struct bgp_enhanced_refresh_request { - struct rt_feeding_request rfr; - struct bgp_channel *c; -}; - -void -bgp_done_route_refresh(struct rt_feeding_request *rfr) +static int +bgp_reload_routes(struct channel *C, struct rt_feeding_request *rfr) { - SKIP_BACK_DECLARE(struct bgp_enhanced_refresh_request, berr, rfr, rfr); - struct bgp_channel *c = berr->c; - SKIP_BACK_DECLARE(struct bgp_proto, p, p, c->c.proto); + /* Can't reload partially */ + if (rfr && rfr->prefilter.mode) + return 0; - /* Schedule EoRR packet */ - ASSERT_DIE(c->feed_state == BFS_REFRESHING); + struct bgp_proto *p = SKIP_BACK(struct bgp_proto, p, C->proto); - c->feed_state = BFS_REFRESHED; - bgp_schedule_packet(p->conn, c, PKT_UPDATE); + if (p->p.proto_state == PS_UP) + { + struct bgp_channel *c; + BGP_WALK_CHANNELS(p, c) + if (&c->c != p->p.mpls_channel) + { + log("%s.%s: reloading", p->p.name, c->c.name); + bgp_schedule_packet(p->conn, c, PKT_ROUTE_REFRESH); + } + } + else + log("%s: not reloading, not up", p->p.name); - mb_free(berr); + if (rfr) + CALL(rfr->done, rfr); + + return 1; +} + +static void +bgp_refeed_begin(struct channel *C, struct rt_feeding_request *rfr) +{ + struct bgp_channel *c = SKIP_BACK(struct bgp_channel, c, C); + struct bgp_proto *p = SKIP_BACK(struct bgp_proto, p, C->proto); + + /* Do not announce partial refeed */ + if (rfr && rfr->prefilter.mode) + return; + + /* Run the enhanced refresh if available */ + if (p->enhanced_refresh && !c->tx_keep) + { + c->feed_state = BFS_REFRESHING; + bgp_schedule_packet(p->conn, c, PKT_BEGIN_REFRESH); + } } static void @@ -2239,10 +2305,17 @@ bgp_export_fed(struct channel *C) SKIP_BACK_DECLARE(struct bgp_proto, p, p, c->c.proto); /* Schedule End-of-RIB packet */ - if (c->feed_state == BFS_LOADING) + switch (c->feed_state) { - c->feed_state = BFS_LOADED; - bgp_schedule_packet(p->conn, c, PKT_UPDATE); + case BFS_LOADING: + c->feed_state = BFS_LOADED; + bgp_schedule_packet(p->conn, c, PKT_UPDATE); + break; + + case BFS_REFRESHING: + c->feed_state = BFS_REFRESHED; + bgp_schedule_packet(p->conn, c, PKT_UPDATE); + break; } } @@ -2513,7 +2586,9 @@ bgp_init(struct proto_config *CF) P->rt_notify = bgp_rt_notify; P->preexport = bgp_preexport; P->iface_sub.neigh_notify = bgp_neigh_notify; + P->refeed_begin = bgp_refeed_begin; P->export_fed = bgp_export_fed; + P->reload_routes = bgp_reload_routes; P->sources.class = &bgp_rte_owner_class; P->sources.rte_recalculate = cf->deterministic_med ? bgp_rte_recalculate : NULL; diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 39a2fb55c..cd37227b1 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -668,6 +668,8 @@ const char *bgp_format_role_name(u8 role); void bgp_reload_in(struct proto *P, uintptr_t, int); void bgp_reload_out(struct proto *P, uintptr_t, int); +const char *bgp_begin_route_refresh(struct bgp_proto *p, struct bgp_channel *c); + static inline int rte_resolvable(const rte *rt) { @@ -715,7 +717,7 @@ void bgp_setup_out_table(struct bgp_channel *c); void bgp_init_pending_tx(struct bgp_channel *c); void bgp_free_pending_tx(struct bgp_channel *c); -void bgp_tx_resend(struct bgp_proto *p, struct bgp_channel *c); +const char *bgp_tx_resend(struct bgp_proto *p, struct bgp_channel *c); void bgp_withdraw_bucket(struct bgp_ptx_private *c, struct bgp_bucket *b); int bgp_done_bucket(struct bgp_ptx_private *c, struct bgp_bucket *b); diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index d9c4ee597..12b60729c 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -3050,10 +3050,8 @@ bgp_rx_route_refresh(struct bgp_conn *conn, byte *pkt, uint len) { case BGP_RR_REQUEST: BGP_TRACE(D_PACKETS, "Got ROUTE-REFRESH"); - if (c->tx_keep) - bgp_tx_resend(p, c); - else - rt_export_refeed(&c->c.out_req, NULL); + const char *info = bgp_begin_route_refresh(p, c); + BGP_TRACE(D_EVENTS, "Reloading %s %s", c->c.name, info); break; case BGP_RR_BEGIN: -- 2.47.2