]>
Commit | Line | Data |
---|---|---|
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 |
24 | static pool *proto_pool; |
25 | ||
7f4a3988 | 26 | list protocol_list; |
2326b001 | 27 | list proto_list; |
67bd949a MM |
28 | |
29 | static list inactive_proto_list; | |
30 | static list initial_proto_list; | |
1a54b1c6 MM |
31 | static list flush_proto_list; |
32 | ||
f4aabcee MM |
33 | static int proto_shutdown_counter; |
34 | ||
1a54b1c6 | 35 | static event *proto_flush_event; |
67bd949a MM |
36 | |
37 | static char *p_states[] = { "DOWN", "START", "UP", "STOP" }; | |
38 | static char *c_states[] = { "HUNGRY", "FEEDING", "HAPPY", "FLUSHING" }; | |
39 | ||
0d70292d | 40 | static int proto_flush_all(void *); |
1a54b1c6 | 41 | |
b2280748 MM |
42 | static void |
43 | proto_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 |
59 | static void |
60 | proto_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 | 79 | void * |
31b3e1bb | 80 | proto_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 |
98 | static void |
99 | proto_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 |
107 | struct announce_hook * |
108 | proto_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 | ||
124 | static void | |
125 | proto_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 |
134 | void * |
135 | proto_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 | 149 | void |
31b3e1bb | 150 | protos_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 | ||
169 | void | |
31b3e1bb | 170 | protos_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 | ||
186 | void | |
187 | protos_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 | 215 | static void |
67bd949a | 216 | proto_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 | ||
243 | static void | |
244 | proto_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 | |
252 | void | |
253 | protos_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 |
262 | void |
263 | protos_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 |
281 | void |
282 | protos_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 |
307 | void |
308 | protos_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 | ||
326 | static void | |
327 | proto_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 | 335 | static int |
67bd949a MM |
336 | proto_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 |
350 | void |
351 | proto_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 | 408 | static int |
1a54b1c6 MM |
409 | proto_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 | ||
431 | static char * | |
432 | proto_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 | 450 | static void |
1d2664a4 MM |
451 | proto_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 | ||
474 | static void | |
475 | proto_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 | 483 | void |
0d3e6bce | 484 | proto_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 | |
503 | struct proto * | |
504 | proto_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 | } |