{.tokenname = "PGNAME", .completions = bgp_ac_peergroup},
{.completions = NULL} };
+DEFINE_HOOK(bgp_config_end, (struct bgp *bgp), (bgp));
+
+static struct thread *t_bgp_cfg;
+
+bool bgp_config_inprocess(void)
+{
+ return thread_is_scheduled(t_bgp_cfg);
+}
+
+static void bgp_config_finish(struct thread *t)
+{
+ struct listnode *node;
+ struct bgp *bgp;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp))
+ hook_call(bgp_config_end, bgp);
+}
+
+static void bgp_config_start(void)
+{
+#define BGP_PRE_CONFIG_MAX_WAIT_SECONDS 600
+ THREAD_OFF(t_bgp_cfg);
+ thread_add_timer(bm->master, bgp_config_finish, NULL,
+ BGP_PRE_CONFIG_MAX_WAIT_SECONDS, &t_bgp_cfg);
+}
+
+/* When we receive a hook the configuration is read,
+ * we start a timer to make sure we postpone sending
+ * EoR before route-maps are processed.
+ * This is especially valid if using `bgp route-map delay-timer`.
+ */
+static void bgp_config_end(void)
+{
+#define BGP_POST_CONFIG_DELAY_SECONDS 1
+ uint32_t bgp_post_config_delay =
+ thread_is_scheduled(bm->t_rmap_update)
+ ? thread_timer_remain_second(bm->t_rmap_update)
+ : BGP_POST_CONFIG_DELAY_SECONDS;
+
+ /* If BGP config processing thread isn't running, then
+ * we can return and rely it's properly handled.
+ */
+ if (!bgp_config_inprocess())
+ return;
+
+ THREAD_OFF(t_bgp_cfg);
+
+ /* Start a new timer to make sure we don't send EoR
+ * before route-maps are processed.
+ */
+ thread_add_timer(bm->master, bgp_config_finish, NULL,
+ bgp_post_config_delay, &t_bgp_cfg);
+}
+
void bgp_vty_init(void)
{
cmd_variable_handler_register(bgp_var_neighbor);
cmd_variable_handler_register(bgp_var_peergroup);
+ cmd_init_config_callbacks(bgp_config_start, bgp_config_end);
+
/* Install bgp top node. */
install_node(&bgp_node);
install_node(&bgp_ipv4_unicast_node);
extern void bgp_config_write_listen(struct vty *vty, struct bgp *bgp);
extern void bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp);
extern int bgp_vty_return(struct vty *vty, int ret);
+extern bool bgp_config_inprocess(void);
extern struct peer *peer_and_group_lookup_vty(struct vty *vty,
const char *peer_str);
peer->v_routeadv = (peer_sort(peer) == BGP_PEER_IBGP)
? BGP_DEFAULT_IBGP_ROUTEADV
: BGP_DEFAULT_EBGP_ROUTEADV;
+ if (bgp_config_inprocess())
+ peer->shut_during_cfg = true;
peer = peer_lock(peer); /* bgp peer list reference */
peer->group = group;
frr_pthread_stop_all();
}
+static int peer_unshut_after_cfg(struct bgp *bgp)
+{
+ struct listnode *node;
+ struct peer *peer;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ if (!peer->shut_during_cfg)
+ continue;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s: released from config-pending hold",
+ peer->host);
+
+ peer->shut_during_cfg = false;
+ if (peer_active(peer) && peer->status != Established) {
+ if (peer->status != Idle)
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ BGP_EVENT_ADD(peer, BGP_Start);
+ }
+ }
+
+ return 0;
+}
+
void bgp_init(unsigned short instance)
{
+ hook_register(bgp_config_end, peer_unshut_after_cfg);
/* allocates some vital data structures used by peer commands in
* vty_init */
DECLARE_HOOK(bgp_inst_config_write,
(struct bgp *bgp, struct vty *vty),
(bgp, vty));
+DECLARE_HOOK(bgp_config_end, (struct bgp *bgp), (bgp));
/* Thread callback information */
struct afi_safi_info {
/* Long-lived Graceful Restart */
struct llgr_info llgr[AFI_MAX][SAFI_MAX];
+ bool shut_during_cfg;
+
QOBJ_FIELDS;
};
DECLARE_QOBJ_TYPE(peer);
/* Check if suppress start/restart of sessions to peer. */
#define BGP_PEER_START_SUPPRESSED(P) \
- (CHECK_FLAG((P)->flags, PEER_FLAG_SHUTDOWN) \
- || CHECK_FLAG((P)->sflags, PEER_STATUS_PREFIX_OVERFLOW) \
- || CHECK_FLAG((P)->bgp->flags, BGP_FLAG_SHUTDOWN))
+ (CHECK_FLAG((P)->flags, PEER_FLAG_SHUTDOWN) || \
+ CHECK_FLAG((P)->sflags, PEER_STATUS_PREFIX_OVERFLOW) || \
+ CHECK_FLAG((P)->bgp->flags, BGP_FLAG_SHUTDOWN) || \
+ (P)->shut_during_cfg)
#define PEER_ROUTE_ADV_DELAY(peer) \
(CHECK_FLAG(peer->thread_flags, PEER_THREAD_SUBGRP_ADV_DELAY))