diff options
Diffstat (limited to 'sysdep/unix/krt.c')
-rw-r--r-- | sysdep/unix/krt.c | 256 |
1 files changed, 239 insertions, 17 deletions
diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 609ee921..40a58442 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -285,24 +285,249 @@ krt_metric(rte *a) } static inline int -krt_rte_better(rte *a, rte *b) +krt_same_key(rte *a, rte *b) { - return (krt_metric(a) > krt_metric(b)); + return (krt_metric(a) == krt_metric(b)); +} + +static inline int +krt_uptodate(rte *a, rte *b) +{ + return (a->attrs == b->attrs); +} + +static void +krt_learn_announce_update(struct krt_proto *p, rte *e) +{ + rte e0 = { + .attrs = rta_clone(e->attrs), + .src = p->p.main_source, + }; + + rte_update(p->p.main_channel, e->net, &e0, p->p.main_source); +} + +static void +krt_learn_announce_delete(struct krt_proto *p, net_addr *n) +{ + rte_update(p->p.main_channel, n, NULL, p->p.main_source); } /* Called when alien route is discovered during scan */ static void -krt_learn_rte(struct krt_proto *p, rte *e) +krt_learn_scan(struct krt_proto *p, rte *e) +{ + net *n = net_get(p->krt_table, e->net); + struct rte_storage *m, **mm; + + struct rte_storage *ee = rte_store(e, n, p->krt_table); + + for(mm = &n->routes; m = *mm; mm = &m->next) + if (krt_same_key(&m->rte, e)) + break; + if (m) + { + if (krt_uptodate(&m->rte, e)) + { + krt_trace_in_rl(&rl_alien, p, e, "[alien] seen"); + rte_free(ee, p->krt_table); + m->rte.pflags |= KRT_REF_SEEN; + } + else + { + krt_trace_in(p, e, "[alien] updated"); + *mm = m->next; + rte_free(m, p->krt_table); + m = NULL; + } + } + else + krt_trace_in(p, e, "[alien] created"); + + if (!m) + { + ee->next = n->routes; + n->routes = ee; + ee->rte.pflags |= KRT_REF_SEEN; + } +} + +static void +krt_learn_prune(struct krt_proto *p) { - e->src = rt_get_source(&p->p, krt_metric(e)); - rte_update(p->p.main_channel, e->net, e, e->src); + struct fib *fib = &p->krt_table->fib; + struct fib_iterator fit; + + KRT_TRACE(p, D_EVENTS, "Pruning inherited routes"); + + FIB_ITERATE_INIT(&fit, fib); +again: + FIB_ITERATE_START(fib, &fit, net, n) + { + struct rte_storage *e, **ee, *best, **pbest, *old_best; + + /* + * Note that old_best may be NULL even if there was an old best route in + * the previous step, because it might be replaced in krt_learn_scan(). + * But in that case there is a new valid best route. + */ + + old_best = NULL; + best = NULL; + pbest = NULL; + ee = &n->routes; + while (e = *ee) + { + if (e->rte.pflags & KRT_REF_BEST) + old_best = e; + + if (!(e->rte.pflags & KRT_REF_SEEN)) + { + *ee = e->next; + rte_free(e, p->krt_table); + continue; + } + + if (!best || krt_metric(&best->rte) > krt_metric(&e->rte)) + { + best = e; + pbest = ee; + } + + e->rte.pflags &= ~(KRT_REF_SEEN | KRT_REF_BEST); + ee = &e->next; + } + if (!n->routes) + { + DBG("%I/%d: deleting\n", n->n.prefix, n->n.pxlen); + if (old_best) + krt_learn_announce_delete(p, n->n.addr); + + FIB_ITERATE_PUT(&fit); + fib_delete(fib, n); + goto again; + } + + best->rte.pflags |= KRT_REF_BEST; + *pbest = best->next; + best->next = n->routes; + n->routes = best; + + if ((best != old_best) || p->reload) + { + DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, krt_metric(&best->rte)); + krt_learn_announce_update(p, &best->rte); + } + else + DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, krt_metric(&best->rte)); + } + FIB_ITERATE_END; + + p->reload = 0; +} + +static void +krt_learn_async(struct krt_proto *p, rte *e, int new) +{ + net *n = net_get(p->krt_table, e->net); + struct rte_storage *g, **gg, *best, **bestp, *old_best; + + ASSERT(!e->attrs->cached); + e->attrs->pref = p->p.main_channel->preference; + + struct rte_storage *ee = rte_store(e, n, p->krt_table); + + old_best = n->routes; + for(gg=&n->routes; g = *gg; gg = &g->next) + if (krt_same_key(&g->rte, e)) + break; + if (new) + { + if (g) + { + if (krt_uptodate(&g->rte, e)) + { + krt_trace_in(p, e, "[alien async] same"); + rte_free(ee, p->krt_table); + return; + } + krt_trace_in(p, e, "[alien async] updated"); + *gg = g->next; + rte_free(g, p->krt_table); + } + else + krt_trace_in(p, e, "[alien async] created"); + + ee->next = n->routes; + n->routes = ee; + } + else if (!g) + { + krt_trace_in(p, e, "[alien async] delete failed"); + rte_free(ee, p->krt_table); + return; + } + else + { + krt_trace_in(p, e, "[alien async] removed"); + *gg = g->next; + rte_free(ee, p->krt_table); + rte_free(g, p->krt_table); + } + best = n->routes; + bestp = &n->routes; + for(gg=&n->routes; g=*gg; gg=&g->next) + { + if (krt_metric(&best->rte) > krt_metric(&g->rte)) + { + best = g; + bestp = gg; + } + + g->rte.pflags &= ~KRT_REF_BEST; + } + + if (best) + { + best->rte.pflags |= KRT_REF_BEST; + *bestp = best->next; + best->next = n->routes; + n->routes = best; + } + + if (best != old_best) + { + DBG("krt_learn_async: distributing change\n"); + if (best) + krt_learn_announce_update(p, &best->rte); + else + krt_learn_announce_delete(p, n->n.addr); + } } static void krt_learn_init(struct krt_proto *p) { if (KRT_CF->learn) - channel_setup_in_table(p->p.main_channel, 1); + { + struct rtable_config *cf = mb_allocz(p->p.pool, sizeof(struct rtable_config)); + cf->name = "Inherited"; + cf->addr_type = p->p.net_type; + cf->internal = 1; + + p->krt_table = rt_setup(p->p.pool, cf); + } +} + +static void +krt_dump(struct proto *P) +{ + struct krt_proto *p = (struct krt_proto *) P; + + if (!KRT_CF->learn) + return; + debug("KRT: Table of inheritable routes\n"); + rt_dump(p->krt_table); } #endif @@ -322,7 +547,7 @@ rte_feed_count(net *n) { uint count = 0; for (struct rte_storage *e = n->routes; e; e = e->next) - if (rte_is_valid(RTES_OR_NULL(e))) + if (rte_is_valid(RTE_OR_NULL(e))) count++; return count; } @@ -332,7 +557,7 @@ rte_feed_obtain(net *n, rte **feed, uint count) { uint i = 0; for (struct rte_storage *e = n->routes; e; e = e->next) - if (rte_is_valid(RTES_OR_NULL(e))) + if (rte_is_valid(RTE_OR_NULL(e))) { ASSERT_DIE(i < count); feed[i++] = &e->rte; @@ -418,7 +643,7 @@ krt_got_route(struct krt_proto *p, rte *e, s8 src) case KRT_SRC_ALIEN: if (KRT_CF->learn) - krt_learn_rte(p, e); + krt_learn_scan(p, e); else krt_trace_in_rl(&rl_alien, p, e, "[alien] ignored"); return; @@ -487,11 +712,6 @@ static void krt_init_scan(struct krt_proto *p) { bmap_reset(&p->seen_map, 1024); - -#ifdef KRT_ALLOW_LEARN - if (KRT_CF->learn) - channel_refresh_begin(p->p.main_channel); -#endif } static void @@ -519,7 +739,7 @@ krt_prune(struct krt_proto *p) #ifdef KRT_ALLOW_LEARN if (KRT_CF->learn) - channel_refresh_end(p->p.main_channel); + krt_learn_prune(p); #endif if (p->ready) @@ -561,7 +781,7 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new, s8 src) case KRT_SRC_ALIEN: if (KRT_CF->learn) { - krt_learn_rte(p, e); + krt_learn_async(p, e, new); return; } #endif @@ -807,7 +1027,6 @@ krt_init(struct proto_config *CF) p->p.if_notify = krt_if_notify; p->p.reload_routes = krt_reload_routes; p->p.feed_end = krt_feed_end; - p->p.rte_better = krt_rte_better; krt_sys_init(p); return &p->p; @@ -963,4 +1182,7 @@ struct protocol proto_unix_kernel = { .reconfigure = krt_reconfigure, .copy_config = krt_copy_config, .get_attr = krt_get_attr, +#ifdef KRT_ALLOW_LEARN + .dump = krt_dump, +#endif }; |