From: Igor Putovny Date: Mon, 5 May 2025 13:47:10 +0000 (+0200) Subject: BGP: Fix route refresh behavior X-Git-Tag: v3.0.3~14 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=91a0e078796ee745c27d58fbbdb35ef11b5ab483;p=thirdparty%2Fbird.git 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 --- diff --git a/nest/proto.c b/nest/proto.c index d65329d7a..3f5cf0d25 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"); @@ -2836,7 +2837,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 ec561b263..71b839168 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -200,6 +200,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 cc11674f6..330cd822b 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -1981,9 +1981,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); @@ -2012,8 +2018,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 ec86442b2..706e9b0bd 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -658,6 +658,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); @@ -753,7 +755,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; @@ -1687,6 +1689,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) { @@ -1697,40 +1744,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 @@ -1740,10 +1806,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; } } @@ -2005,7 +2078,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 dac6e84ea..c3a49f5c6 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -634,6 +634,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) { @@ -681,7 +683,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 eeee3b4f3..9ee6d0adb 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -3007,10 +3007,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: