From 094d2bdb79e1ffa0a02761fd651aa0f0b6b0c585 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 14 Aug 2012 16:25:22 +0200 Subject: Implements ADD-PATH extension for BGP. Allows to send and receive multiple routes for one network by one BGP session. Also contains necessary core changes to support this (routing tables accepting several routes for one network from one protocol). It needs some more cleanup before merging to the master branch. --- nest/rt-table.c | 111 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 45 deletions(-) (limited to 'nest/rt-table.c') diff --git a/nest/rt-table.c b/nest/rt-table.c index 118f4c25..ecd6e324 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -58,6 +58,14 @@ static void rt_next_hop_update(rtable *tab); static inline void rt_schedule_gc(rtable *tab); +static inline struct ea_list * +make_tmp_attrs(struct rte *rt, struct linpool *pool) +{ + struct ea_list *(*mta)(struct rte *rt, struct linpool *pool); + mta = rt->attrs->src->proto->make_tmp_attrs; + return mta ? mta(rt, rte_update_pool) : NULL; +} + /* Like fib_route(), but skips empty net entries */ static net * net_route(rtable *tab, ip_addr a, int len) @@ -88,17 +96,17 @@ rte_init(struct fib_node *N) /** * rte_find - find a route * @net: network node - * @p: protocol + * @src: route source * * The rte_find() function returns a route for destination @net - * which belongs has been defined by protocol @p. + * which is from route source @src. */ rte * -rte_find(net *net, struct proto *p) +rte_find(net *net, struct rte_src *src) { rte *e = net->routes; - while (e && e->attrs->proto != p) + while (e && e->attrs->src != src) e = e->next; return e; } @@ -119,7 +127,7 @@ rte_get_temp(rta *a) e->attrs = a; e->flags = 0; - e->pref = a->proto->preference; + e->pref = a->src->proto->preference; return e; } @@ -145,16 +153,16 @@ rte_better(rte *new, rte *old) return 1; if (new->pref < old->pref) return 0; - if (new->attrs->proto->proto != old->attrs->proto->proto) + if (new->attrs->src->proto->proto != old->attrs->src->proto->proto) { /* * If the user has configured protocol preferences, so that two different protocols * have the same preference, try to break the tie by comparing addresses. Not too * useful, but keeps the ordering of routes unambiguous. */ - return new->attrs->proto->proto > old->attrs->proto->proto; + return new->attrs->src->proto->proto > old->attrs->src->proto->proto; } - if (better = new->attrs->proto->rte_better) + if (better = new->attrs->src->proto->rte_better) return better(new, old); return 0; } @@ -198,8 +206,7 @@ export_filter(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa, /* If called does not care for eattrs, we prepare one internally */ if (!tmpa) { - struct proto *src = rt->attrs->proto; - tmpb = src->make_tmp_attrs ? src->make_tmp_attrs(rt, rte_update_pool) : NULL; + tmpb = make_tmp_attrs(rt, rte_update_pool); tmpa = &tmpb; } @@ -536,9 +543,9 @@ rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *befo if (type == RA_OPTIMAL) { if (new) - new->attrs->proto->stats.pref_routes++; + new->attrs->src->proto->stats.pref_routes++; if (old) - old->attrs->proto->stats.pref_routes--; + old->attrs->src->proto->stats.pref_routes--; if (tab->hostcache) rt_notify_hostcache(tab, net); @@ -588,7 +595,7 @@ rte_validate(rte *e) void rte_free(rte *e) { - if (e->attrs->aflags & RTAF_CACHED) + if (rta_is_cached(e->attrs)) rta_free(e->attrs); sl_free(rte_slab, e); } @@ -608,11 +615,11 @@ rte_same(rte *x, rte *y) x->flags == y->flags && x->pflags == y->pflags && x->pref == y->pref && - (!x->attrs->proto->rte_same || x->attrs->proto->rte_same(x, y)); + (!x->attrs->src->proto->rte_same || x->attrs->src->proto->rte_same(x, y)); } static void -rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, struct proto *src) +rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, struct rte_src *src) { struct proto *p = ah->proto; struct rtable *table = ah->table; @@ -625,7 +632,7 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str k = &net->routes; /* Find and remove original route from the same protocol */ while (old = *k) { - if (old->attrs->proto == src) + if (old->attrs->src == src) { /* If there is the same route in the routing table but from * a different sender, then there are two paths from the @@ -656,7 +663,7 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str #ifdef CONFIG_RIP /* lastmod is used internally by RIP as the last time when the route was received. */ - if (src->proto == &proto_rip) + if (src->proto->proto == &proto_rip) old->lastmod = now; #endif return; @@ -725,7 +732,7 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str /* If routes are not sorted, find the best route and move it on the first position. There are several optimized cases. */ - if (src->rte_recalculate && src->rte_recalculate(table, net, new, old, old_best)) + if (src->proto->rte_recalculate && src->proto->rte_recalculate(table, net, new, old, old_best)) goto do_recalculate; if (new && rte_better(new, old_best)) @@ -881,7 +888,7 @@ rte_update_unlock(void) */ void -rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src) +rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src) { struct proto *p = ah->proto; struct proto_stats *stats = ah->stats; @@ -906,8 +913,8 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src) rte_trace_in(D_FILTERS, p, new, "filtered out"); goto drop; } - if (src->make_tmp_attrs) - tmpa = src->make_tmp_attrs(new, rte_update_pool); + + tmpa = make_tmp_attrs(new, rte_update_pool); if (filter) { ea_list *old_tmpa = tmpa; @@ -918,15 +925,25 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src) rte_trace_in(D_FILTERS, p, new, "filtered out"); goto drop; } - if (tmpa != old_tmpa && src->store_tmp_attrs) - src->store_tmp_attrs(new, tmpa); + if (tmpa != old_tmpa && src->proto->store_tmp_attrs) + src->proto->store_tmp_attrs(new, tmpa); } - if (!(new->attrs->aflags & RTAF_CACHED)) /* Need to copy attributes */ + + if (!rta_is_cached(new->attrs)) /* Need to copy attributes */ new->attrs = rta_lookup(new->attrs); new->flags |= REF_COW; } else - stats->imp_withdraws_received++; + { + stats->imp_withdraws_received++; + + if (!net || !src) + { + stats->imp_withdraws_ignored++; + rte_update_unlock(); + return; + } + } rte_recalculate(ah, net, new, tmpa, src); rte_update_unlock(); @@ -943,12 +960,10 @@ drop: static inline void rte_announce_i(rtable *tab, unsigned type, net *n, rte *new, rte *old) { - struct proto *src; ea_list *tmpa; rte_update_lock(); - src = new->attrs->proto; - tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(new, rte_update_pool) : NULL; + tmpa = make_tmp_attrs(new, rte_update_pool); rte_announce(tab, type, n, new, old, NULL, tmpa); rte_update_unlock(); } @@ -957,7 +972,7 @@ void rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used during garbage collection */ { rte_update_lock(); - rte_recalculate(old->sender, old->net, NULL, NULL, old->attrs->proto); + rte_recalculate(old->sender, old->net, NULL, NULL, old->attrs->src); rte_update_unlock(); } @@ -974,8 +989,8 @@ rte_dump(rte *e) debug("%-1I/%2d ", n->n.prefix, n->n.pxlen); debug("KF=%02x PF=%02x pref=%d lm=%d ", n->n.flags, e->pflags, e->pref, now-e->lastmod); rta_dump(e->attrs); - if (e->attrs->proto->proto->dump_attrs) - e->attrs->proto->proto->dump_attrs(e); + if (e->attrs->src->proto->proto->dump_attrs) + e->attrs->src->proto->proto->dump_attrs(e); debug("\n"); } @@ -1096,7 +1111,10 @@ rt_event(void *ptr) rt_next_hop_update(tab); if (tab->gc_scheduled) - rt_prune_nets(tab); + { + rt_prune_nets(tab); + rt_prune_sources(); // FIXME this should be moved to independent event + } } void @@ -1303,8 +1321,8 @@ rt_next_hop_update_net(rtable *tab, net *n) /* Call a pre-comparison hook */ /* Not really an efficient way to compute this */ - if (e->attrs->proto->rte_recalculate) - e->attrs->proto->rte_recalculate(tab, n, new, e, NULL); + if (e->attrs->src->proto->rte_recalculate) + e->attrs->src->proto->rte_recalculate(tab, n, new, e, NULL); if (e != old_best) rte_free_quick(e); @@ -1502,11 +1520,10 @@ rt_commit(struct config *new, struct config *old) static inline void do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e) { - struct proto *src = e->attrs->proto; ea_list *tmpa; rte_update_lock(); - tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(e, rte_update_pool) : NULL; + tmpa = make_tmp_attrs(e, rte_update_pool); if (type == RA_ACCEPTED) rt_notify_accepted(h, n, e, NULL, NULL, tmpa, p->refeeding ? 2 : 1); else @@ -1889,7 +1906,7 @@ rt_update_hostcache(rtable *tab) } static struct hostentry * -rt_find_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep) +rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep) { struct hostentry *he; @@ -1910,9 +1927,10 @@ rt_find_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep) void rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr *gw, ip_addr *ll) { - rta_apply_hostentry(a, rt_find_hostentry(tab, *gw, *ll, dep)); + rta_apply_hostentry(a, rt_get_hostentry(tab, *gw, *ll, dep)); } + /* * CLI commands */ @@ -1942,6 +1960,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm rta *a = e->attrs; int primary = (e->net->routes == e); int sync_error = (e->net->n.flags & KRF_SYNC_ERROR); + void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs); struct mpnh *nh; rt_format_via(e, via); @@ -1950,7 +1969,9 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm bsprintf(from, " from %I", a->from); else from[0] = 0; - if (a->proto->proto->get_route_info || d->verbose) + + get_route_info = a->src->proto->proto->get_route_info; + if (get_route_info || d->verbose) { /* Need to normalize the extended attributes */ ea_list *t = tmpa; @@ -1959,11 +1980,11 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm ea_merge(t, tmpa); ea_sort(tmpa); } - if (a->proto->proto->get_route_info) - a->proto->proto->get_route_info(e, info, tmpa); + if (get_route_info) + get_route_info(e, info, tmpa); else bsprintf(info, " (%d)", e->pref); - cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, via, a->proto->name, + cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, via, a->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info); for (nh = a->nexthops; nh; nh = nh->next) cli_printf(c, -1007, "\tvia %I on %s weight %d", nh->gw, nh->iface->name, nh->weight + 1); @@ -1985,15 +2006,15 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) for(e=n->routes; e; e=e->next) { struct ea_list *tmpa; - struct proto *p0 = e->attrs->proto; + struct rte_src *src = e->attrs->src; struct proto *p1 = d->export_protocol; struct proto *p2 = d->show_protocol; d->rt_counter++; ee = e; rte_update_lock(); /* We use the update buffer for filtering */ - tmpa = p0->make_tmp_attrs ? p0->make_tmp_attrs(e, rte_update_pool) : NULL; + tmpa = make_tmp_attrs(e, rte_update_pool); ok = (d->filter == FILTER_ACCEPT || f_run(d->filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT); - if (p2 && p2 != p0) ok = 0; + if (p2 && p2 != src->proto) ok = 0; if (ok && d->export_mode) { int ic; -- cgit v1.2.3