]> git.ipfire.org Git - thirdparty/bird.git/blame - nest/proto.c
Added tracked_fopen() which is a fopen registered in resource database.
[thirdparty/bird.git] / nest / proto.c
CommitLineData
2326b001
MM
1/*
2 * BIRD -- Protocols
3 *
31b3e1bb 4 * (c) 1998--1999 Martin Mares <mj@ucw.cz>
2326b001
MM
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
7 */
8
7f4a3988
MM
9#define LOCAL_DEBUG
10
11#include <string.h>
12
2326b001
MM
13#include "nest/bird.h"
14#include "nest/protocol.h"
15#include "lib/resource.h"
16#include "lib/lists.h"
67bd949a 17#include "lib/event.h"
fe7cec12 18#include "conf/conf.h"
47b79306
MM
19#include "nest/route.h"
20#include "nest/iface.h"
ae97b946 21#include "nest/cli.h"
529c4149 22#include "filter/filter.h"
2326b001 23
67bd949a
MM
24static pool *proto_pool;
25
7f4a3988 26list protocol_list;
2326b001 27list proto_list;
67bd949a
MM
28
29static list inactive_proto_list;
30static list initial_proto_list;
1a54b1c6
MM
31static list flush_proto_list;
32
f4aabcee
MM
33static int proto_shutdown_counter;
34
1a54b1c6 35static event *proto_flush_event;
67bd949a
MM
36
37static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
38static char *c_states[] = { "HUNGRY", "FEEDING", "HAPPY", "FLUSHING" };
39
0d70292d 40static int proto_flush_all(void *);
1a54b1c6 41
b2280748
MM
42static void
43proto_enqueue(list *l, struct proto *p)
44{
45 int pri = p->proto->priority;
46
47 if (!pri)
48 add_tail(l, &p->n);
49 else
50 {
51 struct proto *q = HEAD(*l);
52 while (q->n.next && q->proto->priority >= pri)
53 q = (struct proto *) q->n.next;
54 insert_node(&p->n, q->n.prev);
55 }
9685deb9 56 p->last_state_change = now;
b2280748
MM
57}
58
67bd949a
MM
59static void
60proto_relink(struct proto *p)
61{
1a54b1c6
MM
62 list *l;
63
67bd949a 64 rem_node(&p->n);
1a54b1c6
MM
65 switch (p->core_state)
66 {
67 case FS_HAPPY:
68 l = &proto_list;
69 break;
70 case FS_FLUSHING:
71 l = &flush_proto_list;
72 break;
73 default:
74 l = &inactive_proto_list;
75 }
b2280748 76 proto_enqueue(l, p);
67bd949a 77}
2326b001 78
7f4a3988 79void *
31b3e1bb 80proto_new(struct proto_config *c, unsigned size)
7f4a3988 81{
1d2664a4 82 struct protocol *pr = c->protocol;
8fe48f13 83 struct proto *p = mb_allocz(proto_pool, size);
7f4a3988 84
31b3e1bb
MM
85 p->cf = c;
86 p->debug = c->debug;
67bd949a 87 p->name = c->name;
31b3e1bb
MM
88 p->preference = c->preference;
89 p->disabled = c->disabled;
7f4a3988 90 p->proto = pr;
9d885689 91 p->table = c->table->table;
529c4149
MM
92 p->in_filter = c->in_filter;
93 p->out_filter = c->out_filter;
1d2664a4 94 c->proto = p;
7f4a3988
MM
95 return p;
96}
97
1a54b1c6
MM
98static void
99proto_init_instance(struct proto *p)
100{
5bc512aa
MM
101 /* Here we cannot use p->cf->name since it won't survive reconfiguration */
102 p->pool = rp_new(proto_pool, p->proto->name);
1a54b1c6
MM
103 p->attn = ev_new(p->pool);
104 p->attn->data = p;
105}
106
0e02abfd
MM
107struct announce_hook *
108proto_add_announce_hook(struct proto *p, struct rtable *t)
109{
110 struct announce_hook *h;
111
112 if (!p->rt_notify)
113 return NULL;
114 DBG("Connecting protocol %s to table %s\n", p->name, t->name);
115 h = mb_alloc(p->pool, sizeof(struct announce_hook));
116 h->table = t;
117 h->proto = p;
118 h->next = p->ahooks;
119 p->ahooks = h;
120 add_tail(&t->hooks, &h->n);
121 return h;
122}
123
124static void
125proto_flush_hooks(struct proto *p)
126{
127 struct announce_hook *h;
128
129 for(h=p->ahooks; h; h=h->next)
130 rem_node(&h->n);
131 p->ahooks = NULL;
132}
133
31b3e1bb
MM
134void *
135proto_config_new(struct protocol *pr, unsigned size)
136{
137 struct proto_config *c = cfg_allocz(size);
138
139 add_tail(&new_config->protos, &c->n);
140 c->global = new_config;
1d2664a4 141 c->protocol = pr;
31b3e1bb
MM
142 c->debug = pr->debug;
143 c->name = pr->name;
5056c559 144 c->out_filter = FILTER_REJECT;
9d885689 145 c->table = c->global->master_rtc;
31b3e1bb
MM
146 return c;
147}
148
2326b001 149void
31b3e1bb 150protos_preconfig(struct config *c)
2326b001 151{
7f4a3988
MM
152 struct protocol *p;
153
2326b001 154 init_list(&proto_list);
47b79306 155 init_list(&inactive_proto_list);
67bd949a 156 init_list(&initial_proto_list);
1a54b1c6 157 init_list(&flush_proto_list);
31b3e1bb 158 debug("Protocol preconfig:");
7f4a3988
MM
159 WALK_LIST(p, protocol_list)
160 {
31b3e1bb 161 debug(" %s", p->name);
4ba84ebc 162 p->name_counter = 0;
3629bcf0 163 if (p->preconfig)
31b3e1bb 164 p->preconfig(p, c);
7f4a3988 165 }
31b3e1bb 166 debug("\n");
7f4a3988
MM
167}
168
169void
31b3e1bb 170protos_postconfig(struct config *c)
7f4a3988 171{
31b3e1bb 172 struct proto_config *x;
7f4a3988
MM
173 struct protocol *p;
174
31b3e1bb
MM
175 debug("Protocol postconfig:");
176 WALK_LIST(x, c->protos)
7f4a3988 177 {
31b3e1bb 178 debug(" %s", x->name);
1d2664a4 179 p = x->protocol;
3629bcf0 180 if (p->postconfig)
31b3e1bb
MM
181 p->postconfig(x);
182 }
183 debug("\n");
184}
185
186void
187protos_commit(struct config *c)
188{
189 struct proto_config *x;
190 struct protocol *p;
191 struct proto *q;
192
193 debug("Protocol commit:");
194 WALK_LIST(x, c->protos)
195 {
196 debug(" %s", x->name);
1d2664a4 197 p = x->protocol;
31b3e1bb 198 q = p->init(x);
67bd949a
MM
199 q->proto_state = PS_DOWN;
200 q->core_state = FS_HUNGRY;
b2280748 201 proto_enqueue(&initial_proto_list, q);
9d885689
MM
202 /*
203 * HACK ALERT! In case of multiple kernel routing tables,
204 * the kernel syncer acts as multiple protocols which cooperate
205 * with each other. In order to speed up their initialization,
206 * we need to know when we're initializing the last one, hence
207 * the startup counter.
208 */
209 if (!q->disabled)
210 p->startup_counter++;
7f4a3988 211 }
31b3e1bb 212 debug("\n");
7f4a3988
MM
213}
214
47b79306 215static void
67bd949a 216proto_rethink_goal(struct proto *p)
47b79306 217{
67bd949a
MM
218 struct protocol *q = p->proto;
219
220 if (p->core_state == p->core_goal)
66efdf96 221 return;
67bd949a
MM
222 if (p->core_goal == FS_HAPPY) /* Going up */
223 {
224 if (p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN)
225 {
226 DBG("Kicking %s up\n", p->name);
9d885689
MM
227 ASSERT(q->startup_counter > 0);
228 q->startup_counter--;
1a54b1c6 229 proto_init_instance(p);
67bd949a
MM
230 proto_notify_state(p, (q->start ? q->start(p) : PS_UP));
231 }
232 }
233 else /* Going down */
234 {
235 if (p->proto_state == PS_START || p->proto_state == PS_UP)
236 {
237 DBG("Kicking %s down\n", p->name);
238 proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN));
239 }
240 }
241}
242
243static void
244proto_set_goal(struct proto *p, unsigned goal)
245{
f4aabcee 246 if (p->disabled || shutting_down)
67bd949a
MM
247 goal = FS_HUNGRY;
248 p->core_goal = goal;
249 proto_rethink_goal(p);
47b79306 250}
7f4a3988
MM
251
252void
253protos_start(void)
254{
47b79306 255 struct proto *p, *n;
7f4a3988
MM
256
257 debug("Protocol start\n");
67bd949a
MM
258 WALK_LIST_DELSAFE(p, n, initial_proto_list)
259 proto_set_goal(p, FS_HAPPY);
7f4a3988
MM
260}
261
f4aabcee
MM
262void
263protos_shutdown(void)
264{
265 struct proto *p, *n;
266
267 debug("Protocol shutdown\n");
9d885689 268 WALK_LIST_BACKWARDS_DELSAFE(p, n, inactive_proto_list)
f4aabcee
MM
269 if (p->core_state != FS_HUNGRY || p->proto_state != PS_DOWN)
270 {
271 proto_shutdown_counter++;
272 proto_set_goal(p, FS_HUNGRY);
273 }
9d885689 274 WALK_LIST_BACKWARDS_DELSAFE(p, n, proto_list)
f4aabcee
MM
275 {
276 proto_shutdown_counter++;
277 proto_set_goal(p, FS_HUNGRY);
278 }
279}
280
87d2be86
PM
281void
282protos_dump_all(void)
283{
284 struct proto *p;
285
286 debug("Protocols:\n");
287
288 WALK_LIST(p, proto_list)
289 {
b2280748
MM
290 debug(" protocol %s (pri=%d): state %s/%s\n", p->name, p->proto->priority,
291 p_states[p->proto_state], c_states[p->core_state]);
529c4149 292 if (p->in_filter)
5056c559
MM
293 debug("\tInput filter: %s\n", filter_name(p->in_filter));
294 if (p->out_filter != FILTER_REJECT)
295 debug("\tOutput filter: %s\n", filter_name(p->out_filter));
66efdf96
MM
296 if (p->disabled)
297 debug("\tDISABLED\n");
31b3e1bb
MM
298 else if (p->proto->dump)
299 p->proto->dump(p);
87d2be86 300 }
47b79306 301 WALK_LIST(p, inactive_proto_list)
67bd949a
MM
302 debug(" inactive %s: state %s/%s\n", p->name, p_states[p->proto_state], c_states[p->core_state]);
303 WALK_LIST(p, initial_proto_list)
304 debug(" initial %s\n", p->name);
87d2be86
PM
305}
306
0432c017
MM
307void
308protos_build(void)
309{
310 init_list(&protocol_list);
311 add_tail(&protocol_list, &proto_device.n);
18fff6a1 312#ifdef CONFIG_RIP
0432c017 313 add_tail(&protocol_list, &proto_rip.n);
18fff6a1
MM
314#endif
315#ifdef CONFIG_STATIC
316 add_tail(&protocol_list, &proto_static.n);
c1f8dc91
OF
317#endif
318#ifdef CONFIG_OSPF
319 add_tail(&protocol_list, &proto_ospf.n);
18fff6a1 320#endif
67bd949a 321 proto_pool = rp_new(&root_pool, "Protocols");
1a54b1c6
MM
322 proto_flush_event = ev_new(proto_pool);
323 proto_flush_event->hook = proto_flush_all;
67bd949a
MM
324}
325
326static void
327proto_fell_down(struct proto *p)
328{
329 DBG("Protocol %s down\n", p->name);
f4aabcee
MM
330 if (!--proto_shutdown_counter)
331 protos_shutdown_notify();
67bd949a
MM
332 proto_rethink_goal(p);
333}
334
0d70292d 335static int
67bd949a
MM
336proto_feed(void *P)
337{
338 struct proto *p = P;
339
340 DBG("Feeding protocol %s\n", p->name);
0e02abfd 341 proto_add_announce_hook(p, p->table);
67bd949a
MM
342 if_feed_baby(p);
343 rt_feed_baby(p);
344 p->core_state = FS_HAPPY;
345 proto_relink(p);
346 DBG("Protocol %s up and running\n", p->name);
0d70292d 347 return 0;
67bd949a
MM
348}
349
67bd949a
MM
350void
351proto_notify_state(struct proto *p, unsigned ps)
352{
353 unsigned ops = p->proto_state;
354 unsigned cs = p->core_state;
355
356 DBG("%s reporting state transition %s/%s -> */%s\n", p->name, c_states[cs], p_states[ops], p_states[ps]);
357 if (ops == ps)
358 return;
359
360 switch (ps)
361 {
362 case PS_DOWN:
363 if (cs == FS_HUNGRY) /* Shutdown finished */
364 proto_fell_down(p);
365 else if (cs == FS_FLUSHING) /* Still flushing... */
366 ;
367 else /* Need to start flushing */
368 goto schedule_flush;
369 break;
370 case PS_START:
371 ASSERT(ops == PS_DOWN);
372 ASSERT(cs == FS_HUNGRY);
373 break;
374 case PS_UP:
375 ASSERT(ops == PS_DOWN || ops == PS_START);
376 ASSERT(cs == FS_HUNGRY);
377 DBG("%s: Scheduling meal\n", p->name);
0e889c52
MM
378 if (p->proto->priority) /* FIXME: Terrible hack to get synchronous device/kernel startup! */
379 {
53b7a298 380 p->proto_state = ps;
0e889c52
MM
381 p->core_state = FS_FEEDING;
382 proto_feed(p);
383 return;
384 }
67bd949a
MM
385 cs = FS_FEEDING;
386 p->attn->hook = proto_feed;
387 ev_schedule(p->attn);
388 break;
389 case PS_STOP:
390 if (cs == FS_FEEDING || cs == FS_HAPPY)
391 {
392 schedule_flush:
393 DBG("%s: Scheduling flush\n", p->name);
9d885689 394 proto_flush_hooks(p);
67bd949a 395 cs = FS_FLUSHING;
1a54b1c6 396 ev_schedule(proto_flush_event);
67bd949a 397 }
f4aabcee 398 break;
67bd949a
MM
399 default:
400 error:
401 bug("Invalid state transition for %s from %s/%s to */%s", p->name, c_states[cs], p_states[ops], p_states[ps]);
402 }
403 p->proto_state = ps;
404 p->core_state = cs;
405 proto_relink(p);
0432c017 406}
1a54b1c6 407
0d70292d 408static int
1a54b1c6
MM
409proto_flush_all(void *unused)
410{
411 struct proto *p;
412
0e02abfd 413 rt_prune_all();
783f8b68 414 neigh_prune();
1a54b1c6
MM
415 while ((p = HEAD(flush_proto_list))->n.next)
416 {
417 DBG("Flushing protocol %s\n", p->name);
418 rfree(p->pool);
419 p->pool = NULL;
420 p->core_state = FS_HUNGRY;
421 proto_relink(p);
f4aabcee 422 proto_fell_down(p);
1a54b1c6 423 }
0d70292d 424 return 0;
1a54b1c6 425}
ae97b946 426
0d3e6bce
MM
427/*
428 * CLI Commands
429 */
430
431static char *
432proto_state_name(struct proto *p)
433{
434#define P(x,y) ((x << 4) | y)
435 switch (P(p->proto_state, p->core_state))
436 {
437 case P(PS_DOWN, FS_HUNGRY): return "down";
438 case P(PS_START, FS_HUNGRY): return "start";
439 case P(PS_UP, FS_HUNGRY):
440 case P(PS_UP, FS_FEEDING): return "feed";
441 case P(PS_STOP, FS_HUNGRY): return "stop";
442 case P(PS_UP, FS_HAPPY): return "up";
443 case P(PS_STOP, FS_FLUSHING):
444 case P(PS_DOWN, FS_FLUSHING): return "flush";
445 default: return "???";
446 }
447#undef P
448}
449
0d3e6bce 450static void
1d2664a4
MM
451proto_do_show(struct proto *p, int verbose)
452{
9685deb9
MM
453 byte buf[256], reltime[TM_RELTIME_BUFFER_SIZE];
454
455 buf[0] = 0;
456 if (p->proto->get_status)
457 p->proto->get_status(p, buf);
458 tm_format_reltime(reltime, p->last_state_change);
459 cli_msg(-1002, "%-8s %-8s %-8s %-5s %-5s %s",
1d2664a4
MM
460 p->name,
461 p->proto->name,
462 p->table->name,
463 proto_state_name(p),
9685deb9
MM
464 reltime,
465 buf);
1d2664a4
MM
466 if (verbose)
467 {
468 cli_msg(-1006, "\tPreference: %d", p->preference);
469 cli_msg(-1006, "\tInput filter: %s", filter_name(p->in_filter));
470 cli_msg(-1006, "\tOutput filter: %s", filter_name(p->out_filter));
471 }
472}
473
474static void
475proto_do_show_list(list *l, int verbose)
0d3e6bce
MM
476{
477 struct proto *p;
478
479 WALK_LIST(p, *l)
1d2664a4 480 proto_do_show(p, verbose);
0d3e6bce
MM
481}
482
ae97b946 483void
0d3e6bce 484proto_show(struct symbol *s, int verbose)
ae97b946 485{
1d2664a4
MM
486 if (s && s->class != SYM_PROTO)
487 {
488 cli_msg(9002, "%s is not a protocol", s->name);
489 return;
490 }
9685deb9 491 cli_msg(-2002, "name proto table state since info");
1d2664a4
MM
492 if (s)
493 proto_do_show(((struct proto_config *)s->def)->proto, verbose);
494 else
495 {
496 proto_do_show_list(&proto_list, verbose);
497 proto_do_show_list(&flush_proto_list, verbose);
498 proto_do_show_list(&inactive_proto_list, verbose);
499 }
ae97b946
MM
500 cli_msg(0, "");
501}
02c1fbdd
MM
502
503struct proto *
504proto_get_named(struct symbol *sym, struct protocol *pr)
505{
506 struct proto *p, *q;
507
508 if (sym)
509 {
510 if (sym->class != SYM_PROTO)
511 cf_error("%s: Not a protocol", sym->name);
512 p = ((struct proto_config *)sym->def)->proto;
513 if (!p || p->proto != pr)
514 cf_error("%s: Not a %s protocol", sym->name, pr->name);
515 }
516 else
517 {
518 p = NULL;
519 WALK_LIST(q, proto_list)
520 if (q->proto == pr)
521 {
522 if (p)
523 cf_error("There are multiple %s protocols running", pr->name);
524 p = q;
525 }
526 if (!p)
527 cf_error("There is no %s protocol running", pr->name);
528 }
529 return p;
530}