Attached is a backport of graceful restart to 1.6.8 (legacy branch). I am aware that this branch is no longer getting feature updates. However I would request this patch inclusion on the basis it's valuable for anyone looking to move from 1.x to 2.x.
Regards, Mathew
diff --git a/conf/conf.c b/conf/conf.c index 4e6354b2..e294fa6e 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -477,19 +477,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 6d8ed5ca..234fdaf2 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -56,6 +56,7 @@ struct config { struct config *fallback; /* Link to regular config for CLI parsing */ 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 */ bird_clock_t load_time; /* When we've got this configuration */ }; @@ -74,7 +75,7 @@ void config_init(void); void cf_error(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 66cb937d..d7c1cffe 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -557,6 +557,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 02fd8d96..72b1a0a3 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -246,6 +246,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 664a38ed..344b50e8 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -306,7 +306,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) { @@ -320,7 +320,14 @@ 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, PKT_SCHEDULE_CLOSE); + } + else + bgp_error(conn, 6, subcode, data, len); + return; default: bug("bgp_graceful_close_conn: Unknown state %d", conn->state); @@ -356,7 +363,7 @@ bgp_decision(void *vp) } 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); @@ -1277,7 +1284,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; @@ -1298,6 +1305,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; @@ -1307,6 +1315,15 @@ 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 dd66c86b..3925a64f 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -233,7 +233,7 @@ void bgp_graceful_restart_done(struct bgp_proto *p); void bgp_refresh_begin(struct bgp_proto *p); void bgp_refresh_end(struct bgp_proto *p); 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 dc5804ed..d044aeff 100644 --- a/sysdep/unix/config.Y +++ b/sysdep/unix/config.Y @@ -16,6 +16,7 @@ CF_DECLS CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT) CF_KEYWORDS(TIMEFORMAT, ISO, OLD, SHORT, LONG, BASE, NAME, CONFIRM, UNDO, CHECK, TIMEOUT) CF_KEYWORDS(DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING, TIMEOUT) +CF_KEYWORDS(GRACEFUL, RESTART) %type <i> log_mask log_mask_list log_cat cfg_timeout %type <g> log_file @@ -143,6 +144,9 @@ CF_CLI(CONFIGURE CHECK, cfg_name, [\"<file>\"], [[Parse configuration and check CF_CLI(DOWN,,, [[Shut the daemon down]]) { cmd_shutdown(); } ; +CF_CLI(GRACEFUL RESTART,,, [[Graceful restart]]) +{ cmd_graceful_restart(); } ; + cfg_name: /* empty */ { $$ = NULL; } | TEXT diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 526c0cab..eb1b1f85 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -1161,7 +1161,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 01c0dedb..10e962e7 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -551,14 +551,24 @@ 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 +cmd_graceful_restart(void) +{ + if (cli_access_restricted()) + return; + + cli_msg(25, "Graceful restart requested"); + order_shutdown(1); } void diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index c71f7940..e5f4c165 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -27,6 +27,7 @@ void cmd_reconfig(char *name, int type, int timeout); void cmd_reconfig_confirm(void); void cmd_reconfig_undo(void); void cmd_shutdown(void); +void cmd_graceful_restart(void); #define UNIX_DEFAULT_CONFIGURE_TIMEOUT 300