First attempt on dynamic reconfiguration. There are still lots of bugs

and problems to solve, but the hardest part works.
This commit is contained in:
Martin Mares 2000-01-16 16:44:50 +00:00
parent 394aec8fdd
commit 50fe90edf3
13 changed files with 434 additions and 90 deletions

17
TODO
View file

@ -26,19 +26,21 @@ Core
- config: executable config files
- config: when parsing prefix, check zero bits
- config: reconfiguration
- config: reconfiguration of filters
- config: useless rules when protocols disabled
- config: remove protocol startup priority hacks?
- config: better datetime format
- config: treat shutdown as reconfiguration to null config? (what about config of logging etc. ?)
- config: fix auto_router_id
- krt: rescan interfaces when route addition fails?
- krt: does PERSIST mode have any sense if kernel syncer is shut down as last?
- krt: check behaviour wrt. reconfiguration of routing tables
- tagging of external routes?
- io: use poll if available
- port to FreeBSD
Commands
~~~~~~~~
shutdown # order system shutdown
@ -50,6 +52,17 @@ show <name> # show everything you know about symbol <name>
symbols
(disable|enable|restart) <protocol> # or ALL?
- showing of routing table as seen by given protocol
- showing of deleted routing tables and filters
Roadmap
~~~~~~~
- Dynamic reconfiguration
- Allocators and data structures
- Client
- Remaining bits of IPv6 support (radvd)
- RIPv6
- BGP?
- Logging and debugging messages
Client
~~~~~~

View file

@ -1,7 +1,7 @@
/*
* BIRD Internet Routing Daemon -- Configuration File Handling
*
* (c) 1999 Martin Mares <mj@ucw.cz>
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -9,18 +9,22 @@
#include <setjmp.h>
#include <stdarg.h>
#define LOCAL_DEBUG
#include "nest/bird.h"
#include "nest/route.h"
#include "nest/protocol.h"
#include "nest/iface.h"
#include "lib/resource.h"
#include "lib/string.h"
#include "lib/event.h"
#include "conf/conf.h"
#include "filter/filter.h"
static jmp_buf conf_jmpbuf;
struct config *config, *new_config;
struct config *config, *new_config, *old_config, *future_config;
static event *config_event;
struct config *
config_alloc(byte *name)
@ -77,12 +81,117 @@ config_free(struct config *c)
}
void
config_add_obstacle(struct config *c)
{
DBG("+++ adding obstacle %d\n", c->obstacle_count);
c->obstacle_count++;
}
void
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);
}
}
static int
global_commit(struct config *c, struct config *old)
{
if (!old)
return 0;
if (c->router_id != old->router_id)
return 1;
return 0;
}
static int
config_do_commit(struct config *c)
{
int force_restart, nobs;
DBG("do_commit\n");
old_config = config;
config = c;
if (old_config)
old_config->obstacle_count++;
DBG("sysdep_commit\n");
force_restart = sysdep_commit(c, old_config);
DBG("global_commit\n");
force_restart |= global_commit(c, old_config);
DBG("rt_commit\n");
rt_commit(c, old_config);
DBG("protos_commit\n");
protos_commit(c, old_config, force_restart);
new_config = NULL; /* Just to be sure nobody uses that now */
if (old_config)
nobs = --old_config->obstacle_count;
else
nobs = 0;
DBG("do_commit finished with %d obstacles remaining\n", nobs);
return !nobs;
}
static int
config_done(void *unused)
{
struct config *c;
DBG("config_done\n");
for(;;)
{
log(L_INFO "Reconfigured");
if (old_config)
{
config_free(old_config);
old_config = NULL;
}
if (!future_config)
break;
c = future_config;
future_config = NULL;
log(L_INFO "Switching to queued configuration...");
if (!config_do_commit(c))
break;
}
return 0;
}
int
config_commit(struct config *c)
{
config = c;
sysdep_commit(c);
rt_commit(c);
protos_commit(c);
if (!config) /* First-time configuration */
{
config_do_commit(c);
return CONF_DONE;
}
if (old_config) /* Reconfiguration already in progress */
{
if (future_config)
{
log(L_INFO "Queueing new configuration, ignoring the one already queued");
config_free(future_config);
}
else
log(L_INFO "Queued new configuration");
future_config = c;
return CONF_QUEUED;
}
if (config_do_commit(c))
{
config_done(NULL);
return CONF_DONE;
}
if (!config_event)
{
config_event = ev_new(&root_pool);
config_event->hook = config_done;
}
return CONF_PROGRESS;
}
void

