mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2025-03-11 17:08:46 +00:00
Merge commit '13d4dd138d5dc6c884ded280f9244fac707c4f32' into integrated
This commit is contained in:
commit
0596b3dbf9
5
NEWS
5
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.
|
||||
|
2
README
2
README
@ -3,7 +3,7 @@
|
||||
(c) 1998--2008 Martin Mares <mj@ucw.cz>
|
||||
(c) 1998--2000 Pavel Machek <pavel@ucw.cz>
|
||||
(c) 1998--2008 Ondrej Filip <feela@network.cz>
|
||||
(c) 2009--2011 CZ.NIC z.s.p.o.
|
||||
(c) 2009--2013 CZ.NIC z.s.p.o.
|
||||
|
||||
================================================================================
|
||||
|
||||
|
10
bird.conf
10
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;
|
||||
|
269
conf/conf.c
269
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
30
conf/conf.h
30
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 */
|
||||
|
||||
|
@ -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)')
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
||||
<tag>router id <m/IPv4 address/</tag> 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.
|
||||
<tag>router id <m/IPv4 address/</tag>
|
||||
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.
|
||||
|
||||
<tag>router id from [-] [ "<m/mask/" ] [ <m/prefix/ ] [, ...]</tag>
|
||||
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 <ref id="dsc-iface" name="interface">
|
||||
section for detailed description of interface patterns.
|
||||
|
||||
<tag>listen bgp [address <m/address/] [port <m/port/] [dual]</tag>
|
||||
This option allows to specify address and port where BGP
|
||||
@ -471,15 +482,23 @@ to zero to disable it. An empty <cf><m/switch/</cf> is equivalent to <cf/on/
|
||||
Specify an import route limit (a maximum number of routes
|
||||
imported from the protocol) and optionally the action to be
|
||||
taken when the limit is hit. Warn action just prints warning
|
||||
log message. Block action ignores new routes coming from the
|
||||
log message. Block action discards new routes coming from the
|
||||
protocol. Restart and disable actions shut the protocol down
|
||||
like appropriate commands. Disable is the default action if an
|
||||
action is not explicitly specified. Note that limits are reset
|
||||
during protocol reconfigure, reload or restart. Also note that
|
||||
if <cf/import keep filtered/ is active, filtered routes are
|
||||
counted towards the limit and blocked routes are forgotten, as
|
||||
the main purpose of the import limit is to protect routing
|
||||
tables from overflow. Default: <cf/none/.
|
||||
during protocol reconfigure, reload or restart. Default: <cf/none/.
|
||||
|
||||
<tag>receive limit <m/number/ [action warn | block | restart | disable]</tag>
|
||||
Specify an receive route limit (a maximum number of routes
|
||||
received from the protocol and remembered). It works almost
|
||||
identically to <cf>import limit</cf> option, the only
|
||||
difference is that if <cf/import keep filtered/ option is
|
||||
active, filtered routes are counted towards the limit and
|
||||
blocked routes are forgotten, as the main purpose of the
|
||||
receive limit is to protect routing tables from
|
||||
overflow. Import limit, on the contrary, counts accepted
|
||||
routes only and routes blocked by the limit are handled like
|
||||
filtered routes. Default: <cf/none/.
|
||||
|
||||
<tag>export limit <m/number/ [action warn | block | restart | disable]</tag>
|
||||
Specify an export route limit, works similarly to
|
||||
@ -702,19 +721,48 @@ This argument can be omitted if there exists only a single instance.
|
||||
<tag>flush roa [table <m/t/>]</tag>
|
||||
Remove all dynamic ROA entries from a ROA table.
|
||||
|
||||
<tag>configure [soft] ["<m/config file/"]</tag>
|
||||
<tag>configure [soft] ["<m/config file/"] [timeout [<m/num/]]</tag>
|
||||
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
|
||||
<cf/soft/ option is used, changes in filters does not cause
|
||||
filters usually lead to restart of affected protocols.
|
||||
|
||||
If <cf/soft/ option is used, changes in filters does not cause
|
||||
BIRD to restart affected protocols, therefore already accepted
|
||||
routes (according to old filters) would be still propagated,
|
||||
but new routes would be processed according to the new
|
||||
filters.
|
||||
|
||||
If <cf/timeout/ option is used, config timer is activated. The
|
||||
new configuration could be either confirmed using
|
||||
<cf/configure confirm/ command, or it will be reverted to the
|
||||
old one when the config timer expires. This is useful for cases
|
||||
when reconfiguration breaks current routing and a router becames
|
||||
inaccessible for an administrator. The config timeout expiration is
|
||||
equivalent to <cf/configure undo/ command. The timeout duration
|
||||
could be specified, default is 300 s.
|
||||
|
||||
<tag>configure confirm</tag>
|
||||
Deactivate the config undo timer and therefore confirm the current
|
||||
configuration.
|
||||
|
||||
<tag>configure undo</tag>
|
||||
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.
|
||||
|
||||
<tag>configure check ["<m/config file/"]</tag>
|
||||
Read and parse given config file, but do not use it. useful
|
||||
for checking syntactic and some semantic validity of an config
|
||||
file.
|
||||
|
||||
<tag>enable|disable|restart <m/name/|"<m/pattern/"|all</tag>
|
||||
Enable, disable or restart a given protocol instance, instances matching the <cf><m/pattern/</cf> or <cf/all/ instances.
|
||||
Enable, disable or restart a given protocol instance,
|
||||
instances matching the <cf><m/pattern/</cf> or
|
||||
<cf/all/ instances.
|
||||
|
||||
<tag>reload [in|out] <m/name/|"<m/pattern/"|all</tag>
|
||||
|
||||
@ -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.
|
||||
<tag>route <m/prefix/ recursive <m/ip/</tag> Static recursive route,
|
||||
its nexthop depends on a route table lookup for given IP address.
|
||||
<tag>route <m/prefix/ drop|reject|prohibit</tag> Special routes
|
||||
specifying to drop the packet, return it as unreachable or return
|
||||
it as administratively prohibited.
|
||||
<tag>route <m/prefix/ blackhole|unreachable|prohibit</tag> 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 <cf/drop/ and <cf/reject/.
|
||||
|
||||
<tag>check link <m/switch/</tag>
|
||||
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
|
||||
}
|
||||
</code>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
20
nest/cli.c
20
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, "<line overflow>");
|
||||
cli_printf(c, errcode, "<line overflow>");
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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 *, ...);
|
||||
|
@ -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");
|
||||
|
@ -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 */
|
||||
|
||||
|
67
nest/iface.c
67
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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
39
nest/proto.c
39
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 */
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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, [<name>], [[Show details of static protocol]])
|
||||
|
@ -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 <i> log_mask log_mask_list log_cat
|
||||
%type <i> log_mask log_mask_list log_cat cfg_timeout
|
||||
%type <g> log_file
|
||||
%type <t> cfg_name
|
||||
%type <tf> timeformat_which
|
||||
@ -104,13 +104,26 @@ timeformat_base:
|
||||
|
||||
/* Unix specific commands */
|
||||
|
||||
CF_CLI_HELP(CONFIGURE, [soft] [\"<file>\"], [[Reload configuration]])
|
||||
CF_CLI_HELP(CONFIGURE, ..., [[Reload configuration]])
|
||||
|
||||
CF_CLI(CONFIGURE, cfg_name, [\"<file>\"], [[Reload configuration]])
|
||||
{ cmd_reconfig($2, RECONFIG_HARD); } ;
|
||||
CF_CLI(CONFIGURE, cfg_name cfg_timeout, [\"<file>\"] [timeout [<sec>]], [[Reload configuration]])
|
||||
{ cmd_reconfig($2, RECONFIG_HARD, $3); } ;
|
||||
|
||||
CF_CLI(CONFIGURE SOFT, cfg_name, [\"<file>\"], [[Reload configuration and ignore changes in filters]])
|
||||
{ cmd_reconfig($3, RECONFIG_SOFT); } ;
|
||||
CF_CLI(CONFIGURE SOFT, cfg_name cfg_timeout, [\"<file>\"] [timeout [<sec>]], [[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, [<sec>], [[Reload configuration with undo timeout]])
|
||||
CF_CLI_CMD(CONFIGURE SOFT TIMEOUT, [<sec>], [[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, [\"<file>\"], [[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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user