iof-bird-daemon / conf / conf.c @ 574b2324
History | View | Annotate | Download (14.8 KB)
1 |
/*
|
---|---|
2 |
* BIRD Internet Routing Daemon -- Configuration File Handling
|
3 |
*
|
4 |
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
|
5 |
*
|
6 |
* Can be freely distributed and used under the terms of the GNU GPL.
|
7 |
*/
|
8 |
|
9 |
/**
|
10 |
* DOC: Configuration manager
|
11 |
*
|
12 |
* Configuration of BIRD is complex, yet straightforward. There are three
|
13 |
* modules taking care of the configuration: config manager (which takes care
|
14 |
* of storage of the config information and controls switching between configs),
|
15 |
* lexical analyzer and parser.
|
16 |
*
|
17 |
* The configuration manager stores each config as a &config structure
|
18 |
* accompanied by a linear pool from which all information associated
|
19 |
* with the config and pointed to by the &config structure is allocated.
|
20 |
*
|
21 |
* There can exist up to four different configurations at one time: an active
|
22 |
* one (pointed to by @config), configuration we are just switching from
|
23 |
* (@old_config), one queued for the next reconfiguration (@future_config; if
|
24 |
* there is one and the user wants to reconfigure once again, we just free the
|
25 |
* previous queued config and replace it with the new one) and finally a config
|
26 |
* being parsed (@new_config). The stored @old_config is also used for undo
|
27 |
* reconfiguration, which works in a similar way. Reconfiguration could also
|
28 |
* have timeout (using @config_timer) and undo is automatically called if the
|
29 |
* new configuration is not confirmed later. The new config (@new_config) and
|
30 |
* associated linear pool (@cfg_mem) is non-NULL only during parsing.
|
31 |
*
|
32 |
* Loading of new configuration is very simple: just call config_alloc() to get
|
33 |
* a new &config structure, then use config_parse() to parse a configuration
|
34 |
* file and fill all fields of the structure and finally ask the config manager
|
35 |
* to switch to the new config by calling config_commit().
|
36 |
*
|
37 |
* CLI commands are parsed in a very similar way -- there is also a stripped-down
|
38 |
* &config structure associated with them and they are lex-ed and parsed by the
|
39 |
* same functions, only a special fake token is prepended before the command
|
40 |
* text to make the parser recognize only the rules corresponding to CLI commands.
|
41 |
*/
|
42 |
|
43 |
#include <setjmp.h> |
44 |
#include <stdarg.h> |
45 |
|
46 |
#undef LOCAL_DEBUG
|
47 |
|
48 |
#include "nest/bird.h" |
49 |
#include "nest/route.h" |
50 |
#include "nest/protocol.h" |
51 |
#include "nest/iface.h" |
52 |
#include "lib/resource.h" |
53 |
#include "lib/string.h" |
54 |
#include "lib/event.h" |
55 |
#include "sysdep/unix/timer.h" |
56 |
#include "conf/conf.h" |
57 |
#include "filter/filter.h" |
58 |
|
59 |
|
60 |
static jmp_buf conf_jmpbuf;
|
61 |
|
62 |
struct config *config, *new_config;
|
63 |
|
64 |
static struct config *old_config; /* Old configuration */ |
65 |
static struct config *future_config; /* New config held here if recon requested during recon */ |
66 |
static int old_cftype; /* Type of transition old_config -> config (RECONFIG_SOFT/HARD) */ |
67 |
static int future_cftype; /* Type of scheduled transition, may also be RECONFIG_UNDO */ |
68 |
/* Note that when future_cftype is RECONFIG_UNDO, then future_config is NULL,
|
69 |
therefore proper check for future scheduled config checks future_cftype */
|
70 |
|
71 |
static event *config_event; /* Event for finalizing reconfiguration */ |
72 |
static timer *config_timer; /* Timer for scheduled configuration rollback */ |
73 |
|
74 |
/* These are public just for cmd_show_status(), should not be accessed elsewhere */
|
75 |
int shutting_down; /* Shutdown requested, do not accept new config changes */ |
76 |
int configuring; /* Reconfiguration is running */ |
77 |
int undo_available; /* Undo was not requested from last reconfiguration */ |
78 |
/* Note that both shutting_down and undo_available are related to requests, not processing */
|
79 |
|
80 |
/**
|
81 |
* config_alloc - allocate a new configuration
|
82 |
* @name: name of the config
|
83 |
*
|
84 |
* This function creates new &config structure, attaches a resource
|
85 |
* pool and a linear memory pool to it and makes it available for
|
86 |
* further use. Returns a pointer to the structure.
|
87 |
*/
|
88 |
struct config *
|
89 |
config_alloc(const char *name) |
90 |
{ |
91 |
pool *p = rp_new(&root_pool, "Config");
|
92 |
linpool *l = lp_new_default(p); |
93 |
struct config *c = lp_allocz(l, sizeof(struct config)); |
94 |
|
95 |
/* Duplication of name string in local linear pool */
|
96 |
uint nlen = strlen(name) + 1;
|
97 |
char *ndup = lp_allocu(l, nlen);
|
98 |
memcpy(ndup, name, nlen); |
99 |
|
100 |
init_list(&c->tests); |
101 |
c->mrtdump_file = -1; /* Hack, this should be sysdep-specific */ |
102 |
c->pool = p; |
103 |
c->mem = l; |
104 |
c->file_name = ndup; |
105 |
c->load_time = current_time(); |
106 |
c->tf_route = c->tf_proto = TM_ISO_SHORT_MS; |
107 |
c->tf_base = c->tf_log = TM_ISO_LONG_MS; |
108 |
c->gr_wait = DEFAULT_GR_WAIT; |
109 |
|
110 |
return c;
|
111 |
} |
112 |
|
113 |
/**
|
114 |
* config_parse - parse a configuration
|
115 |
* @c: configuration
|
116 |
*
|
117 |
* config_parse() reads input by calling a hook function pointed to
|
118 |
* by @cf_read_hook and parses it according to the configuration
|
119 |
* grammar. It also calls all the preconfig and postconfig hooks
|
120 |
* before, resp. after parsing.
|
121 |
*
|
122 |
* Result: 1 if the config has been parsed successfully, 0 if any
|
123 |
* error has occurred (such as anybody calling cf_error()) and
|
124 |
* the @err_msg field has been set to the error message.
|
125 |
*/
|
126 |
int
|
127 |
config_parse(struct config *c)
|
128 |
{ |
129 |
int done = 0; |
130 |
DBG("Parsing configuration file `%s'\n", c->file_name);
|
131 |
new_config = c; |
132 |
cfg_mem = c->mem; |
133 |
if (setjmp(conf_jmpbuf))
|
134 |
goto cleanup;
|
135 |
|
136 |
cf_lex_init(0, c);
|
137 |
sysdep_preconfig(c); |
138 |
protos_preconfig(c); |
139 |
rt_preconfig(c); |
140 |
cf_parse(); |
141 |
|
142 |
if (EMPTY_LIST(c->protos))
|
143 |
cf_error("No protocol is specified in the config file");
|
144 |
|
145 |
/*
|
146 |
if (!c->router_id)
|
147 |
cf_error("Router ID must be configured manually");
|
148 |
*/
|
149 |
|
150 |
done = 1;
|
151 |
|
152 |
cleanup:
|
153 |
new_config = NULL;
|
154 |
cfg_mem = NULL;
|
155 |
return done;
|
156 |
} |
157 |
|
158 |
/**
|
159 |
* cli_parse - parse a CLI command
|
160 |
* @c: temporary config structure
|
161 |
*
|
162 |
* cli_parse() is similar to config_parse(), but instead of a configuration,
|
163 |
* it parses a CLI command. See the CLI module for more information.
|
164 |
*/
|
165 |
int
|
166 |
cli_parse(struct config *c)
|
167 |
{ |
168 |
int done = 0; |
169 |
c->fallback = config; |
170 |
new_config = c; |
171 |
cfg_mem = c->mem; |
172 |
if (setjmp(conf_jmpbuf))
|
173 |
goto cleanup;
|
174 |
|
175 |
cf_lex_init(1, c);
|
176 |
cf_parse(); |
177 |
done = 1;
|
178 |
|
179 |
cleanup:
|
180 |
c->fallback = NULL;
|
181 |
new_config = NULL;
|
182 |
cfg_mem = NULL;
|
183 |
return done;
|
184 |
} |
185 |
|
186 |
/**
|
187 |
* config_free - free a configuration
|
188 |
* @c: configuration to be freed
|
189 |
*
|
190 |
* This function takes a &config structure and frees all resources
|
191 |
* associated with it.
|
192 |
*/
|
193 |
void
|
194 |
config_free(struct config *c)
|
195 |
{ |
196 |
if (c)
|
197 |
rfree(c->pool); |
198 |
} |
199 |
|
200 |
void
|
201 |
config_add_obstacle(struct config *c)
|
202 |
{ |
203 |
DBG("+++ adding obstacle %d\n", c->obstacle_count);
|
204 |
c->obstacle_count++; |
205 |
} |
206 |
|
207 |
void
|
208 |
config_del_obstacle(struct config *c)
|
209 |
{ |
210 |
DBG("+++ deleting obstacle %d\n", c->obstacle_count);
|
211 |
c->obstacle_count--; |
212 |
if (!c->obstacle_count)
|
213 |
ev_schedule(config_event); |
214 |
} |
215 |
|
216 |
static int |
217 |
global_commit(struct config *new, struct config *old) |
218 |
{ |
219 |
if (!old)
|
220 |
return 0; |
221 |
|
222 |
if (!ipa_equal(old->listen_bgp_addr, new->listen_bgp_addr) ||
|
223 |
(old->listen_bgp_port != new->listen_bgp_port) || |
224 |
(old->listen_bgp_flags != new->listen_bgp_flags)) |
225 |
log(L_WARN "Reconfiguration of BGP listening socket not implemented, please restart BIRD.");
|
226 |
|
227 |
if (!new->router_id)
|
228 |
{ |
229 |
new->router_id = old->router_id; |
230 |
|
231 |
if (new->router_id_from)
|
232 |
{ |
233 |
u32 id = if_choose_router_id(new->router_id_from, old->router_id); |
234 |
if (!id)
|
235 |
log(L_WARN "Cannot determine router ID, using old one");
|
236 |
else
|
237 |
new->router_id = id; |
238 |
} |
239 |
} |
240 |
|
241 |
return 0; |
242 |
} |
243 |
|
244 |
static int |
245 |
config_do_commit(struct config *c, int type) |
246 |
{ |
247 |
if (type == RECONFIG_UNDO)
|
248 |
{ |
249 |
c = old_config; |
250 |
type = old_cftype; |
251 |
} |
252 |
else
|
253 |
config_free(old_config); |
254 |
|
255 |
old_config = config; |
256 |
old_cftype = type; |
257 |
config = c; |
258 |
|
259 |
configuring = 1;
|
260 |
if (old_config && !config->shutdown)
|
261 |
log(L_INFO "Reconfiguring");
|
262 |
|
263 |
if (old_config)
|
264 |
old_config->obstacle_count++; |
265 |
|
266 |
DBG("sysdep_commit\n");
|
267 |
int force_restart = sysdep_commit(c, old_config);
|
268 |
DBG("global_commit\n");
|
269 |
force_restart |= global_commit(c, old_config); |
270 |
DBG("rt_commit\n");
|
271 |
rt_commit(c, old_config); |
272 |
DBG("protos_commit\n");
|
273 |
protos_commit(c, old_config, force_restart, type); |
274 |
|
275 |
int obs = 0; |
276 |
if (old_config)
|
277 |
obs = --old_config->obstacle_count; |
278 |
|
279 |
DBG("do_commit finished with %d obstacles remaining\n", obs);
|
280 |
return !obs;
|
281 |
} |
282 |
|
283 |
static void |
284 |
config_done(void *unused UNUSED)
|
285 |
{ |
286 |
if (config->shutdown)
|
287 |
sysdep_shutdown_done(); |
288 |
|
289 |
configuring = 0;
|
290 |
if (old_config)
|
291 |
log(L_INFO "Reconfigured");
|
292 |
|
293 |
if (future_cftype)
|
294 |
{ |
295 |
int type = future_cftype;
|
296 |
struct config *conf = future_config;
|
297 |
future_cftype = RECONFIG_NONE; |
298 |
future_config = NULL;
|
299 |
|
300 |
log(L_INFO "Reconfiguring to queued configuration");
|
301 |
if (config_do_commit(conf, type))
|
302 |
config_done(NULL);
|
303 |
} |
304 |
} |
305 |
|
306 |
/**
|
307 |
* config_commit - commit a configuration
|
308 |
* @c: new configuration
|
309 |
* @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD)
|
310 |
* @timeout: timeout for undo (in seconds; or 0 for no timeout)
|
311 |
*
|
312 |
* When a configuration is parsed and prepared for use, the
|
313 |
* config_commit() function starts the process of reconfiguration.
|
314 |
* It checks whether there is already a reconfiguration in progress
|
315 |
* in which case it just queues the new config for later processing.
|
316 |
* Else it notifies all modules about the new configuration by calling
|
317 |
* their commit() functions which can either accept it immediately
|
318 |
* or call config_add_obstacle() to report that they need some time
|
319 |
* to complete the reconfiguration. After all such obstacles are removed
|
320 |
* using config_del_obstacle(), the old configuration is freed and
|
321 |
* everything runs according to the new one.
|
322 |
*
|
323 |
* When @timeout is nonzero, the undo timer is activated with given
|
324 |
* timeout. The timer is deactivated when config_commit(),
|
325 |
* config_confirm() or config_undo() is called.
|
326 |
*
|
327 |
* Result: %CONF_DONE if the configuration has been accepted immediately,
|
328 |
* %CONF_PROGRESS if it will take some time to switch to it, %CONF_QUEUED
|
329 |
* if it's been queued due to another reconfiguration being in progress now
|
330 |
* or %CONF_SHUTDOWN if BIRD is in shutdown mode and no new configurations
|
331 |
* are accepted.
|
332 |
*/
|
333 |
int
|
334 |
config_commit(struct config *c, int type, uint timeout) |
335 |
{ |
336 |
if (shutting_down)
|
337 |
{ |
338 |
config_free(c); |
339 |
return CONF_SHUTDOWN;
|
340 |
} |
341 |
|
342 |
undo_available = 1;
|
343 |
if (timeout)
|
344 |
tm2_start(config_timer, timeout S); |
345 |
else
|
346 |
tm2_stop(config_timer); |
347 |
|
348 |
if (configuring)
|
349 |
{ |
350 |
if (future_cftype)
|
351 |
{ |
352 |
log(L_INFO "Queueing new configuration, ignoring the one already queued");
|
353 |
config_free(future_config); |
354 |
} |
355 |
else
|
356 |
log(L_INFO "Queueing new configuration");
|
357 |
|
358 |
future_cftype = type; |
359 |
future_config = c; |
360 |
return CONF_QUEUED;
|
361 |
} |
362 |
|
363 |
if (config_do_commit(c, type))
|
364 |
{ |
365 |
config_done(NULL);
|
366 |
return CONF_DONE;
|
367 |
} |
368 |
return CONF_PROGRESS;
|
369 |
} |
370 |
|
371 |
/**
|
372 |
* config_confirm - confirm a commited configuration
|
373 |
*
|
374 |
* When the undo timer is activated by config_commit() with nonzero timeout,
|
375 |
* this function can be used to deactivate it and therefore confirm
|
376 |
* the current configuration.
|
377 |
*
|
378 |
* Result: %CONF_CONFIRM when the current configuration is confirmed,
|
379 |
* %CONF_NONE when there is nothing to confirm (i.e. undo timer is not active).
|
380 |
*/
|
381 |
int
|
382 |
config_confirm(void)
|
383 |
{ |
384 |
if (config_timer->expires == 0) |
385 |
return CONF_NOTHING;
|
386 |
|
387 |
tm2_stop(config_timer); |
388 |
|
389 |
return CONF_CONFIRM;
|
390 |
} |
391 |
|
392 |
/**
|
393 |
* config_undo - undo a configuration
|
394 |
*
|
395 |
* Function config_undo() can be used to change the current
|
396 |
* configuration back to stored %old_config. If no reconfiguration is
|
397 |
* running, this stored configuration is commited in the same way as a
|
398 |
* new configuration in config_commit(). If there is already a
|
399 |
* reconfiguration in progress and no next reconfiguration is
|
400 |
* scheduled, then the undo is scheduled for later processing as
|
401 |
* usual, but if another reconfiguration is already scheduled, then
|
402 |
* such reconfiguration is removed instead (i.e. undo is applied on
|
403 |
* the last commit that scheduled it).
|
404 |
*
|
405 |
* Result: %CONF_DONE if the configuration has been accepted immediately,
|
406 |
* %CONF_PROGRESS if it will take some time to switch to it, %CONF_QUEUED
|
407 |
* if it's been queued due to another reconfiguration being in progress now,
|
408 |
* %CONF_UNQUEUED if a scheduled reconfiguration is removed, %CONF_NOTHING
|
409 |
* if there is no relevant configuration to undo (the previous config request
|
410 |
* was config_undo() too) or %CONF_SHUTDOWN if BIRD is in shutdown mode and
|
411 |
* no new configuration changes are accepted.
|
412 |
*/
|
413 |
int
|
414 |
config_undo(void)
|
415 |
{ |
416 |
if (shutting_down)
|
417 |
return CONF_SHUTDOWN;
|
418 |
|
419 |
if (!undo_available || !old_config)
|
420 |
return CONF_NOTHING;
|
421 |
|
422 |
undo_available = 0;
|
423 |
tm2_stop(config_timer); |
424 |
|
425 |
if (configuring)
|
426 |
{ |
427 |
if (future_cftype)
|
428 |
{ |
429 |
config_free(future_config); |
430 |
future_config = NULL;
|
431 |
|
432 |
log(L_INFO "Removing queued configuration");
|
433 |
future_cftype = RECONFIG_NONE; |
434 |
return CONF_UNQUEUED;
|
435 |
} |
436 |
else
|
437 |
{ |
438 |
log(L_INFO "Queueing undo configuration");
|
439 |
future_cftype = RECONFIG_UNDO; |
440 |
return CONF_QUEUED;
|
441 |
} |
442 |
} |
443 |
|
444 |
if (config_do_commit(NULL, RECONFIG_UNDO)) |
445 |
{ |
446 |
config_done(NULL);
|
447 |
return CONF_DONE;
|
448 |
} |
449 |
return CONF_PROGRESS;
|
450 |
} |
451 |
|
452 |
extern void cmd_reconfig_undo_notify(void); |
453 |
|
454 |
static void |
455 |
config_timeout(timer *t UNUSED) |
456 |
{ |
457 |
log(L_INFO "Config timeout expired, starting undo");
|
458 |
cmd_reconfig_undo_notify(); |
459 |
|
460 |
int r = config_undo();
|
461 |
if (r < 0) |
462 |
log(L_ERR "Undo request failed");
|
463 |
} |
464 |
|
465 |
void
|
466 |
config_init(void)
|
467 |
{ |
468 |
config_event = ev_new(&root_pool); |
469 |
config_event->hook = config_done; |
470 |
|
471 |
config_timer = tm2_new(&root_pool); |
472 |
config_timer->hook = config_timeout; |
473 |
} |
474 |
|
475 |
/**
|
476 |
* order_shutdown - order BIRD shutdown
|
477 |
*
|
478 |
* This function initiates shutdown of BIRD. It's accomplished by asking
|
479 |
* for switching to an empty configuration.
|
480 |
*/
|
481 |
void
|
482 |
order_shutdown(void)
|
483 |
{ |
484 |
struct config *c;
|
485 |
|
486 |
if (shutting_down)
|
487 |
return;
|
488 |
|
489 |
log(L_INFO "Shutting down");
|
490 |
c = lp_alloc(config->mem, sizeof(struct config)); |
491 |
memcpy(c, config, sizeof(struct config)); |
492 |
init_list(&c->protos); |
493 |
init_list(&c->tables); |
494 |
c->shutdown = 1;
|
495 |
|
496 |
config_commit(c, RECONFIG_HARD, 0);
|
497 |
shutting_down = 1;
|
498 |
} |
499 |
|
500 |
/**
|
501 |
* cf_error - report a configuration error
|
502 |
* @msg: printf-like format string
|
503 |
*
|
504 |
* cf_error() can be called during execution of config_parse(), that is
|
505 |
* from the parser, a preconfig hook or a postconfig hook, to report an
|
506 |
* error in the configuration.
|
507 |
*/
|
508 |
void
|
509 |
cf_error(const char *msg, ...) |
510 |
{ |
511 |
char buf[1024]; |
512 |
va_list args; |
513 |
|
514 |
va_start(args, msg); |
515 |
if (bvsnprintf(buf, sizeof(buf), msg, args) < 0) |
516 |
strcpy(buf, "<bug: error message too long>");
|
517 |
va_end(args); |
518 |
new_config->err_msg = cfg_strdup(buf); |
519 |
new_config->err_lino = ifs->lino; |
520 |
new_config->err_file_name = ifs->file_name; |
521 |
cf_lex_unwind(); |
522 |
longjmp(conf_jmpbuf, 1);
|
523 |
} |
524 |
|
525 |
/**
|
526 |
* cfg_strdup - copy a string to config memory
|
527 |
* @c: string to copy
|
528 |
*
|
529 |
* cfg_strdup() creates a new copy of the string in the memory
|
530 |
* pool associated with the configuration being currently parsed.
|
531 |
* It's often used when a string literal occurs in the configuration
|
532 |
* and we want to preserve it for further use.
|
533 |
*/
|
534 |
char *
|
535 |
cfg_strdup(const char *c) |
536 |
{ |
537 |
int l = strlen(c) + 1; |
538 |
char *z = cfg_allocu(l);
|
539 |
memcpy(z, c, l); |
540 |
return z;
|
541 |
} |
542 |
|
543 |
|
544 |
void
|
545 |
cfg_copy_list(list *dest, list *src, unsigned node_size)
|
546 |
{ |
547 |
node *dn, *sn; |
548 |
|
549 |
init_list(dest); |
550 |
WALK_LIST(sn, *src) |
551 |
{ |
552 |
dn = cfg_alloc(node_size); |
553 |
memcpy(dn, sn, node_size); |
554 |
add_tail(dest, dn); |
555 |
} |
556 |
} |