diff --git a/NEWS b/NEWS index f8e76601..16e1f6d1 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,8 @@ -Version 1.3.9 (2012-11-16) +Version 1.3.9 (2013-01-11) o BIRD can be configured to keep and show filtered routes. + o Separate receive and import limits. + o Several new reconfiguration cmd options (undo, timeout, check). + o Configurable automatic router ID selection. o Dragonfly BSD support. o Fixed OSPFv3 vlinks. o Several minor bugfixes. diff --git a/README b/README index 5c2ef076..daeb18bd 100644 --- a/README +++ b/README @@ -3,7 +3,7 @@ (c) 1998--2008 Martin Mares (c) 1998--2000 Pavel Machek (c) 1998--2008 Ondrej Filip - (c) 2009--2011 CZ.NIC z.s.p.o. + (c) 2009--2013 CZ.NIC z.s.p.o. ================================================================================ diff --git a/bird.conf b/bird.conf index 2d10ef4b..bafd6ea1 100644 --- a/bird.conf +++ b/bird.conf @@ -25,14 +25,14 @@ protocol kernel { protocol static { # disabled; - route fec0:2::/64 reject; - route fec0:3::/64 reject; - route fec0:4::/64 reject; + route fec0:2::/64 blackhole; + route fec0:3::/64 unreachable; + route fec0:4::/64 prohibit; # route 0.0.0.0/0 via 195.113.31.113; -# route 62.168.0.0/25 reject; +# route 62.168.0.0/25 unreachable; # route 1.2.3.4/32 via 195.113.31.124; -# route 10.0.0.0/8 reject; +# route 10.0.0.0/8 unreachable; # route 10.1.1.0:255.255.255.0 via 62.168.0.3; # route 10.1.2.0:255.255.255.0 via 62.168.0.3; # route 10.1.3.0:255.255.255.0 via 62.168.0.4; diff --git a/conf/conf.c b/conf/conf.c index be490f05..956e9dcb 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -21,9 +21,12 @@ * There can exist up to four different configurations at one time: an active * one (pointed to by @config), configuration we are just switching from * (@old_config), one queued for the next reconfiguration (@future_config; - * if it's non-%NULL and the user wants to reconfigure once again, we just + * if there is one and the user wants to reconfigure once again, we just * free the previous queued config and replace it with the new one) and - * finally a config being parsed (@new_config). + * finally a config being parsed (@new_config). The stored @old_config + * is also used for undo reconfiguration, which works in a similar way. + * Reconfiguration could also have timeout (using @config_timer) and undo + * is automatically called if the new configuration is not confirmed later. * * Loading of new configuration is very simple: just call config_alloc() * to get a new &config structure, then use config_parse() to parse a @@ -55,10 +58,23 @@ static jmp_buf conf_jmpbuf; -struct config *config, *new_config, *old_config, *future_config; -static event *config_event; -int shutting_down, future_type; -bird_clock_t boot_time; +struct config *config, *new_config; + +static struct config *old_config; /* Old configuration */ +static struct config *future_config; /* New config held here if recon requested during recon */ +static int old_cftype; /* Type of transition old_config -> config (RECONFIG_SOFT/HARD) */ +static int future_cftype; /* Type of scheduled transition, may also be RECONFIG_UNDO */ +/* Note that when future_cftype is RECONFIG_UNDO, then future_config is NULL, + therefore proper check for future scheduled config checks future_cftype */ + +static event *config_event; /* Event for finalizing reconfiguration */ +static timer *config_timer; /* Timer for scheduled configuration rollback */ + +/* These are public just for cmd_show_status(), should not be accessed elsewhere */ +int shutting_down; /* Shutdown requested, do not accept new config changes */ +int configuring; /* Reconfiguration is running */ +int undo_available; /* Undo was not requested from last reconfiguration */ +/* Note that both shutting_down and undo_available are related to requests, not processing */ /** * config_alloc - allocate a new configuration @@ -82,8 +98,6 @@ config_alloc(byte *name) c->load_time = now; c->tf_base.fmt1 = c->tf_log.fmt1 = "%d-%m-%Y %T"; - if (!boot_time) - boot_time = now; return c; } @@ -155,7 +169,8 @@ cli_parse(struct config *c) void config_free(struct config *c) { - rfree(c->pool); + if (c) + rfree(c->pool); } void @@ -171,10 +186,7 @@ config_del_obstacle(struct config *c) DBG("+++ deleting obstacle %d\n", c->obstacle_count); c->obstacle_count--; if (!c->obstacle_count) - { - ASSERT(config_event); - ev_schedule(config_event); - } + ev_schedule(config_event); } static int @@ -189,25 +201,50 @@ global_commit(struct config *new, struct config *old) log(L_WARN "Reconfiguration of BGP listening socket not implemented, please restart BIRD."); if (!new->router_id) - new->router_id = old->router_id; - if (new->router_id != old->router_id) - return 1; + { + new->router_id = old->router_id; + + if (new->router_id_from) + { + u32 id = if_choose_router_id(new->router_id_from, old->router_id); + if (!id) + log(L_WARN "Cannot determine router ID, using old one"); + else + new->router_id = id; + } + } + return 0; } static int config_do_commit(struct config *c, int type) { - int force_restart, nobs; + if (type == RECONFIG_UNDO) + { + c = old_config; + type = old_cftype; + } + else + config_free(old_config); - DBG("do_commit\n"); old_config = config; - config = new_config = c; + old_cftype = type; + config = c; + + configuring = 1; + if (old_config && !config->shutdown) + log(L_INFO "Reconfiguring"); + + /* This should not be necessary, but it seems there are some + functions that access new_config instead of config */ + new_config = config; + if (old_config) old_config->obstacle_count++; DBG("sysdep_commit\n"); - force_restart = sysdep_commit(c, old_config); + int force_restart = sysdep_commit(c, old_config); DBG("global_commit\n"); force_restart |= global_commit(c, old_config); DBG("rt_commit\n"); @@ -215,38 +252,38 @@ config_do_commit(struct config *c, int type) roa_commit(c, old_config); DBG("protos_commit\n"); protos_commit(c, old_config, force_restart, type); - new_config = NULL; /* Just to be sure nobody uses that now */ + + /* Just to be sure nobody uses that now */ + new_config = NULL; + + int obs = 0; if (old_config) - nobs = --old_config->obstacle_count; - else - nobs = 0; - DBG("do_commit finished with %d obstacles remaining\n", nobs); - return !nobs; + obs = --old_config->obstacle_count; + + DBG("do_commit finished with %d obstacles remaining\n", obs); + return !obs; } static void config_done(void *unused UNUSED) { - struct config *c; + if (config->shutdown) + sysdep_shutdown_done(); - DBG("config_done\n"); - for(;;) + configuring = 0; + if (old_config) + log(L_INFO "Reconfigured"); + + if (future_cftype) { - if (config->shutdown) - sysdep_shutdown_done(); - log(L_INFO "Reconfigured"); - if (old_config) - { - config_free(old_config); - old_config = NULL; - } - if (!future_config) - break; - c = future_config; + int type = future_cftype; + struct config *conf = future_config; + future_cftype = RECONFIG_NONE; future_config = NULL; + log(L_INFO "Reconfiguring to queued configuration"); - if (!config_do_commit(c, future_type)) - break; + if (config_do_commit(conf, type)) + config_done(NULL); } } @@ -254,6 +291,7 @@ config_done(void *unused UNUSED) * config_commit - commit a configuration * @c: new configuration * @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD) + * @timeout: timeout for undo (or 0 for no timeout) * * When a configuration is parsed and prepared for use, the * config_commit() function starts the process of reconfiguration. @@ -266,6 +304,10 @@ config_done(void *unused UNUSED) * using config_del_obstacle(), the old configuration is freed and * everything runs according to the new one. * + * When @timeout is nonzero, the undo timer is activated with given + * timeout. The timer is deactivated when config_commit(), + * config_confirm() or config_undo() is called. + * * Result: %CONF_DONE if the configuration has been accepted immediately, * %CONF_PROGRESS if it will take some time to switch to it, %CONF_QUEUED * if it's been queued due to another reconfiguration being in progress now @@ -273,49 +315,147 @@ config_done(void *unused UNUSED) * are accepted. */ int -config_commit(struct config *c, int type) +config_commit(struct config *c, int type, int timeout) { - if (!config) /* First-time configuration */ + if (shutting_down) { - config_do_commit(c, RECONFIG_HARD); - return CONF_DONE; + config_free(c); + return CONF_SHUTDOWN; } - if (old_config) /* Reconfiguration already in progress */ + + undo_available = 1; + if (timeout > 0) + tm_start(config_timer, timeout); + else + tm_stop(config_timer); + + if (configuring) { - if (shutting_down == 2) - { - log(L_INFO "New configuration discarded due to shutdown"); - config_free(c); - return CONF_SHUTDOWN; - } - if (future_config) + if (future_cftype) { log(L_INFO "Queueing new configuration, ignoring the one already queued"); config_free(future_config); } else - log(L_INFO "Queued new configuration"); + log(L_INFO "Queueing new configuration"); + + future_cftype = type; future_config = c; - future_type = type; return CONF_QUEUED; } - if (!shutting_down) - log(L_INFO "Reconfiguring"); - if (config_do_commit(c, type)) { config_done(NULL); return CONF_DONE; } - if (!config_event) + return CONF_PROGRESS; +} + +/** + * config_confirm - confirm a commited configuration + * + * When the undo timer is activated by config_commit() with nonzero timeout, + * this function can be used to deactivate it and therefore confirm + * the current configuration. + * + * Result: %CONF_CONFIRM when the current configuration is confirmed, + * %CONF_NONE when there is nothing to confirm (i.e. undo timer is not active). + */ +int +config_confirm(void) +{ + if (config_timer->expires == 0) + return CONF_NOTHING; + + tm_stop(config_timer); + + return CONF_CONFIRM; +} + +/** + * config_undo - undo a configuration + * + * Function config_undo() can be used to change the current + * configuration back to stored %old_config. If no reconfiguration is + * running, this stored configuration is commited in the same way as a + * new configuration in config_commit(). If there is already a + * reconfiguration in progress and no next reconfiguration is + * scheduled, then the undo is scheduled for later processing as + * usual, but if another reconfiguration is already scheduled, then + * such reconfiguration is removed instead (i.e. undo is applied on + * the last commit that scheduled it). + * + * Result: %CONF_DONE if the configuration has been accepted immediately, + * %CONF_PROGRESS if it will take some time to switch to it, %CONF_QUEUED + * if it's been queued due to another reconfiguration being in progress now, + * %CONF_UNQUEUED if a scheduled reconfiguration is removed, %CONF_NOTHING + * if there is no relevant configuration to undo (the previous config request + * was config_undo() too) or %CONF_SHUTDOWN if BIRD is in shutdown mode and + * no new configuration changes are accepted. + */ +int +config_undo(void) +{ + if (shutting_down) + return CONF_SHUTDOWN; + + if (!undo_available || !old_config) + return CONF_NOTHING; + + undo_available = 0; + tm_stop(config_timer); + + if (configuring) { - config_event = ev_new(&root_pool); - config_event->hook = config_done; + if (future_cftype) + { + config_free(future_config); + future_config = NULL; + + log(L_INFO "Removing queued configuration"); + future_cftype = RECONFIG_NONE; + return CONF_UNQUEUED; + } + else + { + log(L_INFO "Queueing undo configuration"); + future_cftype = RECONFIG_UNDO; + return CONF_QUEUED; + } + } + + if (config_do_commit(NULL, RECONFIG_UNDO)) + { + config_done(NULL); + return CONF_DONE; } return CONF_PROGRESS; } +extern void cmd_reconfig_undo_notify(void); + +static void +config_timeout(struct timer *t) +{ + log(L_INFO "Config timeout expired, starting undo"); + cmd_reconfig_undo_notify(); + + int r = config_undo(); + if (r < 0) + log(L_ERR "Undo request failed"); +} + +void +config_init(void) +{ + config_event = ev_new(&root_pool); + config_event->hook = config_done; + + config_timer = tm_new(&root_pool); + config_timer->hook = config_timeout; +} + /** * order_shutdown - order BIRD shutdown * @@ -329,15 +469,16 @@ order_shutdown(void) if (shutting_down) return; + log(L_INFO "Shutting down"); 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; + + config_commit(c, RECONFIG_HARD, 0); shutting_down = 1; - config_commit(c, RECONFIG_HARD); - shutting_down = 2; } /** diff --git a/conf/conf.h b/conf/conf.h index c76832b6..683374e0 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -26,6 +26,7 @@ struct config { int mrtdump_file; /* Configured MRTDump file (sysdep, fd in unix) */ char *syslog_name; /* Name used for syslog (NULL -> no syslog) */ struct rtable_config *master_rtc; /* Configuration of master routing table */ + struct iface_patt *router_id_from; /* Configured list of router ID iface patterns */ u32 router_id; /* Our Router ID */ ip_addr listen_bgp_addr; /* Listening BGP socket should use this address */ @@ -54,28 +55,33 @@ struct config { /* Please don't use these variables in protocols. Use proto_config->global instead. */ extern struct config *config; /* Currently active configuration */ extern struct config *new_config; /* Configuration being parsed */ -extern struct config *old_config; /* Old configuration when reconfiguration is in progress */ -extern struct config *future_config; /* New config held here if recon requested during recon */ - -extern int shutting_down; -extern bird_clock_t boot_time; struct config *config_alloc(byte *name); int config_parse(struct config *); int cli_parse(struct config *); void config_free(struct config *); -int config_commit(struct config *, int type); -#define RECONFIG_HARD 0 -#define RECONFIG_SOFT 1 +int config_commit(struct config *, int type, int timeout); +int config_confirm(void); +int config_undo(void); +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); -#define CONF_DONE 0 -#define CONF_PROGRESS 1 -#define CONF_QUEUED 2 -#define CONF_SHUTDOWN 3 +#define RECONFIG_NONE 0 +#define RECONFIG_HARD 1 +#define RECONFIG_SOFT 2 +#define RECONFIG_UNDO 3 + +#define CONF_DONE 0 +#define CONF_PROGRESS 1 +#define CONF_QUEUED 2 +#define CONF_UNQUEUED 3 +#define CONF_CONFIRM 4 +#define CONF_SHUTDOWN -1 +#define CONF_NOTHING -2 + /* Pools */ diff --git a/conf/gen_commands.m4 b/conf/gen_commands.m4 index a88ba014..3ed21f13 100644 --- a/conf/gen_commands.m4 +++ b/conf/gen_commands.m4 @@ -10,6 +10,9 @@ m4_divert(-1)m4_dnl m4_define(CF_CLI, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$3", "$4", 1 }, m4_divert(-1)') +m4_define(CF_CLI_CMD, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 1 }, +m4_divert(-1)') + m4_define(CF_CLI_HELP, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 0 }, m4_divert(-1)') diff --git a/conf/gen_parser.m4 b/conf/gen_parser.m4 index 74385f32..00b55023 100644 --- a/conf/gen_parser.m4 +++ b/conf/gen_parser.m4 @@ -44,6 +44,7 @@ m4_define(CF_CLI, `m4_define([[CF_cmd]], cmd_[[]]m4_translit($1, [[ ]], _))DNL m4_divert(2)CF_KEYWORDS(m4_translit($1, [[ ]], [[,]])) m4_divert(3)CF_ADDTO(cli_cmd, CF_cmd) CF_cmd: $1 $2 END') +m4_define(CF_CLI_CMD, `') m4_define(CF_CLI_HELP, `') # ENUM declarations are ignored diff --git a/doc/bird.conf.example b/doc/bird.conf.example index 5e07ab5a..dcc62e29 100644 --- a/doc/bird.conf.example +++ b/doc/bird.conf.example @@ -67,8 +67,8 @@ protocol static { # debug { states, routes, filters, interfaces, events, packets }; # debug all; # route 0.0.0.0/0 via 198.51.100.13; -# route 198.51.100.0/25 reject; -# route 10.0.0.0/8 reject; +# route 198.51.100.0/25 unreachable; +# route 10.0.0.0/8 unreachable; # route 10.1.1.0:255.255.255.0 via 198.51.100.3; # route 10.1.2.0:255.255.255.0 via 198.51.100.3; # route 10.1.3.0:255.255.255.0 via 198.51.100.4; diff --git a/doc/bird.sgml b/doc/bird.sgml index d833f82f..b0d4e6a1 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -337,7 +337,18 @@ protocol rip { Besides, there are some predefined numeric constants based on /etc/iproute2/rt_* files. A list of defined constants can be seen (together with other symbols) using 'show symbols' command. - router id Set BIRD's router ID. It's a world-wide unique identification of your router, usually one of router's IPv4 addresses. Default: in IPv4 version, the lowest IP address of a non-loopback interface. In IPv6 version, this option is mandatory. + router id + Set BIRD's router ID. It's a world-wide unique identification + of your router, usually one of router's IPv4 addresses. + Default: in IPv4 version, the lowest IP address of a + non-loopback interface. In IPv6 version, this option is + mandatory. + + router id from [-] [ " + Set BIRD's router ID based on an IP address of an interface + specified by an interface pattern. The option is applicable + for IPv4 version only. See + section for detailed description of interface patterns. listen bgp [address This option allows to specify address and port where BGP @@ -471,15 +482,23 @@ to zero to disable it. An empty is equivalent to receive limit + Specify an receive route limit (a maximum number of routes + received from the protocol and remembered). It works almost + identically to import limit option, the only + difference is that if export limit Specify an export route limit, works similarly to @@ -702,19 +721,48 @@ This argument can be omitted if there exists only a single instance. flush roa [table ] Remove all dynamic ROA entries from a ROA table. - configure [soft] [" + configure [soft] [" Reload configuration from a given file. BIRD will smoothly switch itself to the new configuration, protocols are reconfigured if possible, restarted otherwise. Changes in - filters usually lead to restart of affected protocols. If - configure confirm + Deactivate the config undo timer and therefore confirm the current + configuration. + + configure undo + Undo the last configuration change and smoothly switch back to + the previous (stored) configuration. If the last configuration + change was soft, the undo change is also soft. There is only + one level of undo, but in some specific cases when several + reconfiguration requests are given immediately in a row and + the intermediate ones are skipped then the undo also skips them back. + + configure check [" + Read and parse given config file, but do not use it. useful + for checking syntactic and some semantic validity of an config + file. + enable|disable|restart - Enable, disable or restart a given protocol instance, instances matching the or or + reload [in|out] @@ -2733,9 +2781,10 @@ definition of the protocol contains mainly a list of static routes: route through an interface to hosts on a directly connected network. route Static recursive route, its nexthop depends on a route table lookup for given IP address. - route Special routes - specifying to drop the packet, return it as unreachable or return - it as administratively prohibited. + route Special routes + specifying to silently drop the packet, return it as unreachable or return + it as administratively prohibited. First two targets are also known + as check link If set, hardware link states of network interfaces are taken @@ -2761,7 +2810,7 @@ protocol static { via 198.51.100.10 weight 2 via 198.51.100.20 via 192.0.2.1; - route 203.0.113.0/24 reject; # Sink route + route 203.0.113.0/24 unreachable; # Sink route route 10.2.0.0/24 via "arc0"; # Secondary network } diff --git a/doc/reply_codes b/doc/reply_codes index 7ec2e27d..58807241 100644 --- a/doc/reply_codes +++ b/doc/reply_codes @@ -25,6 +25,12 @@ Reply codes of BIRD command-line interface 0014 Route count 0015 Reloading 0016 Access restricted +0017 Reconfiguration already in progress, removing queued config +0018 Reconfiguration confirmed +0019 Nothing to do (configure undo/confirm) +0020 Configuration OK +0021 Undo requested +0022 Undo scheduled 1000 BIRD version 1001 Interface list diff --git a/filter/filter.c b/filter/filter.c index a06fe5bf..4ce1dd50 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -622,9 +622,6 @@ interpret(struct f_inst *what) case T_VOID: runtime( "Can't operate with values of type void" ); case T_INT: if (v2.val.i == 0) runtime( "Mother told me not to divide by 0" ); res.val.i = v1.val.i / v2.val.i; break; - case T_IP: if (v2.type != T_INT) - runtime( "Incompatible types in / operator" ); - break; default: runtime( "Usage of unknown type" ); } break; @@ -956,11 +953,25 @@ interpret(struct f_inst *what) l->attrs[0].type = what->aux | EAF_ORIGINATED; switch (what->aux & EAF_TYPE_MASK) { case EAF_TYPE_INT: - case EAF_TYPE_ROUTER_ID: if (v1.type != T_INT) runtime( "Setting int attribute to non-int value" ); l->attrs[0].u.data = v1.val.i; break; + + case EAF_TYPE_ROUTER_ID: +#ifndef IPV6 + /* IP->Quad implicit conversion */ + if (v1.type == T_IP) { + l->attrs[0].u.data = ipa_to_u32(v1.val.px.ip); + break; + } +#endif + /* T_INT for backward compatibility */ + if ((v1.type != T_QUAD) && (v1.type != T_INT)) + runtime( "Setting quad attribute to non-quad value" ); + l->attrs[0].u.data = v1.val.i; + break; + case EAF_TYPE_OPAQUE: runtime( "Setting opaque attribute is not allowed" ); break; diff --git a/nest/cli.c b/nest/cli.c index d245790b..11f98794 100644 --- a/nest/cli.c +++ b/nest/cli.c @@ -122,6 +122,7 @@ cli_printf(cli *c, int code, char *msg, ...) va_list args; byte buf[CLI_LINE_SIZE]; int cd = code; + int errcode; int size, cnt; if (cd < 0) @@ -131,16 +132,26 @@ cli_printf(cli *c, int code, char *msg, ...) size = bsprintf(buf, " "); else size = bsprintf(buf, "%04d-", cd); + errcode = -8000; + } + else if (cd == CLI_ASYNC_CODE) + { + size = 1; buf[0] = '+'; + errcode = cd; } else - size = bsprintf(buf, "%04d ", cd); + { + size = bsprintf(buf, "%04d ", cd); + errcode = 8000; + } + c->last_reply = cd; va_start(args, msg); cnt = bvsnprintf(buf+size, sizeof(buf)-size-1, msg, args); va_end(args); if (cnt < 0) { - cli_printf(c, code < 0 ? -8000 : 8000, ""); + cli_printf(c, errcode, ""); return; } size += cnt; @@ -385,12 +396,17 @@ cli_echo(unsigned int class, byte *msg) } } +/* Hack for scheduled undo notification */ +extern cli *cmd_reconfig_stored_cli; + void cli_free(cli *c) { cli_set_log_echo(c, 0, 0); if (c->cleanup) c->cleanup(c); + if (c == cmd_reconfig_stored_cli) + cmd_reconfig_stored_cli = NULL; rfree(c->pool); } diff --git a/nest/cli.h b/nest/cli.h index ea64680a..396656e8 100644 --- a/nest/cli.h +++ b/nest/cli.h @@ -49,6 +49,8 @@ typedef struct cli { extern pool *cli_pool; extern struct cli *this_cli; /* Used during parsing */ +#define CLI_ASYNC_CODE 10000 + /* Functions to be called by command handlers */ void cli_printf(cli *, int, char *, ...); diff --git a/nest/cmds.c b/nest/cmds.c index 2a803930..54ace169 100644 --- a/nest/cmds.c +++ b/nest/cmds.c @@ -14,6 +14,9 @@ #include "lib/string.h" #include "lib/resource.h" +extern int shutting_down; +extern int configuring; + void cmd_show_status(void) { @@ -27,9 +30,10 @@ cmd_show_status(void) cli_msg(-1011, "Last reboot on %s", tim); tm_format_datetime(tim, &config->tf_base, config->load_time); cli_msg(-1011, "Last reconfiguration on %s", tim); + if (shutting_down) cli_msg(13, "Shutdown in progress"); - else if (old_config) + else if (configuring) cli_msg(13, "Reconfiguration in progress"); else cli_msg(13, "Daemon is up and running"); diff --git a/nest/config.Y b/nest/config.Y index c6bb63cc..f4052ae6 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -45,7 +45,7 @@ CF_DECLS CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT) CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS) CF_KEYWORDS(IPV4, IPVX, VPN4, VPN6, MPLS) -CF_KEYWORDS(LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED) +CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED) CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, AS, MAX, FLUSH) CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED) @@ -76,9 +76,9 @@ CF_GRAMMAR CF_ADDTO(conf, rtrid) -rtrid: ROUTER ID idval ';' { - new_config->router_id = $3; - } +rtrid: + ROUTER ID idval ';' { new_config->router_id = $3; } + | ROUTER ID FROM iface_patt ';' { new_config->router_id_from = this_ipatt; } ; idval: @@ -188,6 +188,7 @@ proto_item: | MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; } | IMPORT imexport { this_proto->in_filter = $2; } | EXPORT imexport { this_proto->out_filter = $2; } + | RECEIVE LIMIT limit_spec { this_proto->rx_limit = $3; } | IMPORT LIMIT limit_spec { this_proto->in_limit = $3; } | EXPORT LIMIT limit_spec { this_proto->out_limit = $3; } | IMPORT KEEP FILTERED bool { this_proto->in_keep_filtered = $4; } @@ -271,6 +272,17 @@ iface_patt_list: | iface_patt_list ',' iface_patt_node ; +iface_patt_init: { + /* Generic this_ipatt init */ + this_ipatt = cfg_allocz(sizeof(struct iface_patt)); + init_list(&this_ipatt->ipn_list); + } + ; + +iface_patt: + iface_patt_init iface_patt_list + ; + /* Direct device route protocol */ diff --git a/nest/iface.c b/nest/iface.c index 7f756e5c..1664670e 100644 --- a/nest/iface.c +++ b/nest/iface.c @@ -35,8 +35,6 @@ static pool *if_pool; -static void auto_router_id(void); - list iface_list; /** @@ -354,9 +352,6 @@ if_end_update(void) struct iface *i; struct ifa *a, *b; - if (!config->router_id) - auto_router_id(); - WALK_LIST(i, iface_list) { if (!(i->flags & IF_UPDATED)) @@ -581,27 +576,59 @@ ifa_delete(struct ifa *a) } } - -static void -auto_router_id(void) +u32 +if_choose_router_id(struct iface_patt *mask, u32 old_id) { // XXXX check this #if 0 - struct iface *i, *j; + struct iface *i; + struct ifa *a, *b; - j = NULL; + b = NULL; WALK_LIST(i, iface_list) - if ((i->flags & IF_ADMIN_UP) && - !(i->flags & (IF_IGNORE | IF_SHUTDOWN)) && - i->addr && - !(i->addr->flags & IA_PEER) && - (!j || ipa_to_u32(i->addr->ip) < ipa_to_u32(j->addr->ip))) - j = i; - if (!j) - die("Cannot determine router ID (no suitable network interface found), please configure it manually"); - log(L_INFO "Guessed router ID %I according to interface %s", j->addr->ip, j->name); - config->router_id = ipa_to_u32(j->addr->ip); + { + if (!(i->flags & IF_ADMIN_UP) || + (i->flags & (IF_IGNORE | IF_SHUTDOWN))) + continue; + + WALK_LIST(a, i->addrs) + { + if (a->flags & IA_SECONDARY) + continue; + + if (a->scope <= SCOPE_LINK) + continue; + + /* FIXME: This should go away */ + if (a->flags & IA_PEER) + continue; + + /* FIXME: This should go away too */ + if (!mask && (a != i->addr)) + continue; + + /* Check pattern if specified */ + if (mask && !iface_patt_match(mask, i, a)) + continue; + + /* No pattern or pattern matched */ + if (!b || ipa_to_u32(a->ip) < ipa_to_u32(b->ip)) + b = a; + } + } + + if (!b) + return 0; + + u32 id = ipa_to_u32(b->ip); + if (id != old_id) + log(L_INFO "Chosen router ID %R according to interface %s", id, b->iface->name); + + return id; + +#else + return 0; #endif } diff --git a/nest/iface.h b/nest/iface.h index 2416f82f..697ea543 100644 --- a/nest/iface.h +++ b/nest/iface.h @@ -101,6 +101,7 @@ struct iface *if_find_by_name(char *); struct iface *if_get_by_name(char *); void ifa_recalc_all_primary_addresses(void); + /* The Neighbor Cache */ typedef struct neighbor { @@ -161,4 +162,7 @@ int iface_patt_match(struct iface_patt *ifp, struct iface *i, struct ifa *a); struct iface_patt *iface_patt_find(list *l, struct iface *i, struct ifa *a); int iface_patts_equal(list *, list *, int (*)(struct iface_patt *, struct iface_patt *)); + +u32 if_choose_router_id(struct iface_patt *mask, u32 old_id); + #endif diff --git a/nest/proto.c b/nest/proto.c index bf238a02..8566d297 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -344,6 +344,7 @@ protos_postconfig(struct config *c) WALK_LIST(x, c->protos) { DBG(" %s", x->name); + p = x->protocol; if (p->postconfig) p->postconfig(x); @@ -382,11 +383,9 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config /* If there is a too big change in core attributes, ... */ if ((nc->protocol != oc->protocol) || (nc->disabled != p->disabled) || - (nc->table->table != oc->table->table) || - (proto_get_router_id(nc) != proto_get_router_id(oc))) + (nc->table->table != oc->table->table)) return 0; - p->debug = nc->debug; p->mrtdump = nc->mrtdump; proto_reconfig_type = type; @@ -412,6 +411,7 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config { p->main_ahook->in_filter = nc->in_filter; p->main_ahook->out_filter = nc->out_filter; + p->main_ahook->rx_limit = nc->rx_limit; p->main_ahook->in_limit = nc->in_limit; p->main_ahook->out_limit = nc->out_limit; p->main_ahook->in_keep_filtered = nc->in_keep_filtered; @@ -516,7 +516,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART; p->cf_new = nc; } - else if (!shutting_down) + else if (!new->shutdown) { log(L_INFO "Removing protocol %s", p->name); p->down_code = PDC_CF_REMOVE; @@ -537,7 +537,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty WALK_LIST(nc, new->protos) if (!nc->proto) { - if (old_config) /* Not a first-time configuration */ + if (old) /* Not a first-time configuration */ log(L_INFO "Adding protocol %s", nc->name); proto_init(nc); } @@ -552,6 +552,16 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty initial_device_proto = NULL; } + /* Determine router ID for the first time - it has to be here and not in + global_commit() because it is postponed after start of device protocol */ + if (!config->router_id) + { + config->router_id = if_choose_router_id(config->router_id_from, 0); + if (!config->router_id) + die("Cannot determine router ID, please configure it manually"); + } + + /* Start all other protocols */ WALK_LIST_DELSAFE(p, n, initial_proto_list) proto_rethink_goal(p); } @@ -797,9 +807,11 @@ proto_schedule_feed(struct proto *p, int initial) p->main_ahook = proto_add_announce_hook(p, p->table, &p->stats); p->main_ahook->in_filter = p->cf->in_filter; p->main_ahook->out_filter = p->cf->out_filter; + p->main_ahook->rx_limit = p->cf->rx_limit; p->main_ahook->in_limit = p->cf->in_limit; p->main_ahook->out_limit = p->cf->out_limit; p->main_ahook->in_keep_filtered = p->cf->in_keep_filtered; + proto_reset_limit(p->main_ahook->rx_limit); proto_reset_limit(p->main_ahook->in_limit); proto_reset_limit(p->main_ahook->out_limit); } @@ -971,6 +983,7 @@ proto_limit_name(struct proto_limit *l) * proto_notify_limit: notify about limit hit and take appropriate action * @ah: announce hook * @l: limit being hit + * @dir: limit direction (PLD_*) * @rt_count: the number of routes * * The function is called by the route processing core when limit @l @@ -978,10 +991,11 @@ proto_limit_name(struct proto_limit *l) * according to @l->action. */ void -proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count) +proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32 rt_count) { + const char *dir_name[PLD_MAX] = { "receive", "import" , "export" }; + const byte dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT }; struct proto *p = ah->proto; - int dir = (ah->in_limit == l); if (l->state == PLS_BLOCKED) return; @@ -989,7 +1003,7 @@ proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count /* For warning action, we want the log message every time we hit the limit */ if (!l->state || ((l->action == PLA_WARN) && (rt_count == l->limit))) log(L_WARN "Protocol %s hits route %s limit (%d), action: %s", - p->name, dir ? "import" : "export", l->limit, proto_limit_name(l)); + p->name, dir_name[dir], l->limit, proto_limit_name(l)); switch (l->action) { @@ -1004,8 +1018,7 @@ proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count case PLA_RESTART: case PLA_DISABLE: l->state = PLS_BLOCKED; - proto_schedule_down(p, l->action == PLA_RESTART, - dir ? PDC_IN_LIMIT_HIT : PDC_OUT_LIMIT_HIT); + proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]); break; } } @@ -1139,6 +1152,7 @@ proto_show_basic_info(struct proto *p) cli_msg(-1006, " Input filter: %s", filter_name(p->cf->in_filter)); cli_msg(-1006, " Output filter: %s", filter_name(p->cf->out_filter)); + proto_show_limit(p->cf->rx_limit, "Receive limit:"); proto_show_limit(p->cf->in_limit, "Import limit:"); proto_show_limit(p->cf->out_limit, "Export limit:"); @@ -1260,7 +1274,10 @@ proto_cmd_reload(struct proto *p, unsigned int dir, int cnt UNUSED) * Perhaps, but these hooks work asynchronously. */ if (!p->proto->multitable) - proto_reset_limit(p->main_ahook->in_limit); + { + proto_reset_limit(p->main_ahook->rx_limit); + proto_reset_limit(p->main_ahook->in_limit); + } } /* re-exporting routes */ diff --git a/nest/protocol.h b/nest/protocol.h index 72ed277f..7bacdbb3 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -96,6 +96,8 @@ struct proto_config { u32 router_id; /* Protocol specific router ID */ struct rtable_config *table; /* Table we're attached to */ struct filter *in_filter, *out_filter; /* Attached filters */ + struct proto_limit *rx_limit; /* Limit for receiving routes from protocol + (relevant when in_keep_filtered is active) */ struct proto_limit *in_limit; /* Limit for importing routes from protocol */ struct proto_limit *out_limit; /* Limit for exporting routes to protocol */ @@ -226,8 +228,9 @@ 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_IN_LIMIT_HIT 0x21 /* Route import limit reached */ -#define PDC_OUT_LIMIT_HIT 0x22 /* Route export limit reached */ +#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 */ void *proto_new(struct proto_config *, unsigned size); @@ -374,6 +377,11 @@ extern struct proto_config *cf_dev_proto; * Protocol limits */ +#define PLD_RX 0 /* Receive limit */ +#define PLD_IN 1 /* Import limit */ +#define PLD_OUT 2 /* Export limit */ +#define PLD_MAX 3 + #define PLA_WARN 1 /* Issue log warning */ #define PLA_BLOCK 2 /* Block new routes */ #define PLA_RESTART 4 /* Force protocol restart */ @@ -389,7 +397,7 @@ struct proto_limit { byte state; /* State of limit (PLS_*) */ }; -void proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count); +void proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32 rt_count); static inline void proto_reset_limit(struct proto_limit *l) @@ -409,6 +417,7 @@ struct announce_hook { struct proto *proto; struct filter *in_filter; /* Input filter */ struct filter *out_filter; /* Output filter */ + struct proto_limit *rx_limit; /* Receive limit (for in_keep_filtered) */ struct proto_limit *in_limit; /* Input limit */ struct proto_limit *out_limit; /* Output limit */ struct proto_stats *stats; /* Per-table protocol statistics */ diff --git a/nest/rt-table.c b/nest/rt-table.c index e3f23647..9677116b 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -288,7 +288,7 @@ do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tm if (l && new) { if ((!old || refeed) && (stats->exp_routes >= l->limit)) - proto_notify_limit(ah, l, stats->exp_routes); + proto_notify_limit(ah, l, PLD_OUT, stats->exp_routes); if (l->state == PLS_BLOCKED) { @@ -708,16 +708,22 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str return; } - struct proto_limit *l = ah->in_limit; + int new_ok = rte_is_ok(new); + int old_ok = rte_is_ok(old); + + struct proto_limit *l = ah->rx_limit; if (l && !old && new) { u32 all_routes = stats->imp_routes + stats->filt_routes; if (all_routes >= l->limit) - proto_notify_limit(ah, l, all_routes); + proto_notify_limit(ah, l, PLD_RX, all_routes); if (l->state == PLS_BLOCKED) { + /* In receive limit the situation is simple, old is NULL so + we just free new and exit like nothing happened */ + stats->imp_updates_ignored++; rte_trace_in(D_FILTERS, p, new, "ignored [limit]"); rte_free_quick(new); @@ -725,8 +731,39 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str } } - int new_ok = rte_is_ok(new); - int old_ok = rte_is_ok(old); + l = ah->in_limit; + if (l && !old_ok && new_ok) + { + if (stats->imp_routes >= l->limit) + proto_notify_limit(ah, l, PLD_IN, stats->imp_routes); + + if (l->state == PLS_BLOCKED) + { + /* In import limit the situation is more complicated. We + shouldn't just drop the route, we should handle it like + it was filtered. We also have to continue the route + processing if old or new is non-NULL, but we should exit + if both are NULL as this case is probably assumed to be + already handled. */ + + stats->imp_updates_ignored++; + rte_trace_in(D_FILTERS, p, new, "ignored [limit]"); + + if (ah->in_keep_filtered) + new->flags |= REF_FILTERED; + else + { rte_free_quick(new); new = NULL; } + + /* Note that old && !new could be possible when + ah->in_keep_filtered changed in the recent past. */ + + if (!old && !new) + return; + + new_ok = 0; + goto skip_stats1; + } + } if (new_ok) stats->imp_updates_accepted++; @@ -735,6 +772,8 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str else stats->imp_withdraws_ignored++; + skip_stats1: + if (new) rte_is_filtered(new) ? stats->filt_routes++ : stats->imp_routes++; if (old) diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 346c641b..0f351b44 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -878,6 +878,7 @@ bgp_shutdown(struct proto *P) subcode = 4; // Errcode 6, 4 - administrative reset break; + case PDC_RX_LIMIT_HIT: case PDC_IN_LIMIT_HIT: subcode = 1; // Errcode 6, 1 - max number of prefixes reached /* log message for compatibility */ @@ -1009,6 +1010,9 @@ bgp_reconfigure(struct proto *P, struct proto_config *C) struct bgp_proto *p = (struct bgp_proto *) P; struct bgp_config *old = p->cf; + if (proto_get_router_id(C) != p->local_id) + return 0; + int same = !memcmp(((byte *) old) + sizeof(struct proto_config), ((byte *) new) + sizeof(struct proto_config), // password item is last and must be checked separately diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index 0b5de651..bfd7777d 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -717,6 +717,9 @@ ospf_reconfigure(struct proto *p, struct proto_config *c) struct ospf_iface *ifa, *ifx; struct ospf_iface_patt *ip; + if (proto_get_router_id(c) != po->router_id) + return 0; + if (po->rfc1583 != new->rfc1583) return 0; diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c index 6099d284..51be3c7d 100644 --- a/proto/pipe/pipe.c +++ b/proto/pipe/pipe.c @@ -200,6 +200,11 @@ pipe_postconfig(struct proto_config *C) cf_error("Name of peer routing table not specified"); if (c->peer == C->table) cf_error("Primary table and peer table must be different"); + + if (C->in_keep_filtered) + cf_error("Pipe protocol prohibits keeping filtered routes"); + if (C->rx_limit) + cf_error("Pipe protocol does not support receive limits"); } extern int proto_reconfig_type; diff --git a/proto/static/config.Y b/proto/static/config.Y index f8e84f92..2d9d4b42 100644 --- a/proto/static/config.Y +++ b/proto/static/config.Y @@ -18,7 +18,7 @@ static struct static_route *this_srt, *this_srt_nh, *last_srt_nh; CF_DECLS CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK) -CF_KEYWORDS(MULTIPATH, WEIGHT, RECURSIVE, IGP, TABLE) +CF_KEYWORDS(MULTIPATH, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE) CF_GRAMMAR @@ -86,9 +86,12 @@ stat_route: this_srt->dest = RTDX_RECURSIVE; this_srt->via = $3; } - | stat_route0 DROP { this_srt->dest = RTD_BLACKHOLE; } - | stat_route0 REJECT { this_srt->dest = RTD_UNREACHABLE; } - | stat_route0 PROHIBIT { this_srt->dest = RTD_PROHIBIT; } + + | stat_route0 DROP { this_srt->dest = RTD_BLACKHOLE; } + | stat_route0 REJECT { this_srt->dest = RTD_UNREACHABLE; } + | stat_route0 BLACKHOLE { this_srt->dest = RTD_BLACKHOLE; } + | stat_route0 UNREACHABLE { this_srt->dest = RTD_UNREACHABLE; } + | stat_route0 PROHIBIT { this_srt->dest = RTD_PROHIBIT; } ; CF_CLI(SHOW STATIC, optsym, [], [[Show details of static protocol]]) diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y index 844f53df..7bade918 100644 --- a/sysdep/unix/config.Y +++ b/sysdep/unix/config.Y @@ -14,9 +14,9 @@ CF_HDR CF_DECLS CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT) -CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, BASE, NAME) +CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, BASE, NAME, CONFIRM, UNDO, CHECK, TIMEOUT) -%type log_mask log_mask_list log_cat +%type log_mask log_mask_list log_cat cfg_timeout %type log_file %type cfg_name %type timeformat_which @@ -104,13 +104,26 @@ timeformat_base: /* Unix specific commands */ -CF_CLI_HELP(CONFIGURE, [soft] [\"\"], [[Reload configuration]]) +CF_CLI_HELP(CONFIGURE, ..., [[Reload configuration]]) -CF_CLI(CONFIGURE, cfg_name, [\"\"], [[Reload configuration]]) -{ cmd_reconfig($2, RECONFIG_HARD); } ; +CF_CLI(CONFIGURE, cfg_name cfg_timeout, [\"\"] [timeout []], [[Reload configuration]]) +{ cmd_reconfig($2, RECONFIG_HARD, $3); } ; -CF_CLI(CONFIGURE SOFT, cfg_name, [\"\"], [[Reload configuration and ignore changes in filters]]) -{ cmd_reconfig($3, RECONFIG_SOFT); } ; +CF_CLI(CONFIGURE SOFT, cfg_name cfg_timeout, [\"\"] [timeout []], [[Reload configuration and ignore changes in filters]]) +{ cmd_reconfig($3, RECONFIG_SOFT, $4); } ; + +/* Hack to get input completion for 'timeout' */ +CF_CLI_CMD(CONFIGURE TIMEOUT, [], [[Reload configuration with undo timeout]]) +CF_CLI_CMD(CONFIGURE SOFT TIMEOUT, [], [[Reload configuration with undo timeout]]) + +CF_CLI(CONFIGURE CONFIRM,,, [[Confirm last configuration change - deactivate undo timeout]]) +{ cmd_reconfig_confirm(); } ; + +CF_CLI(CONFIGURE UNDO,,, [[Undo last configuration change]]) +{ cmd_reconfig_undo(); } ; + +CF_CLI(CONFIGURE CHECK, cfg_name, [\"\"], [[Parse configuration and check its validity]]) +{ cmd_check_config($3); } ; CF_CLI(DOWN,,, [[Shut the daemon down]]) { cmd_shutdown(); } ; @@ -120,6 +133,12 @@ cfg_name: | TEXT ; +cfg_timeout: + /* empty */ { $$ = 0; } + | TIMEOUT { $$ = UNIX_DEFAULT_CONFIGURE_TIMEOUT; } + | TIMEOUT expr { $$ = $2; } + ; + CF_CODE CF_END diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 0f5e1d39..eb103f1b 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -121,7 +121,7 @@ static list near_timers, far_timers; static bird_clock_t first_far_timer = TIME_INFINITY; /* now must be different from 0, because 0 is a special value in timer->expires */ -bird_clock_t now = 1, now_real; +bird_clock_t now = 1, now_real, boot_time; static void update_times_plain(void) @@ -1553,6 +1553,7 @@ io_init(void) krt_io_init(); init_times(); update_times(); + boot_time = now; srandom((int) now_real); } @@ -1580,7 +1581,7 @@ io_loop(void) tm_shot(); continue; } - timo.tv_sec = events ? 0 : tout - now; + timo.tv_sec = events ? 0 : MIN(tout - now, 3); timo.tv_usec = 0; if (sock_recalc_fdsets_p) diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index afc97082..efcb87ea 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -901,7 +901,7 @@ krt_notify(struct proto *P, struct rtable *table UNUSED, net *net, { struct krt_proto *p = (struct krt_proto *) P; - if (shutting_down) + if (config->shutdown) return; if (!(net->n.flags & KRF_INSTALLED)) old = NULL; diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index f0344a8f..23040e54 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -210,7 +210,7 @@ read_config(void) else die("Unable to open configuration file %s: %m", config_name); } - config_commit(conf, RECONFIG_HARD); + config_commit(conf, RECONFIG_HARD, 0); } void @@ -228,19 +228,17 @@ async_config(void) config_free(conf); } else - config_commit(conf, RECONFIG_HARD); + config_commit(conf, RECONFIG_HARD, 0); } -void -cmd_reconfig(char *name, int type) +static struct config * +cmd_read_config(char *name) { struct config *conf; - if (cli_access_restricted()) - return; - if (!name) name = config_name; + cli_msg(-2, "Reading configuration from %s", name); if (!unix_read_config(&conf, name)) { @@ -249,26 +247,96 @@ cmd_reconfig(char *name, int type) else cli_msg(8002, "%s: %m", name); config_free(conf); + conf = NULL; } - else + + return conf; +} + +void +cmd_check_config(char *name) +{ + struct config *conf = cmd_read_config(name); + if (!conf) + return; + + cli_msg(20, "Configuration OK"); + config_free(conf); +} + +static void +cmd_reconfig_msg(int r) +{ + switch (r) { - switch (config_commit(conf, type)) - { - case CONF_DONE: - cli_msg(3, "Reconfigured."); - break; - case CONF_PROGRESS: - cli_msg(4, "Reconfiguration in progress."); - break; - case CONF_SHUTDOWN: - cli_msg(6, "Reconfiguration ignored, shutting down."); - break; - default: - cli_msg(5, "Reconfiguration already in progress, queueing new config"); - } + case CONF_DONE: cli_msg( 3, "Reconfigured"); break; + case CONF_PROGRESS: cli_msg( 4, "Reconfiguration in progress"); break; + case CONF_QUEUED: cli_msg( 5, "Reconfiguration already in progress, queueing new config"); break; + case CONF_UNQUEUED: cli_msg(17, "Reconfiguration already in progress, removing queued config"); break; + case CONF_CONFIRM: cli_msg(18, "Reconfiguration confirmed"); break; + case CONF_SHUTDOWN: cli_msg( 6, "Reconfiguration ignored, shutting down"); break; + case CONF_NOTHING: cli_msg(19, "Nothing to do"); break; + default: break; } } +/* Hack for scheduled undo notification */ +cli *cmd_reconfig_stored_cli; + +void +cmd_reconfig_undo_notify(void) +{ + if (cmd_reconfig_stored_cli) + { + cli *c = cmd_reconfig_stored_cli; + cli_printf(c, CLI_ASYNC_CODE, "Config timeout expired, starting undo"); + cli_write_trigger(c); + } +} + +void +cmd_reconfig(char *name, int type, int timeout) +{ + if (cli_access_restricted()) + return; + + struct config *conf = cmd_read_config(name); + if (!conf) + return; + + int r = config_commit(conf, type, timeout); + + if ((r >= 0) && (timeout > 0)) + { + cmd_reconfig_stored_cli = this_cli; + cli_msg(-22, "Undo scheduled in %d s", timeout); + } + + cmd_reconfig_msg(r); +} + +void +cmd_reconfig_confirm(void) +{ + if (cli_access_restricted()) + return; + + int r = config_confirm(); + cmd_reconfig_msg(r); +} + +void +cmd_reconfig_undo(void) +{ + if (cli_access_restricted()) + return; + + cli_msg(-21, "Undo requested"); + + int r = config_undo(); + cmd_reconfig_msg(r); +} + /* * Command-Line Interface */ @@ -623,6 +691,7 @@ main(int argc, char **argv) rt_init(); if_init(); roa_init(); + config_init(); uid_t use_uid = get_uid(use_user); gid_t use_gid = get_gid(use_group); diff --git a/sysdep/unix/timer.h b/sysdep/unix/timer.h index a788ae27..17450322 100644 --- a/sysdep/unix/timer.h +++ b/sysdep/unix/timer.h @@ -32,6 +32,7 @@ void tm_dump_all(void); extern bird_clock_t now; /* Relative, monotonic time in seconds */ extern bird_clock_t now_real; /* Time in seconds since fixed known epoch */ +extern bird_clock_t boot_time; static inline bird_clock_t tm_remains(timer *t) diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index a9edbebc..aca3cc77 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -19,9 +19,14 @@ extern char *bird_name; void async_config(void); void async_dump(void); void async_shutdown(void); -void cmd_reconfig(char *name, int type); +void cmd_check_config(char *name); +void cmd_reconfig(char *name, int type, int timeout); +void cmd_reconfig_confirm(void); +void cmd_reconfig_undo(void); void cmd_shutdown(void); +#define UNIX_DEFAULT_CONFIGURE_TIMEOUT 300 + /* io.c */ volatile int async_config_flag;