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.c326
1 files changed, 242 insertions, 84 deletions
diff --git a/nest/rt-table.c b/nest/rt-table.c
index ecd6e324..8c91ea0a 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -77,7 +77,7 @@ net_route(rtable *tab, ip_addr a, int len)
{
a0 = ipa_and(a, ipa_mkmask(len));
n = fib_find(&tab->fib, &a0, len);
- if (n && n->routes)
+ if (n && rte_is_valid(n->routes))
return n;
len--;
}
@@ -147,8 +147,11 @@ rte_better(rte *new, rte *old)
{
int (*better)(rte *, rte *);
- if (!old)
+ if (!rte_is_valid(old))
return 1;
+ if (!rte_is_valid(new))
+ return 0;
+
if (new->pref > old->pref)
return 1;
if (new->pref < old->pref)
@@ -217,7 +220,8 @@ export_filter(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa,
goto reject;
stats->exp_updates_rejected++;
- rte_trace_out(D_FILTERS, p, rt, "rejected by protocol");
+ if (v == RIC_REJECT)
+ rte_trace_out(D_FILTERS, p, rt, "rejected by protocol");
goto reject;
}
if (v > 0)
@@ -289,7 +293,7 @@ do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tm
if (l && new)
{
if ((!old || refeed) && (stats->exp_routes >= l->limit))
- proto_notify_limit(ah, l, stats->exp_routes);
+ proto_notify_limit(ah, l, PLD_OUT, stats->exp_routes);
if (l->state == PLS_BLOCKED)
{
@@ -406,9 +410,13 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
rte *old_free = NULL;
rte *r;
- /* Used to track whether we met old_changed position. If it is NULL
- it was the first and met it implicitly before current best route. */
- int old_meet = (old_changed && !before_old) ? 1 : 0;
+ /* Used to track whether we met old_changed position. If before_old is NULL
+ old_changed was the first and we met it implicitly before current best route. */
+ int old_meet = old_changed && !before_old;
+
+ /* Note that before_old is either NULL or valid (not rejected) route.
+ If old_changed is valid, before_old have to be too. If old changed route
+ was not valid, caller must use NULL for both old_changed and before_old. */
if (new_changed)
stats->exp_updates_received++;
@@ -416,7 +424,7 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
stats->exp_withdraws_received++;
/* First, find the new_best route - first accepted by filters */
- for (r=net->routes; r; r=r->next)
+ for (r=net->routes; rte_is_valid(r); r=r->next)
{
if (new_best = export_filter(ah, r, &new_free, &tmpa, 0))
break;
@@ -435,7 +443,8 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
if (feed)
{
if (feed == 2) /* refeed */
- old_best = new_best ? new_best : net->routes;
+ old_best = new_best ? new_best :
+ (rte_is_valid(net->routes) ? net->routes : NULL);
else
old_best = NULL;
@@ -484,7 +493,7 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
}
/* Fourth case */
- for (r=r->next; r; r=r->next)
+ for (r=r->next; rte_is_valid(r); r=r->next)
{
if (old_best = export_filter(ah, r, &old_free, NULL, 1))
goto found;
@@ -538,7 +547,14 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
static void
rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *before_old, ea_list *tmpa)
{
- struct announce_hook *a;
+ if (!rte_is_valid(old))
+ old = before_old = NULL;
+
+ if (!rte_is_valid(new))
+ new = NULL;
+
+ if (!old && !new)
+ return;
if (type == RA_OPTIMAL)
{
@@ -551,6 +567,7 @@ rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *befo
rt_notify_hostcache(tab, net);
}
+ struct announce_hook *a;
WALK_LIST(a, tab->hooks)
{
ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING);
@@ -618,12 +635,15 @@ rte_same(rte *x, rte *y)
(!x->attrs->src->proto->rte_same || x->attrs->src->proto->rte_same(x, y));
}
+static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); }
+
static void
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;
struct proto_stats *stats = ah->stats;
+ static struct rate_limit rl_pipe;
rte *before_old = NULL;
rte *old_best = net->routes;
rte *old = NULL;
@@ -647,7 +667,7 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
{
if (new)
{
- log(L_ERR "Pipe collision detected when sending %I/%d to table %s",
+ log_rl(&rl_pipe, L_ERR "Pipe collision detected when sending %I/%d to table %s",
net->n.prefix, net->n.pxlen, table->name);
rte_free_quick(new);
}
@@ -657,8 +677,13 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
if (new && rte_same(old, new))
{
/* No changes, ignore the new route */
- stats->imp_updates_ignored++;
- rte_trace_in(D_ROUTES, p, new, "ignored");
+
+ if (!rte_is_filtered(new))
+ {
+ stats->imp_updates_ignored++;
+ rte_trace_in(D_ROUTES, p, new, "ignored");
+ }
+
rte_free_quick(new);
#ifdef CONFIG_RIP
/* lastmod is used internally by RIP as the last time
@@ -684,14 +709,22 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
return;
}
- struct proto_limit *l = ah->in_limit;
+ int new_ok = rte_is_ok(new);
+ int old_ok = rte_is_ok(old);
+
+ struct proto_limit *l = ah->rx_limit;
if (l && !old && new)
{
- if (stats->imp_routes >= l->limit)
- proto_notify_limit(ah, l, stats->imp_routes);
+ u32 all_routes = stats->imp_routes + stats->filt_routes;
+
+ if (all_routes >= l->limit)
+ proto_notify_limit(ah, l, PLD_RX, all_routes);
if (l->state == PLS_BLOCKED)
{
+ /* In receive limit the situation is simple, old is NULL so
+ we just free new and exit like nothing happened */
+
stats->imp_updates_ignored++;
rte_trace_in(D_FILTERS, p, new, "ignored [limit]");
rte_free_quick(new);
@@ -699,15 +732,53 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
}
}
- if (new)
+ l = ah->in_limit;
+ if (l && !old_ok && new_ok)
+ {
+ if (stats->imp_routes >= l->limit)
+ proto_notify_limit(ah, l, PLD_IN, stats->imp_routes);
+
+ if (l->state == PLS_BLOCKED)
+ {
+ /* In import limit the situation is more complicated. We
+ shouldn't just drop the route, we should handle it like
+ it was filtered. We also have to continue the route
+ processing if old or new is non-NULL, but we should exit
+ if both are NULL as this case is probably assumed to be
+ already handled. */
+
+ stats->imp_updates_ignored++;
+ rte_trace_in(D_FILTERS, p, new, "ignored [limit]");
+
+ if (ah->in_keep_filtered)
+ new->flags |= REF_FILTERED;
+ else
+ { rte_free_quick(new); new = NULL; }
+
+ /* Note that old && !new could be possible when
+ ah->in_keep_filtered changed in the recent past. */
+
+ if (!old && !new)
+ return;
+
+ new_ok = 0;
+ goto skip_stats1;
+ }
+ }
+
+ if (new_ok)
stats->imp_updates_accepted++;
- else
+ else if (old_ok)
stats->imp_withdraws_accepted++;
+ else
+ stats->imp_withdraws_ignored++;
+
+ skip_stats1:
if (new)
- stats->imp_routes++;
+ rte_is_filtered(new) ? stats->filt_routes++ : stats->imp_routes++;
if (old)
- stats->imp_routes--;
+ rte_is_filtered(old) ? stats->filt_routes-- : stats->imp_routes--;
if (table->config->sorted)
{
@@ -792,17 +863,19 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
new->lastmod = now;
/* Log the route change */
- if (new)
- rte_trace_in(D_ROUTES, p, new, net->routes == new ? "added [best]" : "added");
-
- if (!new && (p->debug & D_ROUTES))
+ if (p->debug & D_ROUTES)
{
- if (old != old_best)
- rte_trace_in(D_ROUTES, p, old, "removed");
- else if (net->routes)
- rte_trace_in(D_ROUTES, p, old, "removed [replaced]");
- else
- rte_trace_in(D_ROUTES, p, old, "removed [sole]");
+ if (new_ok)
+ rte_trace(p, new, '>', new == net->routes ? "added [best]" : "added");
+ else if (old_ok)
+ {
+ if (old != old_best)
+ rte_trace(p, old, '>', "removed");
+ else if (rte_is_ok(net->routes))
+ rte_trace(p, old, '>', "removed [replaced]");
+ else
+ rte_trace(p, old, '>', "removed [sole]");
+ }
}
/* Propagate the route change */
@@ -817,17 +890,13 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
(table->gc_time + table->config->gc_min_time <= now))
rt_schedule_gc(table);
+ if (old_ok && p->rte_remove)
+ p->rte_remove(net, old);
+ if (new_ok && p->rte_insert)
+ p->rte_insert(net, new);
+
if (old)
- {
- if (p->rte_remove)
- p->rte_remove(net, old);
- rte_free_quick(old);
- }
- if (new)
- {
- if (p->rte_insert)
- p->rte_insert(net, new);
- }
+ rte_free_quick(old);
}
static int rte_update_nest_cnt; /* Nesting counter to allow recursive updates */
@@ -845,6 +914,26 @@ rte_update_unlock(void)
lp_flush(rte_update_pool);
}
+static inline void
+rte_hide_dummy_routes(net *net, rte **dummy)
+{
+ if (net->routes && net->routes->attrs->source == RTS_DUMMY)
+ {
+ *dummy = net->routes;
+ net->routes = (*dummy)->next;
+ }
+}
+
+static inline void
+rte_unhide_dummy_routes(net *net, rte **dummy)
+{
+ if (*dummy)
+ {
+ (*dummy)->next = net->routes;
+ net->routes = *dummy;
+ }
+}
+
/**
* rte_update - enter a new update to a routing table
* @table: table to be updated
@@ -894,6 +983,7 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
struct proto_stats *stats = ah->stats;
struct filter *filter = ah->in_filter;
ea_list *tmpa = NULL;
+ rte *dummy = NULL;
rte_update_lock();
if (new)
@@ -907,28 +997,39 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
stats->imp_updates_invalid++;
goto drop;
}
+
if (filter == FILTER_REJECT)
{
stats->imp_updates_filtered++;
rte_trace_in(D_FILTERS, p, new, "filtered out");
- goto drop;
- }
- tmpa = make_tmp_attrs(new, rte_update_pool);
- if (filter)
+ if (! ah->in_keep_filtered)
+ goto drop;
+
+ /* new is a private copy, i could modify it */
+ new->flags |= REF_FILTERED;
+ }
+ else
{
- ea_list *old_tmpa = tmpa;
- int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0);
- if (fr > F_ACCEPT)
+ tmpa = make_tmp_attrs(new, rte_update_pool);
+ if (filter && (filter != FILTER_REJECT))
{
- stats->imp_updates_filtered++;
- rte_trace_in(D_FILTERS, p, new, "filtered out");
- goto drop;
+ ea_list *old_tmpa = tmpa;
+ int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0);
+ if (fr > F_ACCEPT)
+ {
+ stats->imp_updates_filtered++;
+ rte_trace_in(D_FILTERS, p, new, "filtered out");
+
+ if (! ah->in_keep_filtered)
+ goto drop;
+
+ new->flags |= REF_FILTERED;
+ }
+ if (tmpa != old_tmpa && src->proto->store_tmp_attrs)
+ src->proto->store_tmp_attrs(new, tmpa);
}
- if (tmpa != old_tmpa && src->proto->store_tmp_attrs)
- src->proto->store_tmp_attrs(new, tmpa);
}
-
if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
new->attrs = rta_lookup(new->attrs);
new->flags |= REF_COW;
@@ -945,14 +1046,18 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
}
}
+ recalc:
+ rte_hide_dummy_routes(net, &dummy);
rte_recalculate(ah, net, new, tmpa, src);
+ rte_unhide_dummy_routes(net, &dummy);
rte_update_unlock();
return;
-drop:
+ drop:
rte_free(new);
- rte_recalculate(ah, net, NULL, NULL, src);
- rte_update_unlock();
+ new = NULL;
+ tmpa = NULL;
+ goto recalc;
}
/* Independent call to rte_announce(), used from next hop
@@ -976,6 +1081,33 @@ rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used during gar
rte_update_unlock();
}
+/* Check rtable for best route to given net whether it would be exported do p */
+int
+rt_examine(rtable *t, ip_addr prefix, int pxlen, struct proto *p, struct filter *filter)
+{
+ net *n = net_find(t, prefix, pxlen);
+ rte *rt = n ? n->routes : NULL;
+
+ if (!rte_is_valid(rt))
+ return 0;
+
+ rte_update_lock();
+
+ /* Rest is stripped down export_filter() */
+ ea_list *tmpa = make_tmp_attrs(rt, rte_update_pool);
+ int v = p->import_control ? p->import_control(p, &rt, &tmpa, rte_update_pool) : 0;
+ if (v == RIC_PROCESS)
+ v = (f_run(filter, &rt, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
+
+ /* Discard temporary rte */
+ if (rt != n->routes)
+ rte_free(rt);
+
+ rte_update_unlock();
+
+ return v > 0;
+}
+
/**
* rte_dump - dump a route
* @e: &rte to be dumped
@@ -1151,20 +1283,10 @@ rt_init(void)
}
-/* Called from proto_schedule_flush_loop() only,
- ensuring that all prune states are zero */
-void
-rt_schedule_prune_all(void)
-{
- rtable *t;
-
- WALK_LIST(t, routing_tables)
- t->prune_state = 1;
-}
-
static inline int
-rt_prune_step(rtable *tab, int *max_feed)
+rt_prune_step(rtable *tab, int step, int *max_feed)
{
+ static struct rate_limit rl_flush;
struct fib_iterator *fit = &tab->prune_fit;
DBG("Pruning route table %s\n", tab->name);
@@ -1189,8 +1311,8 @@ again:
rescan:
for (e=n->routes; e; e=e->next)
- if (e->sender->proto->core_state != FS_HAPPY &&
- e->sender->proto->core_state != FS_FEEDING)
+ if (e->sender->proto->flushing ||
+ (step && e->attrs->src->proto->flushing))
{
if (*max_feed <= 0)
{
@@ -1198,6 +1320,10 @@ again:
return 0;
}
+ if (step)
+ log_rl(&rl_flush, L_WARN "Route %I/%d from %s still in %s after flush",
+ n->n.prefix, n->n.pxlen, e->attrs->src->proto->name, tab->name);
+
rte_discard(tab, e);
(*max_feed)--;
@@ -1222,23 +1348,42 @@ again:
/**
* rt_prune_loop - prune routing tables
- * @tab: routing table to be pruned
*
* The prune loop scans routing tables and removes routes belonging to
- * inactive protocols and also stale network entries. Returns 1 when
+ * flushing protocols and also stale network entries. Returns 1 when
* all such routes are pruned. It is a part of the protocol flushing
* loop.
+ *
+ * The prune loop runs in two steps. In the first step it prunes just
+ * the routes with flushing senders (in explicitly marked tables) so
+ * the route removal is propagated as usual. In the second step, all
+ * remaining relevant routes are removed. Ideally, there shouldn't be
+ * any, but it happens when pipe filters are changed.
*/
int
rt_prune_loop(void)
{
- rtable *t;
+ static int step = 0;
int max_feed = 512;
+ rtable *t;
+ again:
WALK_LIST(t, routing_tables)
- if (! rt_prune_step(t, &max_feed))
+ if (! rt_prune_step(t, step, &max_feed))
return 0;
+ if (step == 0)
+ {
+ /* Prepare for the second step */
+ WALK_LIST(t, routing_tables)
+ t->prune_state = 1;
+
+ step = 1;
+ goto again;
+ }
+
+ /* Done */
+ step = 0;
return 1;
}
@@ -1570,9 +1715,11 @@ again:
return 0;
}
+ /* XXXX perhaps we should change feed for RA_ACCEPTED to not use 'new' */
+
if ((p->accept_ra_types == RA_OPTIMAL) ||
(p->accept_ra_types == RA_ACCEPTED))
- if (e)
+ if (rte_is_valid(e))
{
if (p->core_state != FS_FEEDING)
return 1; /* In the meantime, the protocol fell down. */
@@ -1581,7 +1728,7 @@ again:
}
if (p->accept_ra_types == RA_ANY)
- for(e = n->routes; e != NULL; e = e->next)
+ for(e = n->routes; rte_is_valid(e); e = e->next)
{
if (p->core_state != FS_FEEDING)
return 1; /* In the meantime, the protocol fell down. */
@@ -1834,7 +1981,8 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
net *n = net_route(tab, he->addr, MAX_PREFIX_LENGTH);
if (n)
{
- rta *a = n->routes->attrs;
+ rte *e = n->routes;
+ rta *a = e->attrs;
pxlen = n->n.pxlen;
if (a->hostentry)
@@ -1867,7 +2015,7 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
}
he->src = rta_clone(a);
- he->igp_metric = rt_get_igp_metric(n->routes);
+ he->igp_metric = rt_get_igp_metric(e);
}
done:
@@ -2001,19 +2149,24 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
int ok;
bsprintf(ia, "%I/%d", n->n.prefix, n->n.pxlen);
- if (n->routes)
- d->net_counter++;
+
for(e=n->routes; e; e=e->next)
{
+ if (rte_is_filtered(e) != d->filtered)
+ continue;
+
struct ea_list *tmpa;
struct rte_src *src = e->attrs->src;
struct proto *p1 = d->export_protocol;
struct proto *p2 = d->show_protocol;
+
+ if (ia[0])
+ d->net_counter++;
d->rt_counter++;
ee = e;
rte_update_lock(); /* We use the update buffer for filtering */
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);
+ ok = f_run(d->filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT;
if (p2 && p2 != src->proto) ok = 0;
if (ok && d->export_mode)
{
@@ -2027,8 +2180,8 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
'configure soft' command may change the export filter
and do not update routes */
- if ((a = proto_find_announce_hook(p1, d->table)) && ((a->out_filter == FILTER_REJECT) ||
- (a->out_filter && f_run(a->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)))
+ if ((a = proto_find_announce_hook(p1, d->table)) &&
+ (f_run(a->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT))
ok = 0;
}
}
@@ -2107,6 +2260,11 @@ rt_show(struct rt_show_data *d)
{
net *n;
+ /* Default is either a master table or a table related to a respective protocol */
+ if ((!d->table) && d->export_protocol) d->table = d->export_protocol->table;
+ if ((!d->table) && d->show_protocol) d->table = d->show_protocol->table;
+ if (!d->table) d->table = config->master_rtc->table;
+
if (d->pxlen == 256)
{
FIB_ITERATE_INIT(&d->fit, &d->table->fib);