summaryrefslogtreecommitdiff
path: root/nest/rt-table.c
diff options
context:
space:
mode:
Diffstat (limited to 'nest/rt-table.c')
-rw-r--r--nest/rt-table.c206
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)
{