0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 17:51:53 +00:00
bird/nest/rt-table.c
Martin Mares 04925e9040 Minor rte/rta interface changes:
o  rte can now contain a pointer to both cached and uncached rta. Protocols
     which don't need their own attribute caching can now just fill-in a rta,
     link it to rte without any calls to attribute cache and call rte_update()
     which will replace rte->attrs by a cached copy.

  o  In order to support this, one of previously pad bytes in struct rta
     now holds new attribute flags (RTAF_CACHED). If you call rte_update()
     with uncached rta, you _must_ clear these flags. In other cases rta_lookup()
     sets it appropriately.

  o  Added rte_free() which is useful when you construct a rte and then the
     circumstances change and you decide not to use it for an update. (Needed
     for temporary rte's in kernel syncer...)
1998-12-07 21:59:15 +00:00

282 lines
5.0 KiB
C

/*
* BIRD -- Routing Table
*
* (c) 1998 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include <string.h>
#include "nest/bird.h"
#include "nest/route.h"
#include "nest/protocol.h"
#include "lib/resource.h"
rtable master_table;
static slab *rte_slab;
void
rte_init(struct fib_node *N)
{
net *n = (net *) N;
N->flags = 0;
n->routes = NULL;
}
void
rt_setup(rtable *t, char *name)
{
bzero(t, sizeof(*t));
fib_init(&t->fib, &root_pool, sizeof(rte), 0, rte_init);
t->name = name;
}
net *
net_find(rtable *tab, unsigned tos, ip_addr mask, unsigned len)
{
while (tab && tab->tos != tos)
tab = tab->sibling;
if (!tab)
return NULL;
return (net *) fib_find(&tab->fib, &mask, len);
}
net *
net_get(rtable *tab, unsigned tos, ip_addr mask, unsigned len)
{
rtable *t = tab;
while (t && t->tos != tos)
t = t->sibling;
if (!t)
{
while (tab->sibling)
tab = tab->sibling;
t = mb_alloc(&root_pool, sizeof(rtable));
rt_setup(t, NULL);
tab->sibling = t;
t->tos = tos;
}
return (net *) fib_get(&t->fib, &mask, len);
}
rte *
rte_find(net *net, struct proto *p)
{
rte *e = net->routes;
while (e && e->attrs->proto != p)
e = e->next;
return e;
}
rte *
rte_get_temp(rta *a)
{
rte *e = sl_alloc(rte_slab);
e->attrs = a;
e->flags = 0;
e->pref = a->proto->preference;
return e;
}
static int /* Actually better or at least as good as */
rte_better(rte *new, rte *old)
{
int (*better)(rte *, rte *);
if (!old)
return 1;
if (new->pref > old->pref)
return 1;
if (new->pref < old->pref)
return 0;
if (new->attrs->proto != old->attrs->proto)
{
/* FIXME!!! */
die("Different protocols, but identical preferences => oops");
}
if (better = new->attrs->proto->rte_better)
return better(new, old);
return 0;
}
void
rte_announce(net *net, rte *new, rte *old)
{
struct proto *p;
WALK_LIST(p, proto_list)
if (p->rt_notify)
p->rt_notify(p, net, new, old);
}
void
rt_feed_baby(struct proto *p)
{
rtable *t = &master_table;
if (!p->rt_notify)
return;
debug("Announcing routes to new protocol %s\n", p->name);
while (t)
{
FIB_WALK(&t->fib, fn)
{
net *n = (net *) fn;
rte *e;
for(e=n->routes; e; e=e->next)
p->rt_notify(p, n, e, NULL);
}
FIB_WALK_END;
t = t->sibling;
}
}
void
rte_free(rte *e)
{
if (e->attrs->aflags & RTAF_CACHED)
rta_free(e->attrs);
sl_free(rte_slab, e);
}
static inline void
rte_free_quick(rte *e)
{
rta_free(e->attrs);
sl_free(rte_slab, e);
}
void
rte_update(net *net, struct proto *p, rte *new)
{
rte *old_best = net->routes;
rte *old = NULL;
rte **k, *r, *s;
if (new && !(new->attrs->aflags & RTAF_CACHED)) /* Need to copy attributes */
new->attrs = rta_lookup(new->attrs);
k = &net->routes; /* Find and remove original route from the same protocol */
while (old = *k)
{
if (old->attrs->proto == p)
{
*k = old->next;
break;
}
k = &old->next;
}
if (new && rte_better(new, old_best)) /* It's a new optimal route => announce and relink it */
{
rte_announce(net, new, old_best);
new->next = net->routes;
net->routes = new;
}
else
{
if (old == old_best) /* It has _replaced_ the old optimal route */
{
r = new; /* Find new optimal route and announce it */
for(s=net->routes; s; s=s->next)
if (rte_better(s, r))
r = s;
rte_announce(net, r, old_best);
if (r) /* Re-link the new optimal route */
{
k = &net->routes;
while (s = *k)
{
if (s == r)
{
*k = r->next;
break;
}
k = &s->next;
}
r->next = net->routes;
net->routes = r;
}
}
if (new) /* Link in the new non-optimal route */
{
new->next = old_best->next;
old_best->next = new;
}
}
if (old)
{
if (p->rte_remove)
p->rte_remove(net, old);
rte_free_quick(old);
}
if (new)
{
new->lastmod = now;
if (p->rte_insert)
p->rte_insert(net, new);
}
}
void
rte_discard(rte *old) /* Non-filtered route deletion, used during garbage collection */
{
rte_update(old->net, old->attrs->proto, NULL);
}
void
rte_dump(rte *e)
{
net *n = e->net;
if (n)
debug("%1I/%2d ", n->n.prefix, n->n.pxlen);
else
debug("??? ");
debug("PF=%02x pref=%d lm=%d ", e->pflags, e->pref, now-e->lastmod);
rta_dump(e->attrs);
if (e->flags & REF_CHOSEN)
debug(" [*]");
debug("\n");
}
void
rt_dump(rtable *t)
{
rte *e;
net *n;
debug("Dump of routing table <%s>\n", t->name);
while (t)
{
debug("Routes for TOS %02x:\n", t->tos);
FIB_WALK(&t->fib, fn)
{
n = (net *) fn;
for(e=n->routes; e; e=e->next)
rte_dump(e);
}
FIB_WALK_END;
t = t->sibling;
}
debug("\n");
}
void
rt_dump_all(void)
{
rt_dump(&master_table);
}
void
rt_init(void)
{
rta_init();
rt_setup(&master_table, "master");
rte_slab = sl_new(&root_pool, sizeof(rte));
}