Commit 50fe90ed authored by Martin Mareš's avatar Martin Mareš

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

and problems to solve, but the hardest part works.
parent 394aec8f
......@@ -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
~~~~~~
......
/*
* 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_commit(struct config *c)
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;
sysdep_commit(c);
rt_commit(c);
protos_commit(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)
{
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
......
/*
* 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
......@@ -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
......
/*
* 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,42 +187,118 @@ protos_postconfig(struct config *c)
debug("\n");
}
static struct proto *
proto_init(struct proto_config *c)
{
struct protocol *p = c->protocol;
struct proto *q = p->init(c);
q->proto_state = PS_DOWN;
q->core_state = FS_HUNGRY;
proto_enqueue(&initial_proto_list, q);
/*
* HACK ALERT! In case of multiple kernel routing tables,
* the kernel syncer acts as multiple protocols which cooperate
* with each other. In order to speed up their initialization,
* we need to know when we're initializing the last one, hence
* the startup counter.
*/
if (!q->disabled)
p->startup_counter++;
return q;
}
void
protos_commit(struct config *c)
protos_commit(struct config *new, struct config *old, int force_reconfig)
{
struct proto_config *x;
struct protocol *p;
struct proto *q;
struct proto_config *oc, *nc;
struct proto *p, *n;
debug("Protocol commit:");
WALK_LIST(x, c->protos)
DBG("protos_commit:\n");
if (old)
{
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);
/*
* HACK ALERT! In case of multiple kernel routing tables,
* the kernel syncer acts as multiple protocols which cooperate
* with each other. In order to speed up their initialization,
* we need to know when we're initializing the last one, hence
* the startup counter.
*/
if (!q->disabled)
p->startup_counter++;
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;
}
}
/* 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);
}
}
debug("\n");
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);
{
proto_fell_down(p);
return; /* The protocol might have ceased to exist */
}
else if (cs == FS_FLUSHING) /* Still flushing... */
;
else /* Need to start flushing */
......
......@@ -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 */
/*
......
/*
* 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); }
......
/*
* 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++;
}
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;
WALK_LIST(r, c->tables)
DBG("rt_commit:\n");
if (old)
{
rtable *t = mb_alloc(rt_table_pool, sizeof(struct rtable));
rt_setup(rt_table_pool, t, r->name);
add_tail(&routing_tables, &t->n);
r->table = t;
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");
}
/*
......
/*
* 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,
};
/*
* 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
/*
* 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);
}
......
/*
* 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 void
read_config(void)
static int
unix_read_config(struct config **cp, char *name)