Hi,

When import table is enabled for bgp, "reload in" uses the table and
one cannot force refresh from the peer. I suggest to add refresh
functions to reload routes on a protocol level, and "reload receive"
command to call them. I used "reload receive" not to add additional
keywords and it seems descriptive too. Pathches are attached.
commit 7c97f39d851aabf01672e2092d929e0be3566047
Author: Alexander Zubkov <[email protected]>
Date:   Sun Oct 16 13:50:53 2022 +0200

    BGP: command to refresh routes from the peer
    
    When import table is enabled for the BGP channel, "reload in" command
    uses this table and initiating a manual refresh is not possible.
    Add separate cli command "reload recevie" that always refreshes routes
    from the peer.

diff --git a/nest/config.Y b/nest/config.Y
index a91fd976..3abff960 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -890,6 +890,8 @@ CF_CLI(RESTART, proto_patt opttext, (<protocol> | \"<pattern>\" | all) [message]
 { proto_apply_cmd($2, proto_cmd_restart, 1, (uintptr_t) $3); } ;
 CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]])
 { proto_apply_cmd($2, proto_cmd_reload, 1, CMD_RELOAD); } ;
+CF_CLI(RELOAD RECEIVE, proto_patt, <protocol> | \"<pattern>\" | all, [[Refresh protocol (request routes from the peer)]])
+{ proto_apply_cmd($3, proto_cmd_refresh, 1, 0); } ;
 CF_CLI(RELOAD IN, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]])
 { proto_apply_cmd($3, proto_cmd_reload, 1, CMD_RELOAD_IN); } ;
 CF_CLI(RELOAD OUT, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just exported routes)]])
diff --git a/nest/proto.c b/nest/proto.c
index 885a0b75..99d04740 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -48,6 +48,7 @@ static char *e_states[] = { "DOWN", "FEEDING", "READY" };
 
 extern struct protocol proto_unix_iface;
 
+static void channel_request_refresh(struct channel *c);
 static void channel_request_reload(struct channel *c);
 static void proto_shutdown_loop(timer *);
 static void proto_rethink_goal(struct proto *p);
@@ -62,6 +63,9 @@ static inline int proto_is_done(struct proto *p)
 static inline int channel_is_active(struct channel *c)
 { return (c->channel_state == CS_START) || (c->channel_state == CS_UP); }
 
+static inline int channel_refreshable(struct channel *c)
+{ return c->proto->refresh_routes && c->refreshable; }
+
 static inline int channel_reloadable(struct channel *c)
 { return c->proto->reload_routes && c->reloadable; }
 
@@ -729,6 +733,24 @@ channel_request_feeding(struct channel *c)
   channel_log_state_change(c);
 }
 
+static void
+channel_request_refresh(struct channel *c)
+{
+  ASSERT(c->channel_state == CS_UP);
+  ASSERT(channel_refreshable(c));
+
+  CD(c, "Refresh requested");
+
+  c->proto->refresh_routes(c);
+
+  /*
+   * Should this be done before refresh_routes() hook?
+   * Perhaps, but routes are updated asynchronously.
+   */
+  channel_reset_limit(&c->rx_limit);
+  channel_reset_limit(&c->in_limit);
+}
+
 static void
 channel_request_reload(struct channel *c)
 {
@@ -2143,6 +2165,39 @@ proto_cmd_restart(struct proto *p, uintptr_t arg, int cnt UNUSED)
   cli_msg(-12, "%s: restarted", p->name);
 }
 
+void
+proto_cmd_refresh(struct proto *p, uintptr_t arg UNUSED, int cnt UNUSED)
+{
+  struct channel *c;
+
+  if (p->disabled)
+  {
+    cli_msg(-8, "%s: already disabled", p->name);
+    return;
+  }
+
+  /* If the protocol in not UP, it has no routes */
+  if (p->proto_state != PS_UP)
+    return;
+
+  /* All channels must support refresh */
+  WALK_LIST(c, p->channels)
+    if ((c->channel_state == CS_UP) && !channel_refreshable(c))
+    {
+      cli_msg(-8006, "%s: refresh failed", p->name);
+      return;
+    }
+
+  log(L_INFO "Refreshing protocol %s", p->name);
+
+  /* refreshing routes */
+  WALK_LIST(c, p->channels)
+    if (c->channel_state == CS_UP)
+      channel_request_refresh(c);
+
+  cli_msg(-15, "%s: refreshing", p->name);
+}
+
 void
 proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED)
 {
diff --git a/nest/protocol.h b/nest/protocol.h
index 46744357..080d28c6 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -202,6 +202,7 @@ struct proto {
    *			  -1 = reject,
    *			   0 = continue to export filter
    *			   1 = accept immediately
+   *	   refresh_routes  Request to initiate refresh from the peer
    *	   reload_routes   Request channel to reload all its routes to the core
    *			(using rte_update()). Returns: 0=reload cannot be done,
    *			1= reload is scheduled and will happen (asynchronously).
@@ -214,6 +215,7 @@ struct proto {
   void (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old);
   void (*neigh_notify)(struct neighbor *neigh);
   int (*preexport)(struct channel *, struct rte *rt);
+  void (*refresh_routes)(struct channel *);
   void (*reload_routes)(struct channel *);
   void (*feed_begin)(struct channel *, int initial);
   void (*feed_end)(struct channel *);
@@ -283,6 +285,7 @@ void proto_cmd_show(struct proto *, uintptr_t, int);
 void proto_cmd_disable(struct proto *, uintptr_t, int);
 void proto_cmd_enable(struct proto *, uintptr_t, int);
 void proto_cmd_restart(struct proto *, uintptr_t, int);
+void proto_cmd_refresh(struct proto *, uintptr_t, int);
 void proto_cmd_reload(struct proto *, uintptr_t, int);
 void proto_cmd_debug(struct proto *, uintptr_t, int);
 void proto_cmd_mrtdump(struct proto *, uintptr_t, int);
@@ -534,6 +537,7 @@ struct channel {
   u8 feed_active;
   u8 flush_active;
   u8 refeeding;				/* We are refeeding (valid only if export_state == ES_FEEDING) */
+  u8 refreshable;			/* Hook reload_refresh() is allowed on the channel */
   u8 reloadable;			/* Hook reload_routes() is allowed on the channel */
   u8 gr_lock;				/* Graceful restart mechanism should wait for this channel */
   u8 gr_wait;				/* Route export to channel is postponed until graceful restart */
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index ad78d5e5..7dec7358 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -607,7 +607,8 @@ 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->cf->import_table;
+    c->c.refreshable = p->route_refresh;
+    c->c.reloadable = c->c.refreshable || c->cf->import_table;
 
     c->index = active ? num++ : 0;
 
@@ -1388,6 +1389,21 @@ bgp_update_bfd(struct bgp_proto *p, const struct bfd_options *bfd)
   }
 }
 
+static void
+bgp_refresh_routes(struct channel *C)
+{
+  struct bgp_proto *p = (void *) C->proto;
+  struct bgp_channel *c = (void *) C;
+
+  /* Ignore non-BGP channels */
+  if (C->channel != &channel_bgp)
+    return;
+
+  ASSERT(p->conn && p->route_refresh);
+
+  bgp_schedule_packet(p->conn, c, PKT_ROUTE_REFRESH);
+}
+
 static void
 bgp_reload_routes(struct channel *C)
 {
@@ -1403,7 +1419,7 @@ bgp_reload_routes(struct channel *C)
   if (c->c.in_table)
     channel_schedule_reload(C);
   else
-    bgp_schedule_packet(p->conn, c, PKT_ROUTE_REFRESH);
+    bgp_refresh_routes(C);
 }
 
 static void
@@ -1685,6 +1701,7 @@ bgp_init(struct proto_config *CF)
   P->rt_notify = bgp_rt_notify;
   P->preexport = bgp_preexport;
   P->neigh_notify = bgp_neigh_notify;
+  P->refresh_routes = bgp_refresh_routes;
   P->reload_routes = bgp_reload_routes;
   P->feed_begin = bgp_feed_begin;
   P->feed_end = bgp_feed_end;
commit 7e1835c3ce6d92d298d6689ffde7a972ff679742
Author: Alexander Zubkov <[email protected]>
Date:   Sun Oct 16 14:23:12 2022 +0200

    add documentation for "reload receive" command

diff --git a/doc/bird.sgml b/doc/bird.sgml
index 3dc1e294..132e3d20 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -1201,7 +1201,7 @@ This argument can be omitted if there exists only a single instance.
 	Enable, disable or restart a given protocol instance, instances matching
 	the <cf><m/pattern/</cf> or <cf/all/ instances.
 
-	<tag><label id="cli-reload">reload [in|out] <m/name/|"<m/pattern/"|all</tag>
+	<tag><label id="cli-reload">reload [in|out|receive] <m/name/|"<m/pattern/"|all</tag>
 	Reload a given protocol instance, that means re-import routes from the
 	protocol instance and re-export preferred routes to the instance. If
 	<cf/in/ or <cf/out/ options are used, the command is restricted to one
@@ -1212,6 +1212,9 @@ This argument can be omitted if there exists only a single instance.
 	propagates the old set of routes. For example when <cf/configure soft/
 	command was used to change filters.
 
+	Re-import do not use route-refresh in BGP when <cf/import table/ is enabled.
+	To initiate a protocol-dependent reload use <cf/reload receive/.
+
 	Re-export always succeeds, but re-import is protocol-dependent and might
 	fail (for example, if BGP neighbor does not support route-refresh
 	extension). In that case, re-export is also skipped. Note that for the

Reply via email to