diff --git a/conf/conf.c b/conf/conf.c index d6b3e8e8..0b797dec 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -492,19 +492,24 @@ config_init(void) * for switching to an empty configuration. */ void -order_shutdown(void) +order_shutdown(int gr) { struct config *c; if (shutting_down) return; - log(L_INFO "Shutting down"); + if (!gr) + log(L_INFO "Shutting down"); + else + log(L_INFO "Shutting down for graceful restart"); + c = lp_alloc(config->mem, sizeof(struct config)); memcpy(c, config, sizeof(struct config)); init_list(&c->protos); init_list(&c->tables); c->shutdown = 1; + c->gr_down = gr; config_commit(c, RECONFIG_HARD, 0); shutting_down = 1; diff --git a/conf/conf.h b/conf/conf.h index 51dcb989..777a1fca 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -55,6 +55,7 @@ struct config { struct sym_scope *root_scope; /* Scope for root symbols */ int obstacle_count; /* Number of items blocking freeing of this config */ int shutdown; /* This is a pseudo-config for daemon shutdown */ + int gr_down; /* This is a pseudo-config for graceful restart */ btime load_time; /* When we've got this configuration */ }; @@ -75,7 +76,7 @@ void config_init(void); void cf_error(const char *msg, ...) NORET; void config_add_obstacle(struct config *); void config_del_obstacle(struct config *); -void order_shutdown(void); +void order_shutdown(int gr); #define RECONFIG_NONE 0 #define RECONFIG_HARD 1 diff --git a/doc/reply_codes b/doc/reply_codes index 3a7f2c90..02f4e656 100644 --- a/doc/reply_codes +++ b/doc/reply_codes @@ -33,6 +33,7 @@ Reply codes of BIRD command-line interface 0022 Undo scheduled 0023 Evaluation of expression 0024 Graceful restart status report +0025 Graceful restart ordered 1000 BIRD version 1001 Interface list diff --git a/nest/proto.c b/nest/proto.c index 415471c4..beb3f393 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -1048,6 +1048,11 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty p->down_code = PDC_CF_REMOVE; p->cf_new = NULL; } + else if (new->gr_down) + { + p->down_code = PDC_CMD_GR_DOWN; + p->cf_new = NULL; + } else /* global shutdown */ { p->down_code = PDC_CMD_SHUTDOWN; diff --git a/nest/protocol.h b/nest/protocol.h index b26d9cc1..53cccd5b 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -257,6 +257,7 @@ struct proto_spec { #define PDC_CMD_DISABLE 0x11 /* Result of disable command */ #define PDC_CMD_RESTART 0x12 /* Result of restart command */ #define PDC_CMD_SHUTDOWN 0x13 /* Result of global shutdown */ +#define PDC_CMD_GR_DOWN 0x14 /* Result of global graceful restart */ #define PDC_RX_LIMIT_HIT 0x21 /* Route receive limit reached */ #define PDC_IN_LIMIT_HIT 0x22 /* Route import limit reached */ #define PDC_OUT_LIMIT_HIT 0x23 /* Route export limit reached */ diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 470c6ff5..cfc31ed0 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -422,7 +422,7 @@ bgp_update_startup_delay(struct bgp_proto *p) } static void -bgp_graceful_close_conn(struct bgp_conn *conn, uint subcode, byte *data, uint len) +bgp_graceful_close_conn(struct bgp_conn *conn, int subcode, byte *data, uint len) { switch (conn->state) { @@ -438,7 +438,13 @@ bgp_graceful_close_conn(struct bgp_conn *conn, uint subcode, byte *data, uint le case BS_OPENSENT: case BS_OPENCONFIRM: case BS_ESTABLISHED: - bgp_error(conn, 6, subcode, data, len); + if (subcode < 0) + { + bgp_conn_enter_close_state(conn); + bgp_schedule_packet(conn, NULL, PKT_SCHEDULE_CLOSE); + } + else + bgp_error(conn, 6, subcode, data, len); return; default: @@ -501,7 +507,7 @@ bgp_spawn(struct bgp_proto *pp, ip_addr remote_ip) } void -bgp_stop(struct bgp_proto *p, uint subcode, byte *data, uint len) +bgp_stop(struct bgp_proto *p, int subcode, byte *data, uint len) { proto_notify_state(&p->p, PS_STOP); bgp_graceful_close_conn(&p->outgoing_conn, subcode, data, len); @@ -1555,7 +1561,7 @@ static int bgp_shutdown(struct proto *P) { struct bgp_proto *p = (struct bgp_proto *) P; - uint subcode = 0; + int subcode = 0; char *message = NULL; byte *data = NULL; @@ -1576,6 +1582,7 @@ bgp_shutdown(struct proto *P) case PDC_CMD_DISABLE: case PDC_CMD_SHUTDOWN: + shutdown: subcode = 2; // Errcode 6, 2 - administrative shutdown message = P->message; break; @@ -1585,6 +1592,14 @@ bgp_shutdown(struct proto *P) message = P->message; break; + case PDC_CMD_GR_DOWN: + if ((p->cf->gr_mode != BGP_GR_ABLE) && + (p->cf->llgr_mode != BGP_LLGR_ABLE)) + goto shutdown; + + subcode = -1; // Do not send NOTIFICATION, just close the connection + break; + case PDC_RX_LIMIT_HIT: case PDC_IN_LIMIT_HIT: subcode = 1; // Errcode 6, 1 - max number of prefixes reached diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index d8c9fe94..d7f7427f 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -488,7 +488,7 @@ void bgp_graceful_restart_done(struct bgp_channel *c); void bgp_refresh_begin(struct bgp_channel *c); void bgp_refresh_end(struct bgp_channel *c); void bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code); -void bgp_stop(struct bgp_proto *p, uint subcode, byte *data, uint len); +void bgp_stop(struct bgp_proto *p, int subcode, byte *data, uint len); struct rte_source *bgp_find_source(struct bgp_proto *p, u32 path_id); struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id); diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y index 2895a69e..b78e0e6c 100644 --- a/sysdep/unix/config.Y +++ b/sysdep/unix/config.Y @@ -133,6 +133,10 @@ CF_CLI(CONFIGURE CHECK, cfg_name, [\"\"], [[Parse configuration and check CF_CLI(DOWN,,, [[Shut the daemon down]]) { cmd_shutdown(); } ; +CF_CLI(GRACEFUL DOWN,,, [[Shut the daemon down for graceful restart]]) +{ cmd_graceful_restart(); } ; + + cfg_name: /* empty */ { $$ = NULL; } | TEXT diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 3aee3fe2..f0e78442 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -1129,7 +1129,7 @@ krt_shutdown(struct proto *P) krt_scan_timer_stop(p); /* FIXME we should flush routes even when persist during reconfiguration */ - if (p->initialized && !KRT_CF->persist) + if (p->initialized && !KRT_CF->persist && (P->down_code != PDC_CMD_GR_DOWN)) krt_flush_routes(p); p->ready = 0; diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index c381b44f..39465aa8 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -565,14 +565,14 @@ cmd_shutdown(void) return; cli_msg(7, "Shutdown requested"); - order_shutdown(); + order_shutdown(0); } void async_shutdown(void) { DBG("Shutting down...\n"); - order_shutdown(); + order_shutdown(0); } void @@ -584,6 +584,17 @@ sysdep_shutdown_done(void) exit(0); } +void +cmd_graceful_restart(void) +{ + if (cli_access_restricted()) + return; + + cli_msg(25, "Graceful restart requested"); + order_shutdown(1); +} + + /* * Signals */ diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index d5ce8ff9..bf0aedeb 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -28,6 +28,7 @@ void cmd_reconfig_confirm(void); void cmd_reconfig_undo(void); void cmd_reconfig_status(void); void cmd_shutdown(void); +void cmd_graceful_restart(void); #define UNIX_DEFAULT_CONFIGURE_TIMEOUT 300