View file

@ -1,7 +1,7 @@
/*
* BIRD Internet Routing Daemon -- Configuration File Handling
*
* (c) 1998--1999 Martin Mares <mj@ucw.cz>
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -26,17 +26,27 @@ struct config {
char *file_name; /* Name of configuration file */
struct symbol **sym_hash; /* Lexer: symbol hash table */
struct symbol **sym_fallback; /* Lexer: fallback symbol hash table */
int obstacle_count; /* Number of items blocking freeing of this config */
};
extern struct config *config, *new_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 */
struct config *config_alloc(byte *name);
int config_parse(struct config *);
int cli_parse(struct config *);
void config_free(struct config *);
void config_commit(struct config *);
int config_commit(struct config *);
void cf_error(char *msg, ...) NORET;
void config_add_obstacle(struct config *);
void config_del_obstacle(struct config *);
#define CONF_DONE 0
#define CONF_PROGRESS 1
#define CONF_QUEUED 2
/* Pools */
@ -87,6 +97,6 @@ int cf_parse(void);
/* Sysdep hooks */
void sysdep_preconfig(struct config *);
void sysdep_commit(struct config *);
int sysdep_commit(struct config *, struct config *);
#endif

View file

@ -10,6 +10,7 @@ Reply codes of BIRD command-line interface
0000 OK
0001 Welcome
0002 Reading configuration
1000 BIRD version
1001 Interface list
@ -24,6 +25,7 @@ Reply codes of BIRD command-line interface
8000 Reply too long
8001 Route not found
8002 Configuration file error
9000 Command too long
9001 Parse error

View file

