]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
BGP: Fix route refresh behavior
authorIgor Putovny <igor.putovny@nic.cz>
Mon, 5 May 2025 13:47:10 +0000 (15:47 +0200)
committerMaria Matejka <mq@ucw.cz>
Sat, 10 May 2025 11:55:35 +0000 (13:55 +0200)
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 <mq@ucw.cz>
nest/proto.c
nest/protocol.h
proto/bgp/attrs.c
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/packets.c

index 51b2561e1faa7b45f45bf321f881fb1c3a5ac18a..3565f229d49885bb084eb8c640907a1827da73fa 100644 (file)
@@ -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);
index cb506bf3753d33b57f47d9b60e9b9d69a32e2a7e..ae02ebb8e7c98ec1562442cdc98a619be812f220 100644 (file)
@@ -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);
 
   /*
index 690ffd2a049f6fbef49cb570574bf055b7f5cfa8..d5162bbd0059943db0aa66524c145aa9786583be 100644 (file)
@@ -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";
 }
 
 /*
index e5d0dba29413efdb4ea0ce0ba77d5e7be1148541..d12444ed1a457d13fc8d2840f48169a7305750ff 100644 (file)
@@ -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;
index 39a2fb55c6d3be6c1ecd8bf6fc3d175b28974a83..cd37227b174f8621b24d20a50c9c202934509e13 100644 (file)
@@ -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);
index d9c4ee5971d9684289aac1d919ad4279aee92a41..12b60729cb7579f6a309b0169ad19a53eb8f1f72 100644 (file)
@@ -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: