1999-02-05 21:37:34 +00:00
|
|
|
/*
|
|
|
|
* BIRD Internet Routing Daemon -- Configuration File Handling
|
|
|
|
*
|
2000-01-16 16:44:50 +00:00
|
|
|
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
|
1999-02-05 21:37:34 +00:00
|
|
|
*
|
|
|
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
|
|
|
*/
|
|
|
|
|
2000-06-03 18:23:00 +00:00
|
|
|
/**
|
|
|
|
* DOC: Configuration manager
|
|
|
|
*
|
2000-06-08 12:37:21 +00:00
|
|
|
* Configuration of BIRD is complex, yet straightforward. There are three
|
2000-06-03 18:23:00 +00:00
|
|
|
* modules taking care of the configuration: config manager (which takes care
|
2000-06-07 13:25:53 +00:00
|
|
|
* of storage of the config information and controls switching between configs),
|
2000-06-07 12:29:08 +00:00
|
|
|
* lexical analyzer and parser.
|
2000-06-03 18:23:00 +00:00
|
|
|
*
|
|
|
|
* The configuration manager stores each config as a &config structure
|
|
|
|
* accompanied by a linear pool from which all information associated
|
|
|
|
* with the config and pointed to by the &config structure is allocated.
|
|
|
|
*
|
2000-06-08 12:37:21 +00:00
|
|
|
* There can exist up to four different configurations at one time: an active
|
2000-06-03 18:23:00 +00:00
|
|
|
* one (pointed to by @config), configuration we are just switching from
|
2015-11-08 23:42:02 +00:00
|
|
|
* (@old_config), one queued for the next reconfiguration (@future_config; 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). 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. The new config (@new_config) and
|
|
|
|
* associated linear pool (@cfg_mem) is non-NULL only during parsing.
|
2000-06-03 18:23:00 +00:00
|
|
|
*
|
2015-11-08 23:42:02 +00:00
|
|
|
* Loading of new configuration is very simple: just call config_alloc() to get
|
|
|
|
* a new &config structure, then use config_parse() to parse a configuration
|
|
|
|
* file and fill all fields of the structure and finally ask the config manager
|
|
|
|
* to switch to the new config by calling config_commit().
|
2000-06-03 18:23:00 +00:00
|
|
|
*
|
|
|
|
* CLI commands are parsed in a very similar way -- there is also a stripped-down
|
2000-06-07 12:29:08 +00:00
|
|
|
* &config structure associated with them and they are lex-ed and parsed by the
|
2000-06-03 18:23:00 +00:00
|
|
|
* same functions, only a special fake token is prepended before the command
|
|
|
|
* text to make the parser recognize only the rules corresponding to CLI commands.
|
|
|
|
*/
|
|
|
|
|
1999-02-05 21:37:34 +00:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
2000-03-12 21:01:38 +00:00
|
|
|
#undef LOCAL_DEBUG
|
2000-01-16 16:44:50 +00:00
|
|
|
|
1999-02-05 21:37:34 +00:00
|
|
|
#include "nest/bird.h"
|
2023-10-29 15:25:01 +00:00
|
|
|
#include "nest/route.h"
|
1999-02-05 21:37:34 +00:00
|
|
|
#include "nest/protocol.h"
|
|
|
|
#include "nest/iface.h"
|
2022-09-14 23:38:18 +00:00
|
|
|
#include "nest/mpls.h"
|
2016-11-30 09:17:23 +00:00
|
|
|
#include "lib/resource.h"
|
|
|
|
#include "lib/string.h"
|
2000-01-16 16:44:50 +00:00
|
|
|
#include "lib/event.h"
|
2017-11-28 16:43:20 +00:00
|
|
|
#include "lib/timer.h"
|
1999-02-05 21:37:34 +00:00
|
|
|
#include "conf/conf.h"
|
2016-11-30 09:17:23 +00:00
|
|
|
#include "filter/filter.h"
|
2021-02-10 15:53:57 +00:00
|
|
|
#include "sysdep/unix/unix.h"
|
2016-11-09 15:36:34 +00:00
|
|
|
|
1999-02-05 21:37:34 +00:00
|
|
|
|
|
|
|
static jmp_buf conf_jmpbuf;
|
|
|
|
|
2024-06-13 10:15:38 +00:00
|
|
|
config_ref config;
|
2023-10-29 14:42:46 +00:00
|
|
|
_Thread_local struct config *new_config;
|
2022-11-01 07:56:26 +00:00
|
|
|
pool *config_pool;
|
2012-12-26 11:40:48 +00:00
|
|
|
|
2024-06-25 13:53:13 +00:00
|
|
|
struct config *old_config; /* Old configuration */
|
2024-06-13 10:15:38 +00:00
|
|
|
static config_ref future_config; /* New config held here if recon requested during recon */
|
2012-12-26 11:40:48 +00:00
|
|
|
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 */
|
|
|
|
|
2024-06-13 10:15:38 +00:00
|
|
|
static void config_done(void);
|
2012-12-26 11:40:48 +00:00
|
|
|
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 */
|
1999-02-05 21:37:34 +00:00
|
|
|
|
2024-06-13 10:15:38 +00:00
|
|
|
static void
|
2024-06-25 13:53:13 +00:00
|
|
|
config_obstacles_cleared(struct callback *_ UNUSED)
|
2024-06-13 10:15:38 +00:00
|
|
|
{
|
|
|
|
ASSERT_DIE(birdloop_inside(&main_birdloop));
|
2024-06-25 13:53:13 +00:00
|
|
|
ASSERT_DIE(configuring);
|
|
|
|
config_done();
|
2024-06-13 10:15:38 +00:00
|
|
|
}
|
|
|
|
|
2000-06-03 18:23:00 +00:00
|
|
|
/**
|
|
|
|
* config_alloc - allocate a new configuration
|
|
|
|
* @name: name of the config
|
|
|
|
*
|
|
|
|
* This function creates new &config structure, attaches a resource
|
|
|
|
* pool and a linear memory pool to it and makes it available for
|
|
|
|
* further use. Returns a pointer to the structure.
|
|
|
|
*/
|
1999-02-05 21:37:34 +00:00
|
|
|
struct config *
|
2016-11-09 15:36:34 +00:00
|
|
|
config_alloc(const char *name)
|
1999-02-05 21:37:34 +00:00
|
|
|
{
|
2023-04-21 13:26:06 +00:00
|
|
|
pool *p = rp_new(config_pool, the_bird_domain.the_bird, "Config");
|
2017-05-16 12:31:16 +00:00
|
|
|
linpool *l = lp_new_default(p);
|
1999-02-05 21:37:34 +00:00
|
|
|
struct config *c = lp_allocz(l, sizeof(struct config));
|
|
|
|
|
2015-11-08 23:42:02 +00:00
|
|
|
/* Duplication of name string in local linear pool */
|
|
|
|
uint nlen = strlen(name) + 1;
|
|
|
|
char *ndup = lp_allocu(l, nlen);
|
|
|
|
memcpy(ndup, name, nlen);
|
|
|
|
|
2016-11-09 15:36:34 +00:00
|
|
|
init_list(&c->tests);
|
2019-02-26 15:44:24 +00:00
|
|
|
init_list(&c->symbols);
|
1999-02-05 21:37:34 +00:00
|
|
|
c->pool = p;
|
2015-11-08 23:42:02 +00:00
|
|
|
c->mem = l;
|
|
|
|
c->file_name = ndup;
|
2017-06-06 14:47:30 +00:00
|
|
|
c->tf_route = c->tf_proto = TM_ISO_SHORT_MS;
|
|
|
|
c->tf_base = c->tf_log = TM_ISO_LONG_MS;
|
2014-03-20 13:07:12 +00:00
|
|
|
c->gr_wait = DEFAULT_GR_WAIT;
|
2010-02-02 23:19:24 +00:00
|
|
|
|
2024-06-13 10:15:38 +00:00
|
|
|
callback_init(&c->obstacles_cleared, config_obstacles_cleared, &main_birdloop);
|
|
|
|
obstacle_target_init(&c->obstacles, &c->obstacles_cleared, p, "Config");
|
2023-08-21 13:37:09 +00:00
|
|
|
|
1999-02-05 21:37:34 +00:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2000-06-03 18:23:00 +00:00
|
|
|
/**
|
|
|
|
* config_parse - parse a configuration
|
|
|
|
* @c: configuration
|
|
|
|
*
|
|
|
|
* config_parse() reads input by calling a hook function pointed to
|
|
|
|
* by @cf_read_hook and parses it according to the configuration
|
|
|
|
* grammar. It also calls all the preconfig and postconfig hooks
|
2000-06-07 12:29:08 +00:00
|
|
|
* before, resp. after parsing.
|
2000-06-03 18:23:00 +00:00
|
|
|
*
|
|
|
|
* Result: 1 if the config has been parsed successfully, 0 if any
|
2000-06-07 12:29:08 +00:00
|
|
|
* error has occurred (such as anybody calling cf_error()) and
|
2000-06-03 18:23:00 +00:00
|
|
|
* the @err_msg field has been set to the error message.
|
|
|
|
*/
|
1999-02-05 21:37:34 +00:00
|
|
|
int
|
|
|
|
config_parse(struct config *c)
|
|
|
|
{
|
2015-11-08 23:42:02 +00:00
|
|
|
int done = 0;
|
2000-03-07 21:50:03 +00:00
|
|
|
DBG("Parsing configuration file `%s'\n", c->file_name);
|
1999-02-05 21:37:34 +00:00
|
|
|
new_config = c;
|
|
|
|
cfg_mem = c->mem;
|
|
|
|
if (setjmp(conf_jmpbuf))
|
2015-11-08 23:42:02 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2024-06-13 10:29:21 +00:00
|
|
|
cf_lex_init(NULL, c);
|
2024-06-13 13:49:56 +00:00
|
|
|
filter_preconfig(c);
|
1999-12-06 13:44:45 +00:00
|
|
|
sysdep_preconfig(c);
|
1999-02-05 21:37:34 +00:00
|
|
|
protos_preconfig(c);
|
2022-09-14 23:38:18 +00:00
|
|
|
mpls_preconfig(c);
|
1999-05-17 20:14:52 +00:00
|
|
|
rt_preconfig(c);
|
1999-02-05 21:37:34 +00:00
|
|
|
cf_parse();
|
2022-06-04 15:34:57 +00:00
|
|
|
rt_postconfig(c);
|
2016-01-26 10:48:58 +00:00
|
|
|
|
2010-03-17 11:19:22 +00:00
|
|
|
if (EMPTY_LIST(c->protos))
|
|
|
|
cf_error("No protocol is specified in the config file");
|
2016-01-26 10:48:58 +00:00
|
|
|
|
2015-11-08 23:42:02 +00:00
|
|
|
done = 1;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
new_config = NULL;
|
|
|
|
cfg_mem = NULL;
|
|
|
|
return done;
|
1999-02-05 21:37:34 +00:00
|
|
|
}
|
|
|
|
|
2000-06-03 18:23:00 +00:00
|
|
|
/**
|
|
|
|
* cli_parse - parse a CLI command
|
|
|
|
* @c: temporary config structure
|
|
|
|
*
|
|
|
|
* cli_parse() is similar to config_parse(), but instead of a configuration,
|
|
|
|
* it parses a CLI command. See the CLI module for more information.
|
|
|
|
*/
|
Parse CLI commands. We use the same parser as for configuration files (because
we want to allow filter and similar complex constructs to be used in commands
and we should avoid code duplication), only with CLI_MARKER token prepended
before the whole input.
Defined macro CF_CLI(cmd, args, help) for defining CLI commands in .Y files.
The first argument specifies the command itself, the remaining two arguments
are copied to the help file (er, will be copied after the help file starts
to exist). This macro automatically creates a skeleton rule for the command,
you only need to append arguments as in:
CF_CLI(STEAL MONEY, <$>, [[Steal <$> US dollars or equivalent in any other currency]]): NUM {
cli_msg(0, "%d$ stolen", $3);
} ;
Also don't forget to reset lexer state between inputs.
1999-10-31 17:47:47 +00:00
|
|
|
int
|
2024-06-13 10:29:21 +00:00
|
|
|
cli_parse(struct config *main_config, struct config *c)
|
Parse CLI commands. We use the same parser as for configuration files (because
we want to allow filter and similar complex constructs to be used in commands
and we should avoid code duplication), only with CLI_MARKER token prepended
before the whole input.
Defined macro CF_CLI(cmd, args, help) for defining CLI commands in .Y files.
The first argument specifies the command itself, the remaining two arguments
are copied to the help file (er, will be copied after the help file starts
to exist). This macro automatically creates a skeleton rule for the command,
you only need to append arguments as in:
CF_CLI(STEAL MONEY, <$>, [[Steal <$> US dollars or equivalent in any other currency]]): NUM {
cli_msg(0, "%d$ stolen", $3);
} ;
Also don't forget to reset lexer state between inputs.
1999-10-31 17:47:47 +00:00
|
|
|
{
|
2015-11-08 23:42:02 +00:00
|
|
|
int done = 0;
|
|
|
|
new_config = c;
|
Parse CLI commands. We use the same parser as for configuration files (because
we want to allow filter and similar complex constructs to be used in commands
and we should avoid code duplication), only with CLI_MARKER token prepended
before the whole input.
Defined macro CF_CLI(cmd, args, help) for defining CLI commands in .Y files.
The first argument specifies the command itself, the remaining two arguments
are copied to the help file (er, will be copied after the help file starts
to exist). This macro automatically creates a skeleton rule for the command,
you only need to append arguments as in:
CF_CLI(STEAL MONEY, <$>, [[Steal <$> US dollars or equivalent in any other currency]]): NUM {
cli_msg(0, "%d$ stolen", $3);
} ;
Also don't forget to reset lexer state between inputs.
1999-10-31 17:47:47 +00:00
|
|
|
cfg_mem = c->mem;
|
|
|
|
if (setjmp(conf_jmpbuf))
|
2015-11-08 23:42:02 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2024-06-13 10:29:21 +00:00
|
|
|
cf_lex_init(main_config, c);
|
Parse CLI commands. We use the same parser as for configuration files (because
we want to allow filter and similar complex constructs to be used in commands
and we should avoid code duplication), only with CLI_MARKER token prepended
before the whole input.
Defined macro CF_CLI(cmd, args, help) for defining CLI commands in .Y files.
The first argument specifies the command itself, the remaining two arguments
are copied to the help file (er, will be copied after the help file starts
to exist). This macro automatically creates a skeleton rule for the command,
you only need to append arguments as in:
CF_CLI(STEAL MONEY, <$>, [[Steal <$> US dollars or equivalent in any other currency]]): NUM {
cli_msg(0, "%d$ stolen", $3);
} ;
Also don't forget to reset lexer state between inputs.
1999-10-31 17:47:47 +00:00
|
|
|
cf_parse();
|
2015-11-08 23:42:02 +00:00
|
|
|
done = 1;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
new_config = NULL;
|
|
|
|
cfg_mem = NULL;
|
|
|
|
return done;
|
Parse CLI commands. We use the same parser as for configuration files (because
we want to allow filter and similar complex constructs to be used in commands
and we should avoid code duplication), only with CLI_MARKER token prepended
before the whole input.
Defined macro CF_CLI(cmd, args, help) for defining CLI commands in .Y files.
The first argument specifies the command itself, the remaining two arguments
are copied to the help file (er, will be copied after the help file starts
to exist). This macro automatically creates a skeleton rule for the command,
you only need to append arguments as in:
CF_CLI(STEAL MONEY, <$>, [[Steal <$> US dollars or equivalent in any other currency]]): NUM {
cli_msg(0, "%d$ stolen", $3);
} ;
Also don't forget to reset lexer state between inputs.
1999-10-31 17:47:47 +00:00
|
|
|
}
|
|
|
|
|
2000-06-03 18:23:00 +00:00
|
|
|
/**
|
|
|
|
* config_free - free a configuration
|
|
|
|
* @c: configuration to be freed
|
|
|
|
*
|
|
|
|
* This function takes a &config structure and frees all resources
|
|
|
|
* associated with it.
|
|
|
|
*/
|
1999-02-05 21:37:34 +00:00
|
|
|
void
|
|
|
|
config_free(struct config *c)
|
|
|
|
{
|
2023-02-19 02:59:10 +00:00
|
|
|
if (!c)
|
|
|
|
return;
|
|
|
|
|
2024-06-13 10:15:38 +00:00
|
|
|
synchronize_rcu();
|
|
|
|
ASSERT_DIE(!obstacle_target_count(&c->obstacles));
|
2023-02-19 02:59:10 +00:00
|
|
|
|
2023-04-20 19:08:38 +00:00
|
|
|
rp_free(c->pool);
|
1999-02-05 21:37:34 +00:00
|
|
|
}
|
|
|
|
|
2022-11-09 20:09:16 +00:00
|
|
|
/**
|
|
|
|
* config_free_old - free stored old configuration
|
|
|
|
*
|
|
|
|
* This function frees the old configuration (%old_config) that is saved for the
|
|
|
|
* purpose of undo. It is useful before parsing a new config when reconfig is
|
|
|
|
* requested, to avoid keeping three (perhaps memory-heavy) configs together.
|
2023-02-19 02:59:10 +00:00
|
|
|
* Configuration is not freed when it is still active during reconfiguration.
|
2022-11-09 20:09:16 +00:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
config_free_old(void)
|
|
|
|
{
|
|
|
|
tm_stop(config_timer);
|
|
|
|
undo_available = 0;
|
2024-06-25 13:53:13 +00:00
|
|
|
|
|
|
|
config_free(old_config);
|
|
|
|
old_config = NULL;
|
2000-01-16 16:44:50 +00:00
|
|
|
}
|
|
|
|
|
2024-06-13 14:30:51 +00:00
|
|
|
struct global_runtime global_runtime_internal[2] = {{
|
|
|
|
.tf_log = {
|
|
|
|
.fmt1 = "%F %T.%3f",
|
|
|
|
},
|
|
|
|
}};
|
|
|
|
struct global_runtime * _Atomic global_runtime = &global_runtime_internal[0];
|
|
|
|
|
2024-06-13 13:36:18 +00:00
|
|
|
static void
|
2000-01-16 17:40:26 +00:00
|
|
|
global_commit(struct config *new, struct config *old)
|
2000-01-16 16:44:50 +00:00
|
|
|
{
|
2024-06-13 14:30:51 +00:00
|
|
|
/* Updating the global runtime. */
|
|
|
|
struct global_runtime *og = atomic_load_explicit(&global_runtime, memory_order_relaxed);
|
|
|
|
struct global_runtime *ng = &global_runtime_internal[og == &global_runtime_internal[0]];
|
|
|
|
ASSERT_DIE(ng != og);
|
|
|
|
|
|
|
|
#define COPY(x) ng->x = new->x;
|
|
|
|
MACRO_FOREACH(COPY,
|
|
|
|
tf_route,
|
|
|
|
tf_proto,
|
|
|
|
tf_log,
|
|
|
|
tf_base,
|
|
|
|
cli_debug,
|
|
|
|
latency_debug,
|
|
|
|
latency_limit,
|
|
|
|
watchdog_warning,
|
|
|
|
watchdog_timeout,
|
|
|
|
gr_wait,
|
|
|
|
hostname
|
|
|
|
);
|
|
|
|
#undef COPY
|
|
|
|
|
|
|
|
ng->load_time = current_time();
|
|
|
|
|
|
|
|
if (new->router_id)
|
|
|
|
ng->router_id = new->router_id;
|
|
|
|
else if (old)
|
|
|
|
{
|
|
|
|
/* The startup router ID must be determined after start of device protocol,
|
|
|
|
* thus if old == NULL then we do nothing */
|
|
|
|
ng->router_id = og->router_id;
|
|
|
|
|
|
|
|
if (new->router_id_from)
|
2012-12-27 11:56:23 +00:00
|
|
|
{
|
2024-06-13 14:30:51 +00:00
|
|
|
u32 id = if_choose_router_id(new->router_id_from, og->router_id);
|
|
|
|
if (!id)
|
|
|
|
log(L_WARN "Cannot determine router ID, using old one");
|
|
|
|
else
|
|
|
|
ng->router_id = id;
|
2012-12-27 11:56:23 +00:00
|
|
|
}
|
2024-06-13 14:30:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
atomic_store_explicit(&global_runtime, ng, memory_order_release);
|
|
|
|
|
|
|
|
/* We have to wait until every reader surely doesn't read the old values */
|
|
|
|
synchronize_rcu();
|
2000-01-16 16:44:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2024-06-13 10:15:38 +00:00
|
|
|
config_do_commit(config_ref *cr, int type)
|
2000-01-16 16:44:50 +00:00
|
|
|
{
|
2012-12-26 11:40:48 +00:00
|
|
|
if (type == RECONFIG_UNDO)
|
|
|
|
{
|
2024-06-25 13:53:13 +00:00
|
|
|
OBSREF_SET(*cr, old_config);
|
2012-12-26 11:40:48 +00:00
|
|
|
type = old_cftype;
|
|
|
|
}
|
2024-06-25 13:53:13 +00:00
|
|
|
else
|
|
|
|
config_free(old_config);
|
2000-01-16 16:44:50 +00:00
|
|
|
|
2024-06-25 13:53:13 +00:00
|
|
|
old_config = NULL;
|
2012-12-26 11:40:48 +00:00
|
|
|
old_cftype = type;
|
2024-06-13 10:15:38 +00:00
|
|
|
|
|
|
|
struct config *c = OBSREF_GET(*cr);
|
2024-06-25 13:53:13 +00:00
|
|
|
old_config = OBSREF_GET(config);
|
2024-06-13 10:15:38 +00:00
|
|
|
|
|
|
|
OBSREF_CLEAR(config);
|
|
|
|
OBSREF_SET(config, OBSREF_GET(*cr));
|
2012-12-26 11:40:48 +00:00
|
|
|
|
2024-01-28 22:03:54 +00:00
|
|
|
if (!c->hostname)
|
|
|
|
{
|
|
|
|
c->hostname = get_hostname(c->mem);
|
|
|
|
|
|
|
|
if (!c->hostname)
|
|
|
|
log(L_WARN "Cannot determine hostname");
|
|
|
|
}
|
|
|
|
|
2012-12-26 11:40:48 +00:00
|
|
|
configuring = 1;
|
2024-06-25 13:53:13 +00:00
|
|
|
if (old_config && !c->shutdown)
|
2012-12-26 11:40:48 +00:00
|
|
|
log(L_INFO "Reconfiguring");
|
|
|
|
|
2019-02-26 15:44:24 +00:00
|
|
|
DBG("filter_commit\n");
|
2024-06-25 13:53:13 +00:00
|
|
|
filter_commit(c, old_config);
|
2000-01-16 16:44:50 +00:00
|
|
|
DBG("sysdep_commit\n");
|
2024-06-25 13:53:13 +00:00
|
|
|
sysdep_commit(c, old_config);
|
2000-01-16 16:44:50 +00:00
|
|
|
DBG("global_commit\n");
|
2024-06-25 13:53:13 +00:00
|
|
|
global_commit(c, old_config);
|
|
|
|
mpls_commit(c, old_config);
|
2000-01-16 16:44:50 +00:00
|
|
|
DBG("rt_commit\n");
|
2024-06-25 13:53:13 +00:00
|
|
|
rt_commit(c, old_config);
|
2000-01-16 16:44:50 +00:00
|
|
|
DBG("protos_commit\n");
|
2024-06-25 13:53:13 +00:00
|
|
|
protos_commit(c, old_config, type);
|
2012-12-26 11:40:48 +00:00
|
|
|
|
2024-06-13 10:15:38 +00:00
|
|
|
/* Can be cleared directly? */
|
2024-06-25 13:53:13 +00:00
|
|
|
if (!old_config)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (!callback_is_active(&old_config->obstacles_cleared))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
callback_cancel(&old_config->obstacles_cleared);
|
|
|
|
return 1;
|
2000-01-16 16:44:50 +00:00
|
|
|
}
|
|
|
|
|
2000-04-27 22:28:49 +00:00
|
|
|
static void
|
2024-06-13 10:15:38 +00:00
|
|
|
config_done(void)
|
2000-01-16 16:44:50 +00:00
|
|
|
{
|
2024-06-13 10:15:38 +00:00
|
|
|
struct config *c = OBSREF_GET(config);
|
|
|
|
ASSERT_DIE(c);
|
2023-08-21 13:37:09 +00:00
|
|
|
|
2024-06-13 10:15:38 +00:00
|
|
|
if (c->shutdown)
|
2012-12-26 11:40:48 +00:00
|
|
|
sysdep_shutdown_done();
|
|
|
|
|
|
|
|
configuring = 0;
|
2024-06-25 13:53:13 +00:00
|
|
|
|
|
|
|
if (old_config)
|
2012-12-26 11:40:48 +00:00
|
|
|
log(L_INFO "Reconfigured");
|
2000-01-16 16:44:50 +00:00
|
|
|
|
2012-12-26 11:40:48 +00:00
|
|
|
if (future_cftype)
|
2000-01-16 16:44:50 +00:00
|
|
|
{
|
2012-12-26 11:40:48 +00:00
|
|
|
int type = future_cftype;
|
2024-06-27 07:48:38 +00:00
|
|
|
struct config *fc = OBSREF_GET(future_config);
|
2024-06-13 10:15:38 +00:00
|
|
|
|
2024-06-27 07:48:38 +00:00
|
|
|
if (type == RECONFIG_UNDO)
|
|
|
|
{
|
|
|
|
ASSERT_DIE(!fc);
|
|
|
|
ASSERT_DIE(old_config);
|
|
|
|
fc = old_config;
|
|
|
|
}
|
|
|
|
|
|
|
|
CONFIG_REF_LOCAL(conf, fc);
|
2024-06-13 10:15:38 +00:00
|
|
|
|
2012-12-26 11:40:48 +00:00
|
|
|
future_cftype = RECONFIG_NONE;
|
2024-06-13 10:15:38 +00:00
|
|
|
OBSREF_CLEAR(future_config);
|
2012-12-26 11:40:48 +00:00
|
|
|
|
2010-02-06 21:57:51 +00:00
|
|
|
log(L_INFO "Reconfiguring to queued configuration");
|
2024-06-13 10:15:38 +00:00
|
|
|
if (config_do_commit(&conf, type))
|
|
|
|
config_done();
|
2000-01-16 16:44:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-06-03 18:23:00 +00:00
|
|
|
/**
|
|
|
|
* config_commit - commit a configuration
|
|
|
|
* @c: new configuration
|
2009-06-19 21:49:34 +00:00
|
|
|
* @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD)
|
2017-06-21 12:43:49 +00:00
|
|
|
* @timeout: timeout for undo (in seconds; or 0 for no timeout)
|
2000-06-03 18:23:00 +00:00
|
|
|
*
|
|
|
|
* When a configuration is parsed and prepared for use, the
|
|
|
|
* config_commit() function starts the process of reconfiguration.
|
|
|
|
* It checks whether there is already a reconfiguration in progress
|
|
|
|
* in which case it just queues the new config for later processing.
|
|
|
|
* Else it notifies all modules about the new configuration by calling
|
|
|
|
* their commit() functions which can either accept it immediately
|
|
|
|
* or call config_add_obstacle() to report that they need some time
|
|
|
|
* to complete the reconfiguration. After all such obstacles are removed
|
|
|
|
* using config_del_obstacle(), the old configuration is freed and
|
|
|
|
* everything runs according to the new one.
|
|
|
|
*
|
2012-12-26 11:40:48 +00:00
|
|
|
* 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.
|
|
|
|
*
|
2000-06-03 18:23:00 +00:00
|
|
|
* 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
|
|
|
|
* or %CONF_SHUTDOWN if BIRD is in shutdown mode and no new configurations
|
|
|
|
* are accepted.
|
|
|
|
*/
|
2000-01-16 16:44:50 +00:00
|
|
|
int
|
2024-06-13 10:15:38 +00:00
|
|
|
config_commit(config_ref *cr, int type, uint timeout)
|
2000-01-16 16:44:50 +00:00
|
|
|
{
|
2012-12-26 11:40:48 +00:00
|
|
|
if (shutting_down)
|
2000-01-16 16:44:50 +00:00
|
|
|
{
|
2024-06-13 10:15:38 +00:00
|
|
|
OBSREF_CLEAR(*cr);
|
2012-12-26 11:40:48 +00:00
|
|
|
return CONF_SHUTDOWN;
|
2000-01-16 16:44:50 +00:00
|
|
|
}
|
2012-12-26 11:40:48 +00:00
|
|
|
|
|
|
|
undo_available = 1;
|
2017-06-21 12:43:49 +00:00
|
|
|
if (timeout)
|
2017-11-28 16:43:20 +00:00
|
|
|
tm_start(config_timer, timeout S);
|
2012-12-26 11:40:48 +00:00
|
|
|
else
|
2017-11-28 16:43:20 +00:00
|
|
|
tm_stop(config_timer);
|
2012-12-26 11:40:48 +00:00
|
|
|
|
|
|
|
if (configuring)
|
2000-01-16 16:44:50 +00:00
|
|
|
{
|
2012-12-26 11:40:48 +00:00
|
|
|
if (future_cftype)
|
2000-01-16 16:44:50 +00:00
|
|
|
{
|
|
|
|
log(L_INFO "Queueing new configuration, ignoring the one already queued");
|
2024-06-13 10:15:38 +00:00
|
|
|
OBSREF_CLEAR(future_config);
|
2000-01-16 16:44:50 +00:00
|
|
|
}
|
|
|
|
else
|
2012-12-26 11:40:48 +00:00
|
|
|
log(L_INFO "Queueing new configuration");
|
|
|
|
|
|
|
|
future_cftype = type;
|
2024-06-13 10:15:38 +00:00
|
|
|
OBSREF_SET(future_config, OBSREF_GET(*cr));
|
2000-01-16 16:44:50 +00:00
|
|
|
return CONF_QUEUED;
|
|
|
|
}
|
2010-02-06 21:57:51 +00:00
|
|
|
|
2024-06-13 10:15:38 +00:00
|
|
|
if (config_do_commit(cr, type))
|
2000-01-16 16:44:50 +00:00
|
|
|
{
|
2024-06-13 10:15:38 +00:00
|
|
|
config_done();
|
2000-01-16 16:44:50 +00:00
|
|
|
return CONF_DONE;
|
|
|
|
}
|
2012-12-26 11:40:48 +00:00
|
|
|
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;
|
|
|
|
|
2017-11-28 16:43:20 +00:00
|
|
|
tm_stop(config_timer);
|
2012-12-26 11:40:48 +00:00
|
|
|
|
|
|
|
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
|
2016-11-08 16:46:29 +00:00
|
|
|
* was config_undo() too) or %CONF_SHUTDOWN if BIRD is in shutdown mode and
|
2012-12-26 11:40:48 +00:00
|
|
|
* no new configuration changes are accepted.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
config_undo(void)
|
|
|
|
{
|
|
|
|
if (shutting_down)
|
|
|
|
return CONF_SHUTDOWN;
|
|
|
|
|
2024-06-25 13:53:13 +00:00
|
|
|
if (!undo_available || !old_config)
|
2012-12-26 11:40:48 +00:00
|
|
|
return CONF_NOTHING;
|
|
|
|
|
|
|
|
undo_available = 0;
|
2017-11-28 16:43:20 +00:00
|
|
|
tm_stop(config_timer);
|
2012-12-26 11:40:48 +00:00
|
|
|
|
|
|
|
if (configuring)
|
2000-01-16 16:44:50 +00:00
|
|
|
{
|
2012-12-26 11:40:48 +00:00
|
|
|
if (future_cftype)
|
|
|
|
{
|
2024-06-13 10:15:38 +00:00
|
|
|
OBSREF_CLEAR(future_config);
|
2012-12-26 11:40:48 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-13 10:15:38 +00:00
|
|
|
CONFIG_REF_LOCAL_EMPTY(undo_conf);
|
|
|
|
if (config_do_commit(&undo_conf, RECONFIG_UNDO))
|
2012-12-26 11:40:48 +00:00
|
|
|
{
|
2024-06-13 10:15:38 +00:00
|
|
|
OBSREF_CLEAR(undo_conf);
|
|
|
|
config_done();
|
2012-12-26 11:40:48 +00:00
|
|
|
return CONF_DONE;
|
2000-01-16 16:44:50 +00:00
|
|
|
}
|
|
|
|
return CONF_PROGRESS;
|
1999-02-05 21:37:34 +00:00
|
|
|
}
|
|
|
|
|
2019-06-12 14:13:21 +00:00
|
|
|
int
|
|
|
|
config_status(void)
|
|
|
|
{
|
|
|
|
if (shutting_down)
|
|
|
|
return CONF_SHUTDOWN;
|
|
|
|
|
|
|
|
if (configuring)
|
|
|
|
return future_cftype ? CONF_QUEUED : CONF_PROGRESS;
|
|
|
|
|
|
|
|
return CONF_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
btime
|
|
|
|
config_timer_status(void)
|
|
|
|
{
|
|
|
|
return tm_active(config_timer) ? tm_remains(config_timer) : -1;
|
|
|
|
}
|
|
|
|
|
2012-12-26 11:40:48 +00:00
|
|
|
extern void cmd_reconfig_undo_notify(void);
|
|
|
|
|
|
|
|
static void
|
2017-06-01 10:33:20 +00:00
|
|
|
config_timeout(timer *t UNUSED)
|
2012-12-26 11:40:48 +00:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2023-04-21 13:26:06 +00:00
|
|
|
config_pool = rp_new(&root_pool, the_bird_domain.the_bird, "Configurations");
|
2022-11-01 07:56:26 +00:00
|
|
|
|
|
|
|
config_timer = tm_new(config_pool);
|
2012-12-26 11:40:48 +00:00
|
|
|
config_timer->hook = config_timeout;
|
|
|
|
}
|
|
|
|
|
2000-06-03 18:23:00 +00:00
|
|
|
/**
|
|
|
|
* order_shutdown - order BIRD shutdown
|
|
|
|
*
|
|
|
|
* This function initiates shutdown of BIRD. It's accomplished by asking
|
|
|
|
* for switching to an empty configuration.
|
|
|
|
*/
|
2000-01-16 17:40:26 +00:00
|
|
|
void
|
2019-06-18 14:27:21 +00:00
|
|
|
order_shutdown(int gr)
|
2000-01-16 17:40:26 +00:00
|
|
|
{
|
|
|
|
if (shutting_down)
|
|
|
|
return;
|
2012-12-26 11:40:48 +00:00
|
|
|
|
2019-06-18 14:27:21 +00:00
|
|
|
if (!gr)
|
|
|
|
log(L_INFO "Shutting down");
|
|
|
|
else
|
|
|
|
log(L_INFO "Shutting down for graceful restart");
|
|
|
|
|
2024-06-13 10:15:38 +00:00
|
|
|
struct config *c = lp_alloc(OBSREF_GET(config)->mem, sizeof(struct config));
|
|
|
|
memcpy(c, OBSREF_GET(config), sizeof(struct config));
|
2000-01-16 17:40:26 +00:00
|
|
|
init_list(&c->protos);
|
|
|
|
init_list(&c->tables);
|
2022-09-14 23:38:18 +00:00
|
|
|
init_list(&c->mpls_domains);
|
2021-10-19 23:51:28 +00:00
|
|
|
init_list(&c->symbols);
|
2024-06-13 10:15:38 +00:00
|
|
|
obstacle_target_init(&c->obstacles, &c->obstacles_cleared, c->pool, "Config");
|
2024-06-24 09:10:07 +00:00
|
|
|
c->cli = (struct cli_config_list) {};
|
2021-10-19 23:51:28 +00:00
|
|
|
memset(c->def_tables, 0, sizeof(c->def_tables));
|
2000-01-16 17:40:26 +00:00
|
|
|
c->shutdown = 1;
|
2019-06-18 14:27:21 +00:00
|
|
|
c->gr_down = gr;
|
2012-12-26 11:40:48 +00:00
|
|
|
|
2024-06-13 10:15:38 +00:00
|
|
|
CONFIG_REF_LOCAL(cr, c);
|
|
|
|
config_commit(&cr, RECONFIG_HARD, 0);
|
2000-01-16 17:40:26 +00:00
|
|
|
shutting_down = 1;
|
|
|
|
}
|
|
|
|
|
2000-06-03 18:23:00 +00:00
|
|
|
/**
|
|
|
|
* cf_error - report a configuration error
|
|
|
|
* @msg: printf-like format string
|
|
|
|
*
|
|
|
|
* cf_error() can be called during execution of config_parse(), that is
|
|
|
|
* from the parser, a preconfig hook or a postconfig hook, to report an
|
|
|
|
* error in the configuration.
|
|
|
|
*/
|
1999-02-05 21:37:34 +00:00
|
|
|
void
|
2016-03-09 15:48:28 +00:00
|
|
|
cf_error(const char *msg, ...)
|
1999-02-05 21:37:34 +00:00
|
|
|
{
|
|
|
|
char buf[1024];
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, msg);
|
|
|
|
if (bvsnprintf(buf, sizeof(buf), msg, args) < 0)
|
|
|
|
strcpy(buf, "<bug: error message too long>");
|
2016-03-29 08:37:31 +00:00
|
|
|
va_end(args);
|
1999-02-05 21:37:34 +00:00
|
|
|
new_config->err_msg = cfg_strdup(buf);
|
2012-07-18 17:29:33 +00:00
|
|
|
new_config->err_lino = ifs->lino;
|
2018-09-11 14:55:41 +00:00
|
|
|
new_config->err_chno = ifs->chno - ifs->toklen + 1;
|
2012-07-18 17:29:33 +00:00
|
|
|
new_config->err_file_name = ifs->file_name;
|
2014-03-25 13:58:00 +00:00
|
|
|
cf_lex_unwind();
|
1999-02-05 21:37:34 +00:00
|
|
|
longjmp(conf_jmpbuf, 1);
|
|
|
|
}
|
|
|
|
|
2000-06-03 18:23:00 +00:00
|
|
|
/**
|
|
|
|
* cfg_strdup - copy a string to config memory
|
|
|
|
* @c: string to copy
|
|
|
|
*
|
|
|
|
* cfg_strdup() creates a new copy of the string in the memory
|
|
|
|
* pool associated with the configuration being currently parsed.
|
|
|
|
* It's often used when a string literal occurs in the configuration
|
|
|
|
* and we want to preserve it for further use.
|
|
|
|
*/
|
1999-02-05 21:37:34 +00:00
|
|
|
char *
|
2016-11-08 16:46:29 +00:00
|
|
|
cfg_strdup(const char *c)
|
1999-02-05 21:37:34 +00:00
|
|
|
{
|
|
|
|
int l = strlen(c) + 1;
|
|
|
|
char *z = cfg_allocu(l);
|
|
|
|
memcpy(z, c, l);
|
|
|
|
return z;
|
|
|
|
}
|
2011-11-06 23:31:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
cfg_copy_list(list *dest, list *src, unsigned node_size)
|
|
|
|
{
|
|
|
|
node *dn, *sn;
|
|
|
|
|
|
|
|
init_list(dest);
|
|
|
|
WALK_LIST(sn, *src)
|
|
|
|
{
|
|
|
|
dn = cfg_alloc(node_size);
|
|
|
|
memcpy(dn, sn, node_size);
|
2020-11-24 02:42:23 +00:00
|
|
|
memset(dn, 0, sizeof(node));
|
2011-11-06 23:31:23 +00:00
|
|
|
add_tail(dest, dn);
|
|
|
|
}
|
|
|
|
}
|