]> 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 14:56:27 +0000 (16:56 +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 d65329d7aed3665276f3d39ecc0e622039692c0c..3f5cf0d259ab8f21068d045208d9da69d01d9d10 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");
@@ -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);
index ec561b2636084d30488f51d909b924514ab9bc49..71b839168914ac0446112a1da4ca462852a1c77d 100644 (file)
@@ -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);
 
   /*
index cc11674f6de52eb630fa5b26c71c788504958ad6..330cd822b3f1c15da0548b6739475d9ad36d17ff 100644 (file)
@@ -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";
 }
 
 /*
index ec86442b219eaf918ee2f81ec54ddad76d91f40f..706e9b0bd1f3af0bf1062c20f4a768f417f317e7 100644 (file)
@@ -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;
index dac6e84eaa415792c0347bb07cc909dc999c84ef..c3a49f5c625d11984e74e15f30070f086c692a2c 100644 (file)
@@ -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);
index eeee3b4f3fee4497ed94d41e3bc7770073100eeb..9ee6d0adb197585762ba4ac992e2a7b4df0ad1e4 100644 (file)
@@ -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: