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