diff options
Diffstat (limited to 'nest/rt-table.c')
-rw-r--r-- | nest/rt-table.c | 206 |
1 files changed, 152 insertions, 54 deletions
diff --git a/nest/rt-table.c b/nest/rt-table.c index b0781a33..fb2feaca 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -158,7 +158,7 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg) } static inline void -do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list *tmpa, int class) +do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, ea_list *tmpa, int class) { struct proto *p = a->proto; rte *new0 = new; @@ -167,16 +167,27 @@ do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list * if (new) { + p->stats.exp_updates_received++; + char *drop_reason = NULL; if ((class & IADDR_SCOPE_MASK) < p->min_scope) - drop_reason = "out of scope"; + { + p->stats.exp_updates_rejected++; + drop_reason = "out of scope"; + } else if ((ok = p->import_control ? p->import_control(p, &new, &tmpa, rte_update_pool) : 0) < 0) - drop_reason = "rejected by protocol"; + { + p->stats.exp_updates_rejected++; + drop_reason = "rejected by protocol"; + } else if (ok) rte_trace_out(D_FILTERS, p, new, "forced accept by protocol"); else if (p->out_filter == FILTER_REJECT || p->out_filter && f_run(p->out_filter, &new, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT) - drop_reason = "filtered out"; + { + p->stats.exp_updates_filtered++; + drop_reason = "filtered out"; + } if (drop_reason) { rte_trace_out(D_FILTERS, p, new, drop_reason); @@ -185,7 +196,10 @@ do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list * new = NULL; } } - if (old && p->out_filter) + else + p->stats.exp_withdraws_received++; + + if (old) { if (p->out_filter == FILTER_REJECT) old = NULL; @@ -193,7 +207,7 @@ do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list * { ea_list *tmpb = p->make_tmp_attrs ? p->make_tmp_attrs(old, rte_update_pool) : NULL; ok = p->import_control ? p->import_control(p, &old, &tmpb, rte_update_pool) : 0; - if (ok < 0 || (!ok && f_run(p->out_filter, &old, &tmpb, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)) + if (ok < 0 || (!ok && p->out_filter && f_run(p->out_filter, &old, &tmpb, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)) { if (old != old0) rte_free(old); @@ -201,6 +215,20 @@ do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list * } } } + + if (!new && !old) + return; + + if (new) + p->stats.exp_updates_accepted++; + else + p->stats.exp_withdraws_accepted++; + + if (new) + p->stats.exp_routes++; + if (old) + p->stats.exp_routes--; + if (p->debug & D_ROUTES) { if (new && old) @@ -210,8 +238,6 @@ do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list * else if (old) rte_trace_out(D_ROUTES, p, old, "removed"); } - if (!new && !old) - return; if (!new) p->rt_notify(p, net, NULL, old, NULL); else if (tmpa) @@ -234,31 +260,51 @@ do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list * /** * rte_announce - announce a routing table change * @tab: table the route has been added to + * @type: type of route announcement (RA_OPTIMAL or RA_ANY) * @net: network in question * @new: the new route to be announced - * @old: previous optimal route for the same network + * @old: the previous route for the same network * @tmpa: a list of temporary attributes belonging to the new route * * This function gets a routing table update and announces it - * to all protocols connected to the same table by their announcement hooks. + * to all protocols that acccepts given type of route announcement + * and are connected to the same table by their announcement hooks. * - * For each such protocol, we first call its import_control() hook which - * performs basic checks on the route (each protocol has a right to veto - * or force accept of the route before any filter is asked) and adds default - * values of attributes specific to the new protocol (metrics, tags etc.). - * Then it consults the protocol's export filter and if it accepts the - * route, the rt_notify() hook of the protocol gets called. + * Route announcement of type RA_OPTIMAL si generated when optimal + * route (in routing table @tab) changes. In that case @old stores the + * old optimal route. + * + * Route announcement of type RA_ANY si generated when any route (in + * routing table @tab) changes In that case @old stores the old route + * from the same protocol. + * + * For each appropriate protocol, we first call its import_control() + * hook which performs basic checks on the route (each protocol has a + * right to veto or force accept of the route before any filter is + * asked) and adds default values of attributes specific to the new + * protocol (metrics, tags etc.). Then it consults the protocol's + * export filter and if it accepts the route, the rt_notify() hook of + * the protocol gets called. */ static void -rte_announce(rtable *tab, net *net, rte *new, rte *old, ea_list *tmpa) +rte_announce(rtable *tab, int type, net *net, rte *new, rte *old, ea_list *tmpa) { struct announce_hook *a; int class = ipa_classify(net->n.prefix); + if (type == RA_OPTIMAL) + { + if (new) + new->attrs->proto->stats.pref_routes++; + if (old) + old->attrs->proto->stats.pref_routes--; + } + WALK_LIST(a, tab->hooks) { ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING); - do_rte_announce(a, net, new, old, tmpa, class); + if (a->proto->accept_ra_types == type) + do_rte_announce(a, type, net, new, old, tmpa, class); } } @@ -271,7 +317,7 @@ rte_validate(rte *e) if (ipa_nonzero(ipa_and(n->n.prefix, ipa_not(ipa_mkmask(n->n.pxlen))))) { log(L_BUG "Ignoring bogus prefix %I/%d received via %s", - n->n.prefix, n->n.pxlen, e->attrs->proto->name); + n->n.prefix, n->n.pxlen, e->sender->name); return 0; } if (n->n.pxlen) @@ -290,14 +336,14 @@ rte_validate(rte *e) return 1; } log(L_WARN "Ignoring bogus route %I/%d received via %s", - n->n.prefix, n->n.pxlen, e->attrs->proto->name); + n->n.prefix, n->n.pxlen, e->sender->name); return 0; } - if ((c & IADDR_SCOPE_MASK) < e->attrs->proto->min_scope) + if ((c & IADDR_SCOPE_MASK) < e->sender->min_scope) { log(L_WARN "Ignoring %s scope route %I/%d received from %I via %s", ip_scope_text(c & IADDR_SCOPE_MASK), - n->n.prefix, n->n.pxlen, e->attrs->from, e->attrs->proto->name); + n->n.prefix, n->n.pxlen, e->attrs->from, e->sender->name); return 0; } } @@ -337,7 +383,7 @@ rte_same(rte *x, rte *y) } static void -rte_recalculate(rtable *table, net *net, struct proto *p, rte *new, ea_list *tmpa) +rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte *new, ea_list *tmpa) { rte *old_best = net->routes; rte *old = NULL; @@ -346,11 +392,12 @@ rte_recalculate(rtable *table, net *net, struct proto *p, rte *new, ea_list *tmp k = &net->routes; /* Find and remove original route from the same protocol */ while (old = *k) { - if (old->attrs->proto == p) + if (old->attrs->proto == src) { if (new && rte_same(old, new)) { /* No changes, ignore the new route */ + p->stats.imp_updates_ignored++; rte_trace_in(D_ROUTES, p, new, "ignored"); rte_free_quick(new); old->lastmod = now; @@ -362,10 +409,28 @@ rte_recalculate(rtable *table, net *net, struct proto *p, rte *new, ea_list *tmp k = &old->next; } + if (!old && !new) + { + p->stats.imp_withdraws_ignored++; + return; + } + + if (new) + p->stats.imp_updates_accepted++; + else + p->stats.imp_withdraws_accepted++; + + if (new) + p->stats.imp_routes++; + if (old) + p->stats.imp_routes--; + + rte_announce(table, RA_ANY, net, new, old, tmpa); + if (new && rte_better(new, old_best)) /* It's a new optimal route => announce and relink it */ { rte_trace_in(D_ROUTES, p, new, "added [best]"); - rte_announce(table, net, new, old_best, tmpa); + rte_announce(table, RA_OPTIMAL, net, new, old_best, tmpa); new->next = net->routes; net->routes = new; } @@ -377,7 +442,7 @@ rte_recalculate(rtable *table, net *net, struct proto *p, rte *new, ea_list *tmp for(s=net->routes; s; s=s->next) if (rte_better(s, r)) r = s; - rte_announce(table, net, r, old_best, tmpa); + rte_announce(table, RA_OPTIMAL, net, r, old_best, tmpa); if (r) /* Re-link the new optimal route */ { k = &net->routes; @@ -447,6 +512,7 @@ rte_update_unlock(void) * @table: table to be updated * @net: network node * @p: protocol submitting the update + * @src: protocol originating the update * @new: a &rte representing the new route or %NULL for route removal. * * This function is called by the routing protocols whenever they discover @@ -457,6 +523,12 @@ rte_update_unlock(void) * rta_clone()), call rte_get_temp() to obtain a temporary &rte, fill in all * the appropriate data and finally submit the new &rte by calling rte_update(). * + * @src specifies the protocol that originally created the route and the meaning + * of protocol-dependent data of @new. If @new is not %NULL, @src have to be the + * same value as @new->attrs->proto. @p specifies the protocol that called + * rte_update(). In most cases it is the same protocol as @src. rte_update() + * stores @p in @new->sender; + * * When rte_update() gets any route, it automatically validates it (checks, * whether the network and next hop address are valid IP addresses and also * whether a normal routing protocol doesn't try to smuggle a host or link @@ -466,7 +538,7 @@ rte_update_unlock(void) * stores the temporary attributes back to the &rte. * * Now, having a "public" version of the route, we - * automatically find any old route defined by the protocol @p + * automatically find any old route defined by the protocol @src * for network @n, replace it by the new one (or removing it if @new is %NULL), * recalculate the optimal route for this destination and finally broadcast * the change (if any) to all routing protocols by calling rte_announce(). @@ -475,14 +547,16 @@ rte_update_unlock(void) * from a special linear pool @rte_update_pool and freed when rte_update() * finishes. */ + void -rte_update(rtable *table, net *net, struct proto *p, rte *new) +rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte *new) { ea_list *tmpa = NULL; rte_update_lock(); if (new) { + new->sender = p; struct filter *filter = p->in_filter; /* Do not filter routes going to the secondary side of the pipe, @@ -491,35 +565,42 @@ rte_update(rtable *table, net *net, struct proto *p, rte *new) if (p->table != table) filter = FILTER_ACCEPT; + p->stats.imp_updates_received++; if (!rte_validate(new)) { rte_trace_in(D_FILTERS, p, new, "invalid"); + p->stats.imp_updates_invalid++; goto drop; } if (filter == FILTER_REJECT) { + p->stats.imp_updates_filtered++; rte_trace_in(D_FILTERS, p, new, "filtered out"); goto drop; } - if (p->make_tmp_attrs) - tmpa = p->make_tmp_attrs(new, rte_update_pool); + if (src->make_tmp_attrs) + tmpa = src->make_tmp_attrs(new, rte_update_pool); if (filter) { ea_list *old_tmpa = tmpa; int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0); if (fr > F_ACCEPT) { + p->stats.imp_updates_filtered++; rte_trace_in(D_FILTERS, p, new, "filtered out"); goto drop; } - if (tmpa != old_tmpa && p->store_tmp_attrs) - p->store_tmp_attrs(new, tmpa); + if (tmpa != old_tmpa && src->store_tmp_attrs) + src->store_tmp_attrs(new, tmpa); } if (!(new->attrs->aflags & RTAF_CACHED)) /* Need to copy attributes */ new->attrs = rta_lookup(new->attrs); new->flags |= REF_COW; } - rte_recalculate(table, net, p, new, tmpa); + else + p->stats.imp_withdraws_received++; + + rte_recalculate(table, net, p, src, new, tmpa); rte_update_unlock(); return; @@ -531,11 +612,8 @@ drop: void rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used during garbage collection */ { - net *n = old->net; - struct proto *p = old->attrs->proto; - rte_update_lock(); - rte_recalculate(t, n, p, NULL, NULL); + rte_recalculate(t, old->net, old->sender, old->attrs->proto, NULL, NULL); rte_update_unlock(); } @@ -673,8 +751,8 @@ again: ncnt++; rescan: for (e=n->routes; e; e=e->next, rcnt++) - if (e->attrs->proto->core_state != FS_HAPPY && - e->attrs->proto->core_state != FS_FEEDING) + if (e->sender->core_state != FS_HAPPY && + e->sender->core_state != FS_FEEDING) { rte_discard(tab, e); rdel++; @@ -827,6 +905,18 @@ rt_commit(struct config *new, struct config *old) DBG("\tdone\n"); } +static inline void +do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e) +{ + struct proto *q = e->attrs->proto; + ea_list *tmpa; + + rte_update_lock(); + tmpa = q->make_tmp_attrs ? q->make_tmp_attrs(e, rte_update_pool) : NULL; + do_rte_announce(h, type, n, e, NULL, tmpa, ipa_classify(n->n.prefix)); + rte_update_unlock(); +} + /** * rt_feed_baby - advertise routes to a new protocol * @p: protocol to be fed @@ -865,19 +955,24 @@ again: FIB_ITERATE_PUT(fit, fn); return 0; } - if (e) - { - struct proto *q = e->attrs->proto; - ea_list *tmpa; - - if (p->core_state != FS_FEEDING) - return 1; /* In the meantime, the protocol fell down. */ - rte_update_lock(); - tmpa = q->make_tmp_attrs ? q->make_tmp_attrs(e, rte_update_pool) : NULL; - do_rte_announce(h, n, e, NULL, tmpa, ipa_classify(n->n.prefix)); - rte_update_unlock(); - max_feed--; - } + + if (p->accept_ra_types == RA_OPTIMAL) + if (e) + { + if (p->core_state != FS_FEEDING) + return 1; /* In the meantime, the protocol fell down. */ + do_feed_baby(p, RA_OPTIMAL, h, n, e); + max_feed--; + } + + if (p->accept_ra_types == RA_ANY) + for(e = n->routes; e != NULL; e = e->next) + { + if (p->core_state != FS_FEEDING) + return 1; /* In the meantime, the protocol fell down. */ + do_feed_baby(p, RA_ANY, h, n, e); + max_feed--; + } } FIB_ITERATE_END(fn); p->feed_ahook = h->next; @@ -988,8 +1083,11 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) if (p2 && p2 != p0) ok = 0; if (ok && d->export_mode) { - int ic = (p1->import_control ? p1->import_control(p1, &e, &tmpa, rte_update_pool) : 0); - if (ic < 0) + int class = ipa_classify(n->n.prefix); + int ic; + if ((class & IADDR_SCOPE_MASK) < p1->min_scope) + ok = 0; + else if ((ic = p1->import_control ? p1->import_control(p1, &e, &tmpa, rte_update_pool) : 0) < 0) ok = 0; else if (!ic && d->export_mode > 1) { |