]> git.ipfire.org Git - thirdparty/bird.git/blame - nest/rt-table.c
Added dumping of routing tables (`show route'). This includes filtering.
[thirdparty/bird.git] / nest / rt-table.c
CommitLineData
62aa008a
MM
1/*
2 * BIRD -- Routing Table
3 *
730f2e2c 4 * (c) 1998--1999 Martin Mares <mj@ucw.cz>
62aa008a
MM
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
7 */
8
2326b001
MM
9#include <string.h>
10
1a54b1c6
MM
11#define LOCAL_DEBUG
12
62aa008a
MM
13#include "nest/bird.h"
14#include "nest/route.h"
2326b001 15#include "nest/protocol.h"
730f2e2c
MM
16#include "nest/cli.h"
17#include "nest/iface.h"
2326b001 18#include "lib/resource.h"
5996da6a 19#include "lib/event.h"
730f2e2c 20#include "lib/string.h"
0e02abfd 21#include "conf/conf.h"
529c4149 22#include "filter/filter.h"
2326b001 23
2326b001 24static slab *rte_slab;
e2dc2f30 25static linpool *rte_update_pool;
2326b001 26
5996da6a
MM
27#define RT_GC_MIN_TIME 5 /* FIXME: Make configurable */
28#define RT_GC_MIN_COUNT 100
29
30static pool *rt_table_pool;
0e02abfd 31static list routing_tables;
5996da6a
MM
32static event *rt_gc_event;
33static bird_clock_t rt_last_gc;
34static int rt_gc_counter;
35
36static void
2326b001
MM
37rte_init(struct fib_node *N)
38{
39 net *n = (net *) N;
40
4c45595e 41 N->flags = 0;
2326b001
MM
42 n->routes = NULL;
43}
44
45void
c10421d3 46rt_setup(pool *p, rtable *t, char *name)
2326b001
MM
47{
48 bzero(t, sizeof(*t));
d7975d26 49 fib_init(&t->fib, p, sizeof(net), 0, rte_init);
0cdbd397 50 t->name = name;
0e02abfd 51 init_list(&t->hooks);
2326b001
MM
52}
53
2326b001
MM
54rte *
55rte_find(net *net, struct proto *p)
56{
57 rte *e = net->routes;
58
59 while (e && e->attrs->proto != p)
60 e = e->next;
61 return e;
62}
63
64rte *
65rte_get_temp(rta *a)
66{
67 rte *e = sl_alloc(rte_slab);
68
69 e->attrs = a;
0cdbd397 70 e->flags = 0;
2326b001 71 e->pref = a->proto->preference;
2326b001
MM
72 return e;
73}
74
e2dc2f30
MM
75rte *
76rte_do_cow(rte *r)
77{
78 rte *e = sl_alloc(rte_slab);
79
80 memcpy(e, r, sizeof(rte));
81 e->attrs = rta_clone(r->attrs);
82 e->flags = 0;
83 return e;
84}
85
2326b001
MM
86static int /* Actually better or at least as good as */
87rte_better(rte *new, rte *old)
88{
d9f330c5
MM
89 int (*better)(rte *, rte *);
90
2326b001
MM
91 if (!old)
92 return 1;
93 if (new->pref > old->pref)
94 return 1;
95 if (new->pref < old->pref)
96 return 0;
739ebd8e
MM
97 if (new->attrs->proto->proto != old->attrs->proto->proto)
98 bug("Different protocols, but identical preferences => oops"); /* FIXME */
d9f330c5
MM
99 if (better = new->attrs->proto->rte_better)
100 return better(new, old);
101 return 0;
2326b001
MM
102}
103
529c4149 104static inline void
0e02abfd 105do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list *tmpa)
529c4149 106{
0e02abfd 107 struct proto *p = a->proto;
e2dc2f30
MM
108 rte *new0 = new;
109 rte *old0 = old;
7de45ba4 110
e2dc2f30 111 if (new)
529c4149 112 {
e2dc2f30
MM
113 int ok = p->import_control ? p->import_control(p, &new, &tmpa, rte_update_pool) : 0;
114 if (ok < 0 ||
115 (!ok && (p->out_filter == FILTER_REJECT ||
116 p->out_filter && f_run(p->out_filter, &new, &tmpa, rte_update_pool) > F_MODIFY)
117 )
118 )
529c4149
MM
119 new = NULL;
120 }
e2dc2f30
MM
121 if (old && p->out_filter)
122 {
123 /* FIXME: Do we really need to filter old routes? */
124 if (p->out_filter == FILTER_REJECT)
125 old = NULL;
126 else
127 {
128 ea_list *tmpb = p->make_tmp_attrs ? p->make_tmp_attrs(old, rte_update_pool) : NULL;
129 if (f_run(p->out_filter, &old, &tmpb, rte_update_pool) > F_MODIFY)
130 old = NULL;
131 }
132 }
529c4149 133 if (new || old)
bb027be1 134 p->rt_notify(p, net, new, old, tmpa);
e2dc2f30
MM
135 if (new && new != new0) /* Discard temporary rte's */
136 rte_free(new);
137 if (old && old != old0)
138 rte_free(old);
529c4149
MM
139}
140
e2dc2f30 141static void
0e02abfd 142rte_announce(rtable *tab, net *net, rte *new, rte *old, ea_list *tmpa)
2326b001 143{
0e02abfd 144 struct announce_hook *a;
2326b001 145
0e02abfd 146 WALK_LIST(a, tab->hooks)
0a2e9d9f 147 {
8c943173 148 ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING);
0e02abfd 149 do_rte_announce(a, net, new, old, tmpa);
0a2e9d9f 150 }
2326b001
MM
151}
152
47b79306
MM
153void
154rt_feed_baby(struct proto *p)
155{
0e02abfd 156 struct announce_hook *h;
47b79306 157
0e02abfd 158 if (!p->ahooks)
47b79306 159 return;
64011f89 160 debug("Announcing routes to new protocol %s\n", p->name);
0e02abfd 161 for(h=p->ahooks; h; h=h->next)
47b79306 162 {
0e02abfd
MM
163 rtable *t = h->table;
164 FIB_WALK(&t->fib, fn)
47b79306 165 {
0e02abfd
MM
166 net *n = (net *) fn;
167 rte *e;
168 for(e=n->routes; e; e=e->next)
169 {
170 struct proto *q = e->attrs->proto;
171 ea_list *tmpa = q->make_tmp_attrs ? q->make_tmp_attrs(e, rte_update_pool) : NULL;
172 do_rte_announce(h, n, e, NULL, tmpa);
173 lp_flush(rte_update_pool);
174 }
47b79306 175 }
0e02abfd 176 FIB_WALK_END;
47b79306
MM
177 }
178}
179
421838ff
MM
180static inline int
181rte_validate(rte *e)
182{
183 int c;
184 net *n = e->net;
185
186 ASSERT(!ipa_nonzero(ipa_and(n->n.prefix, ipa_not(ipa_mkmask(n->n.pxlen)))));
187 if (n->n.pxlen)
188 {
189 c = ipa_classify(n->n.prefix);
190 if (c < 0 || !(c & IADDR_HOST))
191 {
192 if (!ipa_nonzero(n->n.prefix) && n->n.pxlen <= 1)
193 return 1; /* Default route and half-default route is OK */
194 log(L_WARN "Ignoring bogus route %I/%d received from %I via %s",
195 n->n.prefix, n->n.pxlen, e->attrs->from, e->attrs->proto->name);
196 return 0;
197 }
198 if ((c & IADDR_SCOPE_MASK) == SCOPE_HOST)
199 {
200 int s = e->attrs->source;
201 if (s != RTS_STATIC && s != RTS_DEVICE && s != RTS_STATIC_DEVICE)
202 {
203 log(L_WARN "Ignoring host scope route %I/%d received from %I via %s",
204 n->n.prefix, n->n.pxlen, e->attrs->from, e->attrs->proto->name);
205 return 0;
206 }
207 }
208 }
209 return 1;
210}
211
04925e90 212void
2326b001 213rte_free(rte *e)
04925e90
MM
214{
215 if (e->attrs->aflags & RTAF_CACHED)
216 rta_free(e->attrs);
217 sl_free(rte_slab, e);
218}
219
220static inline void
221rte_free_quick(rte *e)
2326b001
MM
222{
223 rta_free(e->attrs);
224 sl_free(rte_slab, e);
225}
226
e2dc2f30 227static void
0e02abfd 228rte_recalculate(rtable *table, net *net, struct proto *p, rte *new, ea_list *tmpa)
2326b001
MM
229{
230 rte *old_best = net->routes;
231 rte *old = NULL;
232 rte **k, *r, *s;
2326b001
MM
233
234 k = &net->routes; /* Find and remove original route from the same protocol */
235 while (old = *k)
236 {
237 if (old->attrs->proto == p)
238 {
239 *k = old->next;
240 break;
241 }
242 k = &old->next;
243 }
244
0cdbd397 245 if (new && rte_better(new, old_best)) /* It's a new optimal route => announce and relink it */
2326b001 246 {
0e02abfd 247 rte_announce(table, net, new, old_best, tmpa);
2326b001
MM
248 new->next = net->routes;
249 net->routes = new;
250 }
251 else
252 {
253 if (old == old_best) /* It has _replaced_ the old optimal route */
254 {
255 r = new; /* Find new optimal route and announce it */
256 for(s=net->routes; s; s=s->next)
257 if (rte_better(s, r))
258 r = s;
0e02abfd 259 rte_announce(table, net, r, old_best, tmpa);
2326b001
MM
260 if (r) /* Re-link the new optimal route */
261 {
262 k = &net->routes;
263 while (s = *k)
264 {
265 if (s == r)
266 {
267 *k = r->next;
268 break;
269 }
dafd580e 270 k = &s->next;
2326b001
MM
271 }
272 r->next = net->routes;
273 net->routes = r;
5996da6a
MM
274 if (!r && rt_gc_counter++ >= RT_GC_MIN_COUNT && rt_last_gc + RT_GC_MIN_TIME <= now)
275 ev_schedule(rt_gc_event);
2326b001
MM
276 }
277 }
278 if (new) /* Link in the new non-optimal route */
279 {
280 new->next = old_best->next;
281 old_best->next = new;
282 }
283 }
284 if (old)
5b22683d
MM
285 {
286 if (p->rte_remove)
287 p->rte_remove(net, old);
04925e90 288 rte_free_quick(old);
5b22683d 289 }
8ca8683c
MM
290 if (new)
291 {
292 new->lastmod = now;
293 if (p->rte_insert)
294 p->rte_insert(net, new);
295 }
5b22683d
MM
296}
297
e2dc2f30
MM
298static int rte_update_nest_cnt; /* Nesting counter to allow recursive updates */
299
300static inline void
301rte_update_lock(void)
302{
303 rte_update_nest_cnt++;
304}
305
306static inline void
307rte_update_unlock(void)
308{
309 if (!--rte_update_nest_cnt)
310 lp_flush(rte_update_pool);
311}
312
313void
0e02abfd 314rte_update(rtable *table, net *net, struct proto *p, rte *new)
e2dc2f30
MM
315{
316 ea_list *tmpa = NULL;
317
318 rte_update_lock();
319 if (new)
320 {
321 if (!rte_validate(new) || p->in_filter == FILTER_REJECT)
322 goto drop;
323 if (p->make_tmp_attrs)
324 tmpa = p->make_tmp_attrs(new, rte_update_pool);
325 if (p->in_filter)
326 {
327 int fr = f_run(p->in_filter, &new, &tmpa, rte_update_pool);
328 if (fr > F_MODIFY)
329 goto drop;
330 if (fr == F_MODIFY && p->store_tmp_attrs)
331 p->store_tmp_attrs(new, tmpa);
332 }
333 if (!(new->attrs->aflags & RTAF_CACHED)) /* Need to copy attributes */
334 new->attrs = rta_lookup(new->attrs);
335 new->flags |= REF_COW;
336 }
0e02abfd 337 rte_recalculate(table, net, p, new, tmpa);
e2dc2f30
MM
338 rte_update_unlock();
339 return;
340
341drop:
342 rte_free(new);
343 rte_update_unlock();
344}
345
5b22683d 346void
0e02abfd 347rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used during garbage collection */
5b22683d 348{
e2dc2f30 349 rte_update_lock();
0e02abfd 350 rte_recalculate(t, old->net, old->attrs->proto, NULL, NULL);
e2dc2f30 351 rte_update_unlock();
2326b001
MM
352}
353
354void
a0762910 355rte_dump(rte *e)
2326b001 356{
a0762910 357 net *n = e->net;
0cdbd397 358 if (n)
962ba482 359 debug("%1I/%2d ", n->n.prefix, n->n.pxlen);
a0762910
MM
360 else
361 debug("??? ");
c10421d3 362 debug("KF=%02x PF=%02x pref=%d lm=%d ", n->n.flags, e->pflags, e->pref, now-e->lastmod);
0cdbd397 363 rta_dump(e->attrs);
c10421d3
MM
364 if (e->attrs->proto->proto->dump_attrs)
365 e->attrs->proto->proto->dump_attrs(e);
0cdbd397 366 debug("\n");
2326b001 367}
62aa008a 368
2326b001
MM
369void
370rt_dump(rtable *t)
371{
0cdbd397
MM
372 rte *e;
373 net *n;
0e02abfd 374 struct announce_hook *a;
0cdbd397
MM
375
376 debug("Dump of routing table <%s>\n", t->name);
e440395d 377#ifdef DEBUGGING
08e2d625 378 fib_check(&t->fib);
e440395d 379#endif
08e2d625
MM
380 FIB_WALK(&t->fib, fn)
381 {
382 n = (net *) fn;
383 for(e=n->routes; e; e=e->next)
384 rte_dump(e);
0cdbd397 385 }
08e2d625 386 FIB_WALK_END;
0e02abfd
MM
387 WALK_LIST(a, t->hooks)
388 debug("\tAnnounces routes to protocol %s\n", a->proto->name);
0cdbd397 389 debug("\n");
2326b001 390}
62aa008a 391
6d45cf21
MM
392void
393rt_dump_all(void)
394{
0e02abfd
MM
395 rtable *t;
396
397 WALK_LIST(t, routing_tables)
398 rt_dump(t);
6d45cf21
MM
399}
400
0d70292d 401static int
5996da6a
MM
402rt_gc(void *unused)
403{
0e02abfd
MM
404 rtable *t;
405
5996da6a 406 DBG("Entered routing table garbage collector after %d seconds and %d deletes\n", (int)(now - rt_last_gc), rt_gc_counter);
0e02abfd 407 rt_prune_all();
5996da6a
MM
408 rt_last_gc = now;
409 rt_gc_counter = 0;
0d70292d 410 return 0;
5996da6a
MM
411}
412
2326b001
MM
413void
414rt_init(void)
415{
416 rta_init();
5996da6a 417 rt_table_pool = rp_new(&root_pool, "Routing tables");
e2dc2f30 418 rte_update_pool = lp_new(rt_table_pool, 4080);
5996da6a
MM
419 rte_slab = sl_new(rt_table_pool, sizeof(rte));
420 rt_last_gc = now;
421 rt_gc_event = ev_new(rt_table_pool);
422 rt_gc_event->hook = rt_gc;
0e02abfd 423 init_list(&routing_tables);
2326b001 424}
1a54b1c6
MM
425
426void
427rt_prune(rtable *tab)
428{
429 struct fib_iterator fit;
5996da6a 430 int rcnt = 0, rdel = 0, ncnt = 0, ndel = 0;
1a54b1c6
MM
431
432 DBG("Pruning route table %s\n", tab->name);
08e2d625
MM
433 FIB_ITERATE_INIT(&fit, &tab->fib);
434again:
435 FIB_ITERATE_START(&tab->fib, &fit, f)
1a54b1c6 436 {
08e2d625
MM
437 net *n = (net *) f;
438 rte *e;
439 ncnt++;
440 rescan:
441 for (e=n->routes; e; e=e->next, rcnt++)
442 if (e->attrs->proto->core_state != FS_HAPPY)
443 {
0e02abfd 444 rte_discard(tab, e);
08e2d625
MM
445 rdel++;
446 goto rescan;
447 }
448 if (!n->routes) /* Orphaned FIB entry? */
1a54b1c6 449 {
08e2d625
MM
450 FIB_ITERATE_PUT(&fit, f);
451 fib_delete(&tab->fib, f);
452 ndel++;
453 goto again;
1a54b1c6 454 }
1a54b1c6 455 }
08e2d625 456 FIB_ITERATE_END(f);
5996da6a 457 DBG("Pruned %d of %d routes and %d of %d networks\n", rcnt, rdel, ncnt, ndel);
1a54b1c6 458}
0e02abfd
MM
459
460void
461rt_prune_all(void)
462{
463 rtable *t;
464
465 WALK_LIST(t, routing_tables)
466 rt_prune(t);
467}
468
469void
470rt_preconfig(struct config *c)
471{
472 struct symbol *s = cf_find_symbol("master");
473 struct rtable_config *r = cfg_allocz(sizeof(struct rtable_config));
474
475 cf_define_symbol(s, SYM_TABLE, r);
476 r->name = s->name;
477 init_list(&c->tables);
478 add_tail(&c->tables, &r->n);
479 c->master_rtc = r;
480}
481
482void
483rt_commit(struct config *c)
484{
485 struct rtable_config *r;
486
487 WALK_LIST(r, c->tables)
488 {
489 rtable *t = mb_alloc(rt_table_pool, sizeof(struct rtable));
490 rt_setup(rt_table_pool, t, r->name);
491 add_tail(&routing_tables, &t->n);
492 r->table = t;
493 }
494}
730f2e2c
MM
495
496/*
497 * CLI commands
498 */
499
500static void
501rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d)
502{
503 byte via[STD_ADDRESS_P_LENGTH+32], from[STD_ADDRESS_P_LENGTH];
504 byte tm[TM_RELTIME_BUFFER_SIZE], info[256];
505 rta *a = e->attrs;
506
507 switch (a->dest)
508 {
509 case RTD_ROUTER: bsprintf(via, "via %I on %s", a->gw, a->iface->name); break;
510 case RTD_DEVICE: bsprintf(via, "dev %s", a->iface->name); break;
511 case RTD_BLACKHOLE: bsprintf(via, "blackhole"); break;
512 case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
513 case RTD_PROHIBIT: bsprintf(via, "prohibited"); break;
514 default: bsprintf(via, "???");
515 }
516 tm_format_reltime(tm, e->lastmod);
517 if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->gw))
518 bsprintf(from, " from %I", a->from);
519 else
520 from[0] = 0;
521 if (a->proto->proto->get_route_info)
522 a->proto->proto->get_route_info(e, info);
523 else
524 bsprintf(info, " (%d)", e->pref);
525 cli_printf(c, -1007, "%-18s %s [%s %s%s]%s", ia, via, a->proto->name, tm, from, info);
526 if (d->verbose)
527 {
528 rta_show(c, a);
529 if (a->proto->proto->show_route_data)
530 a->proto->proto->show_route_data(e);
531 }
532}
533
534static void
535rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
536{
537 rte *e, *ee;
538 byte ia[STD_ADDRESS_P_LENGTH+8];
539
540 bsprintf(ia, "%I/%d", n->n.prefix, n->n.pxlen);
541 for(e=n->routes; e; e=e->next)
542 {
543 struct ea_list *tmpa = NULL;
544 ee = e;
545 rte_update_lock(); /* We use the update buffer for filtering */
546 if (d->filter == FILTER_ACCEPT || f_run(d->filter, &ee, &tmpa, rte_update_pool) <= F_MODIFY)
547 {
548 rt_show_rte(c, ia, e, d);
549 ia[0] = 0;
550 }
551 if (e != ee)
552 rte_free(ee);
553 rte_update_unlock();
554 }
555}
556
557static void
558rt_show_cont(struct cli *c)
559{
560 struct rt_show_data *d = c->rover;
561 unsigned max = 1; /* FIXME: After some debugging, increase to reasonable amount */
562 struct fib *fib = &d->table->fib;
563 struct fib_iterator *it = &d->fit;
564
565 FIB_ITERATE_START(fib, it, f)
566 {
567 net *n = (net *) f;
568 if (!max--)
569 {
570 FIB_ITERATE_PUT(it, f);
571 return;
572 }
573 rt_show_net(c, n, d);
574 }
575 FIB_ITERATE_END(f);
576 cli_printf(c, 0, "");
577 c->cont = c->cleanup = NULL;
578}
579
580static void
581rt_show_cleanup(struct cli *c)
582{
583 struct rt_show_data *d = c->rover;
584
585 /* Unlink the iterator */
586 fit_get(&d->table->fib, &d->fit);
587}
588
589void
590rt_show(struct rt_show_data *d)
591{
592 struct rtable_config *tc;
593 net *n;
594
595 if (d->pxlen == 256)
596 {
597 FIB_ITERATE_INIT(&d->fit, &d->table->fib);
598 this_cli->cont = rt_show_cont;
599 this_cli->cleanup = rt_show_cleanup;
600 this_cli->rover = d;
601 }
602 else
603 {
604 n = fib_find(&d->table->fib, &d->prefix, d->pxlen);
605 if (n)
606 {
607 rt_show_net(this_cli, n, d);
608 cli_msg(0, "");
609 }
610 else
611 cli_msg(8001, "Network not in table");
612 }
613}