@ -1,7 +1,7 @@
/*
* BIRD -- Protocols
*
* (c) 1998--1999 Martin Mares <mj@ucw.cz>
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -38,6 +38,7 @@ static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
static char *c_states[] = { "HUNGRY", "FEEDING", "HAPPY", "FLUSHING" };
static int proto_flush_all(void *);
static void proto_rethink_goal(struct proto *p);
static void
proto_enqueue(list *l, struct proto *p)
@ -103,6 +104,7 @@ proto_init_instance(struct proto *p)
p->pool = rp_new(proto_pool, p->proto->name);
p->attn = ev_new(p->pool);
p->attn->data = p;
rt_lock_table(p->table);
}
struct announce_hook *
@ -185,19 +187,12 @@ protos_postconfig(struct config *c)
debug("\n");
}
void
protos_commit(struct config *c)
static struct proto *
proto_init(struct proto_config *c)
{
struct proto_config *x;
struct protocol *p;
struct proto *q;
struct protocol *p = c->protocol;
struct proto *q = p->init(c);
debug("Protocol commit:");
WALK_LIST(x, c->protos)
{
debug(" %s", x->name);
p = x->protocol;
q = p->init(x);
q->proto_state = PS_DOWN;
q->core_state = FS_HUNGRY;
proto_enqueue(&initial_proto_list, q);
@ -210,17 +205,100 @@ protos_commit(struct config *c)
*/
if (!q->disabled)
p->startup_counter++;
return q;
}
void
protos_commit(struct config *new, struct config *old, int force_reconfig)
{
struct proto_config *oc, *nc;
struct proto *p, *n;
DBG("protos_commit:\n");
if (old)
{
WALK_LIST(oc, old->protos)
{
struct proto *p = oc->proto;
struct symbol *sym = cf_find_symbol(oc->name);
if (sym && sym->class == SYM_PROTO)
{
/* Found match, let's check if we can smoothly switch to new configuration */
nc = sym->def;
if (!force_reconfig
&& nc->protocol == oc->protocol
&& nc->preference == oc->preference
&& nc->disabled == oc->disabled
&& nc->table->table == oc->table->table
&& nc->in_filter == oc->in_filter
&& nc->out_filter == oc->out_filter
&& p->proto_state != PS_DOWN)
{
/* Generic attributes match, try converting them and then ask the protocol */
p->debug = nc->debug;
if (p->proto->reconfigure(p, nc))
{
DBG("\t%s: same\n", oc->name);
p->cf = nc;
nc->proto = p;
continue;
}
debug("\n");
}
/* Unsuccessful, force reconfig */
DBG("\t%s: power cycling\n", oc->name);
p->cf_new = nc;
}
else
{
DBG("\t%s: deleting\n", oc->name);
p->cf_new = NULL;
}
p->reconfiguring = 1;
config_add_obstacle(old);
proto_rethink_goal(p);
}
}
WALK_LIST(nc, new->protos)
if (!nc->proto)
{
DBG("\t%s: adding\n", nc->name);
proto_init(nc);
}
DBG("\tdone\n");
DBG("Protocol start\n");
WALK_LIST_DELSAFE(p, n, initial_proto_list)
proto_rethink_goal(p);
}
static void
proto_rethink_goal(struct proto *p)
{
struct protocol *q = p->proto;
struct protocol *q;
if (p->reconfiguring && p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN)
{
struct proto_config *nc = p->cf_new;
DBG("%s has shut down for reconfiguration\n", p->name);
config_del_obstacle(p->cf->global);
rem_node(&p->n);
mb_free(p);
if (!nc)
return;
p = proto_init(nc); /* FIXME: What about protocol priorities??? */
}
/* Determine what state we want to reach */
if (p->disabled || shutting_down || p->reconfiguring)
p->core_goal = FS_HUNGRY;
else
p->core_goal = FS_HAPPY;
if (p->core_state == p->core_goal)
return;
q = p->proto;
if (p->core_goal == FS_HAPPY) /* Going up */
{
if (p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN)
@ -242,25 +320,6 @@ proto_rethink_goal(struct proto *p)
}
}
static void
proto_set_goal(struct proto *p, unsigned goal)
{
if (p->disabled || shutting_down)
goal = FS_HUNGRY;
p->core_goal = goal;
proto_rethink_goal(p);
}
void
protos_start(void)
{
struct proto *p, *n;
debug("Protocol start\n");
WALK_LIST_DELSAFE(p, n, initial_proto_list)
proto_set_goal(p, FS_HAPPY);
}
void
protos_shutdown(void)
{
@ -271,12 +330,12 @@ protos_shutdown(void)
if (p->core_state != FS_HUNGRY || p->proto_state != PS_DOWN)
{
proto_shutdown_counter++;
proto_set_goal(p, FS_HUNGRY);
proto_rethink_goal(p);
}
WALK_LIST_BACKWARDS_DELSAFE(p, n, proto_list)
{
proto_shutdown_counter++;
proto_set_goal(p, FS_HUNGRY);
proto_rethink_goal(p);
}
}
@ -329,6 +388,7 @@ static void
proto_fell_down(struct proto *p)
{
DBG("Protocol %s down\n", p->name);
rt_unlock_table(p->table);
if (!--proto_shutdown_counter)
protos_shutdown_notify();
proto_rethink_goal(p);
@ -363,7 +423,10 @@ proto_notify_state(struct proto *p, unsigned ps)
{
case PS_DOWN:
if (cs == FS_HUNGRY) /* Shutdown finished */
{
proto_fell_down(p);
return; /* The protocol might have ceased to exist */
}
else if (cs == FS_FLUSHING) /* Still flushing... */
;
else /* Need to start flushing */

View file

@ -41,7 +41,7 @@ struct protocol {
void (*preconfig)(struct protocol *, struct config *); /* Just before configuring */
void (*postconfig)(struct proto_config *); /* After configuring each instance */
struct proto * (*init)(struct proto_config *); /* Create new instance */
int (*reconfigure)(struct proto *, struct proto_config *); /* Try to reconfigure instance */
int (*reconfigure)(struct proto *, struct proto_config *); /* Try to reconfigure instance, returns success */
void (*dump)(struct proto *); /* Debugging dump */
void (*dump_attrs)(struct rte *); /* Dump protocol-dependent attributes */
int (*start)(struct proto *); /* Start the instance */
@ -54,8 +54,7 @@ struct protocol {
void protos_build(void);
void protos_preconfig(struct config *);
void protos_postconfig(struct config *);
void protos_commit(struct config *);
void protos_start(void);
void protos_commit(struct config *new, struct config *old, int force_restart);
void protos_dump_all(void);
void protos_shutdown(void);
@ -92,6 +91,7 @@ struct proto {
node n;
struct protocol *proto; /* Protocol */
struct proto_config *cf; /* Configuration data */
struct proto_config *cf_new; /* Configuration we want to switch to after shutdown (NULL=delete) */
pool *pool; /* Pool containing local objects */
struct event *attn; /* "Pay attention" event */
@ -103,6 +103,7 @@ struct proto {
unsigned proto_state; /* Protocol state machine (see below) */
unsigned core_state; /* Core state machine (see below) */
unsigned core_goal; /* State we want to reach (see below) */
unsigned reconfiguring; /* We're shutting down due to reconfiguration */
bird_clock_t last_state_change; /* Time of last state transition */
/*

View file

@ -1,7 +1,7 @@
/*
* BIRD Internet Routing Daemon -- Routing Table
*
* (c) 1998--1999 Martin Mares <mj@ucw.cz>
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -121,6 +121,11 @@ typedef struct rtable {
char *name; /* Name of this table */
list hooks; /* List of announcement hooks */
int pipe_busy; /* Pipe loop detection */
int use_count; /* Number of protocols using this table */
struct config *deleted; /* Table doesn't exist in current configuration,
* delete as soon as use_count becomes 0 and remove
* obstacle from this routing table.
*/
} rtable;
typedef struct network {
@ -171,7 +176,9 @@ struct config;
void rt_init(void);
void rt_preconfig(struct config *);
void rt_commit(struct config *);
void rt_commit(struct config *new, struct config *old);
void rt_lock_table(rtable *);
void rt_unlock_table(rtable *);
void rt_setup(pool *, rtable *, char *);
static inline net *net_find(rtable *tab, ip_addr addr, unsigned len) { return (net *) fib_find(&tab->fib, &addr, len); }
static inline net *net_get(rtable *tab, ip_addr addr, unsigned len) { return (net *) fib_get(&tab->fib, &addr, len); }

View file

@ -1,7 +1,7 @@
/*
* BIRD -- Routing Table
*
* (c) 1998--1999 Martin Mares <mj@ucw.cz>
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -480,17 +480,68 @@ rt_preconfig(struct config *c)
}
void
rt_commit(struct config *c)
rt_lock_table(rtable *r)
{
struct rtable_config *r;
r->use_count++;
}
WALK_LIST(r, c->tables)
void
rt_unlock_table(rtable *r)
{
if (!--r->use_count && r->deleted)
{
struct config *conf = r->deleted;
DBG("Deleting routing table %s\n", r->name);
rem_node(&r->n);
fib_free(&r->fib);
mb_free(r);
config_del_obstacle(conf);
}
}
void
rt_commit(struct config *new, struct config *old)
{
struct rtable_config *o, *r;
DBG("rt_commit:\n");
if (old)
{
WALK_LIST(o, old->tables)
{
rtable *ot = o->table;
if (!ot->deleted)
{
struct symbol *sym = cf_find_symbol(o->name);
if (sym && sym->class == SYM_TABLE)
{
DBG("\t%s: same\n", o->name);
r = sym->def;
r->table = ot;
ot->name = r->name;
}
else
{
DBG("\t%s: deleted", o->name);
ot->deleted = old;
config_add_obstacle(old);
rt_lock_table(ot);
rt_unlock_table(ot);
}
}
}
}
WALK_LIST(r, new->tables)
if (!r->table)
{
rtable *t = mb_alloc(rt_table_pool, sizeof(struct rtable));
DBG("\t%s: created\n", r->name);
rt_setup(rt_table_pool, t, r->name);
add_tail(&routing_tables, &t->n);
r->table = t;
}
DBG("\tdone\n");
}
/*

View file

@ -1,7 +1,7 @@
/*
* BIRD -- Table-to-Table Routing Protocol a.k.a Pipe
*
* (c) 1999 Martin Mares <mj@ucw.cz>
* (c) 1999--2000 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -106,10 +106,20 @@ pipe_start(struct proto *P)
*/
a = proto_add_announce_hook(P, p->peer);
a->proto = &ph->p;
rt_lock_table(p->peer);
return PS_UP;
}
static int
pipe_shutdown(struct proto *P)
{
struct pipe_proto *p = (struct pipe_proto *) P;
rt_unlock_table(p->peer);
return PS_DOWN;
}
static struct proto *
pipe_init(struct proto_config *C)
{
@ -147,5 +157,6 @@ struct protocol proto_pipe = {
postconfig: pipe_postconfig,
init: pipe_init,
start: pipe_start,
shutdown: pipe_shutdown,
get_status: pipe_get_status,
};

View file

@ -1,7 +1,7 @@
/*
* BIRD -- UNIX Configuration
*
* (c) 1999 Martin Mares <mj@ucw.cz>
* (c) 1999--2000 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -16,6 +16,7 @@ CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH,
%type <i> log_mask log_mask_list log_cat
%type <g> log_file
%type <t> cfg_name
CF_GRAMMAR
@ -61,6 +62,16 @@ log_cat:
| BUG { $$ = L_BUG[0]; }
;
/* Unix specific commands */
CF_CLI(CONFIGURE, cfg_name, [<file>], [[Reload configuration]])
{ cmd_reconfig($2); } ;
cfg_name:
/* empty */ { $$ = NULL; }
| TEXT
;
CF_CODE
CF_END

View file

@ -1,7 +1,7 @@
/*
* BIRD -- UNIX Kernel Synchronization
*
* (c) 1998--1999 Martin Mares <mj@ucw.cz>
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -67,6 +67,12 @@ static struct kif_proto *kif_proto;
static timer *kif_scan_timer;
static bird_clock_t kif_last_shot;
static void
kif_preconfig(struct protocol *P, struct config *c)
{
cf_kif = NULL;
}
static void
kif_scan(timer *t)
{
@ -137,6 +143,7 @@ kif_shutdown(struct proto *P)
struct protocol proto_unix_iface = {
name: "Device",
priority: 100,
preconfig: kif_preconfig,
init: kif_init,
start: kif_start,
shutdown: kif_shutdown,
@ -646,6 +653,7 @@ struct proto_config *cf_krt;
static void
krt_preconfig(struct protocol *P, struct config *c)
{
cf_krt = NULL;
krt_scan_preconfig(c);
}

View file

@ -1,7 +1,7 @@
/*
* BIRD Internet Routing Daemon -- Unix Entry Point
*
* (c) 1998--1999 Martin Mares <mj@ucw.cz>
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -74,30 +74,89 @@ sysdep_preconfig(struct config *c)
init_list(&c->logfiles);
}
void
sysdep_commit(struct config *c)
int
sysdep_commit(struct config *new, struct config *old)
{
log_switch(&c->logfiles);
log_switch(&new->logfiles);
return 0;
}
static int
unix_read_config(struct config **cp, char *name)
{
struct config *conf = config_alloc(name);
*cp = conf;
conf_fd = open(name, O_RDONLY);
if (conf_fd < 0)
return 0;
cf_read_hook = cf_read;
return config_parse(conf);
}
static void
read_config(void)
{
struct config *conf = config_alloc(config_name);
struct config *conf;
conf_fd = open(config_name, O_RDONLY);
if (conf_fd < 0)
die("Unable to open configuration file %s: %m", config_name);
cf_read_hook = cf_read;
if (!config_parse(conf))
if (!unix_read_config(&conf, config_name))
{
if (conf->err_msg)
die("%s, line %d: %s", config_name, conf->err_lino, conf->err_msg);
else
die("Unable to open configuration file %s: %m", config_name);
}
config_commit(conf);
}
void
async_config(void)
{
debug("Asynchronous reconfigurations are not supported in demo version\n");
struct config *conf;
log(L_INFO "Reconfiguration requested by SIGHUP");
if (!unix_read_config(&conf, config_name))
{
if (conf->err_msg)
log(L_ERR "%s, line %d: %s", config_name, conf->err_lino, conf->err_msg);
else
log(L_ERR "Unable to open configuration file %s: %m", config_name);
config_free(conf);
}
else
config_commit(conf);
}
void
cmd_reconfig(char *name)
{
struct config *conf;
if (!name)
name = config_name;
cli_msg(-2, "Reading configuration from %s", name);
if (!unix_read_config(&conf, name))
{
if (conf->err_msg)
cli_msg(8002, "%s, line %d: %s", name, conf->err_lino, conf->err_msg);
else
cli_msg(8002, "%s: %m", name);
config_free(conf);
}
else
{
switch (config_commit(conf))
{
case CONF_DONE:
cli_msg(3, "Reconfigured.");
break;
case CONF_PROGRESS:
cli_msg(4, "Reconfiguration in progress.");
break;
default:
cli_msg(5, "Reconfiguration already in progress, queueing new config");
}
}
}
/*
@ -350,8 +409,6 @@ main(int argc, char **argv)
cli_init_unix();
protos_start();
ev_run_list(&global_event_list);
async_dump();

View file

@ -1,7 +1,7 @@
/*
* BIRD -- Declarations Common to Unix Port
*
* (c) 1998 Martin Mares <mj@ucw.cz>
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -16,6 +16,7 @@ struct pool;
void async_config(void);
void async_dump(void);
void async_shutdown(void);
void cmd_reconfig(char *name);
/* io.c */