#include "nest/bird.h"
#include "nest/route.h"
#include "nest/protocol.h"
-#include "nest/cli.h"
#include "nest/iface.h"
#include "lib/resource.h"
#include "lib/event.h"
#include "lib/string.h"
#include "conf/conf.h"
#include "filter/filter.h"
+#include "lib/hash.h"
#include "lib/string.h"
#include "lib/alloca.h"
static slab *rte_slab;
static linpool *rte_update_pool;
-static list routing_tables;
+list routing_tables;
-static byte *rt_format_via(rte *e);
static void rt_free_hostcache(rtable *tab);
static void rt_notify_hostcache(rtable *tab, net *net);
static void rt_update_hostcache(rtable *tab);
static inline void rt_prune_table(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, pool) : NULL;
-}
-
-
/* Like fib_route(), but skips empty net entries */
static inline void *
-net_route_ip4(struct fib *f, net_addr_ip4 *n)
+net_route_ip4(rtable *t, net_addr_ip4 *n)
{
net *r;
- while (r = fib_find(f, (net_addr *) n),
- !(r && rte_is_valid(r->routes)) && (n->pxlen > 0))
+ while (r = net_find_valid(t, (net_addr *) n), (!r) && (n->pxlen > 0))
{
n->pxlen--;
ip4_clrbit(&n->prefix, n->pxlen);
}
static inline void *
-net_route_ip6(struct fib *f, net_addr_ip6 *n)
+net_route_ip6(rtable *t, net_addr_ip6 *n)
{
net *r;
- while (r = fib_find(f, (net_addr *) n),
- !(r && rte_is_valid(r->routes)) && (n->pxlen > 0))
+ while (r = net_find_valid(t, (net_addr *) n), (!r) && (n->pxlen > 0))
{
n->pxlen--;
ip6_clrbit(&n->prefix, n->pxlen);
return r;
}
+static inline void *
+net_route_ip6_sadr(rtable *t, net_addr_ip6_sadr *n)
+{
+ struct fib_node *fn;
+
+ while (1)
+ {
+ net *best = NULL;
+ int best_pxlen = 0;
+
+ /* We need to do dst first matching. Since sadr addresses are hashed on dst
+ prefix only, find the hash table chain and go through it to find the
+ match with the smallest matching src prefix. */
+ for (fn = fib_get_chain(&t->fib, (net_addr *) n); fn; fn = fn->next)
+ {
+ net_addr_ip6_sadr *a = (void *) fn->addr;
+
+ if (net_equal_dst_ip6_sadr(n, a) &&
+ net_in_net_src_ip6_sadr(n, a) &&
+ (a->src_pxlen >= best_pxlen))
+ {
+ best = fib_node_to_user(&t->fib, fn);
+ best_pxlen = a->src_pxlen;
+ }
+ }
+
+ if (best)
+ return best;
+
+ if (!n->dst_pxlen)
+ break;
+
+ n->dst_pxlen--;
+ ip6_clrbit(&n->dst_prefix, n->dst_pxlen);
+ }
+
+ return NULL;
+}
+
void *
net_route(rtable *tab, const net_addr *n)
{
case NET_IP4:
case NET_VPN4:
case NET_ROA4:
- return net_route_ip4(&tab->fib, (net_addr_ip4 *) n0);
+ return net_route_ip4(tab, (net_addr_ip4 *) n0);
case NET_IP6:
case NET_VPN6:
case NET_ROA6:
- return net_route_ip6(&tab->fib, (net_addr_ip6 *) n0);
+ return net_route_ip6(tab, (net_addr_ip6 *) n0);
+
+ case NET_IP6_SADR:
+ return net_route_ip6_sadr(tab, (net_addr_ip6_sadr *) n0);
default:
return NULL;
if (!rta_is_cached(r->attrs))
return r;
- rte *e = rte_cow(r);
+ r = rte_cow(r);
rta *a = rta_do_cow(r->attrs, lp);
- rta_free(e->attrs);
- e->attrs = a;
- return e;
+ rta_free(r->attrs);
+ r->attrs = a;
+ return r;
+}
+
+
+/* Note that rte_make_tmp_attr() requires free eattr in ea_list */
+void
+rte_make_tmp_attr(rte *r, ea_list *e, uint id, uint type, u32 val)
+{
+ if (r->pflags & EA_ID_FLAG(id))
+ {
+ eattr *a = &e->attrs[e->count++];
+ a->id = id;
+ a->type = type | EAF_TEMP;
+ a->flags = 0;
+ a->u.data = val;
+ }
+}
+
+/* Note that rte has to be writable */
+uint
+rte_store_tmp_attr(rte *r, uint id)
+{
+ eattr *a;
+ if (a = ea_find(r->attrs->eattrs, id))
+ {
+ r->pflags |= EA_ID_FLAG(id);
+ return a->u.data;
+ }
+ else
+ {
+ r->pflags &= ~EA_ID_FLAG(id);
+ return 0;
+ }
}
+
static int /* Actually better or at least as good as */
rte_better(rte *new, rte *old)
{
static void
rte_trace(struct proto *p, rte *e, int dir, char *msg)
{
- log(L_TRACE "%s %c %s %N %s", p->name, dir, msg, e->net->n.addr, rt_format_via(e));
+ log(L_TRACE "%s %c %s %N %s", p->name, dir, msg, e->net->n.addr, rta_dest_name(e->attrs->dest));
}
static inline void
}
static rte *
-export_filter_(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, linpool *pool, int silent)
+export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int silent)
{
struct proto *p = c->proto;
struct filter *filter = c->out_filter;
struct proto_stats *stats = &c->stats;
- ea_list *tmpb = NULL;
rte *rt;
int v;
rt = rt0;
*rt_free = NULL;
- if (!tmpa)
- tmpa = &tmpb;
-
- *tmpa = make_tmp_attrs(rt, pool);
-
- v = p->import_control ? p->import_control(p, &rt, tmpa, pool) : 0;
+ v = p->preexport ? p->preexport(p, &rt, pool) : 0;
if (v < 0)
{
if (silent)
goto accept;
}
+ rte_make_tmp_attrs(&rt, pool);
+
v = filter && ((filter == FILTER_REJECT) ||
- (f_run(filter, &rt, tmpa, pool, FF_FORCE_TMPATTR) > F_ACCEPT));
+ (f_run(filter, &rt, pool,
+ (silent ? FF_SILENT : 0)) > F_ACCEPT));
if (v)
{
if (silent)
}
static inline rte *
-export_filter(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, int silent)
+export_filter(struct channel *c, rte *rt0, rte **rt_free, int silent)
{
- return export_filter_(c, rt0, rt_free, tmpa, rte_update_pool, silent);
+ return export_filter_(c, rt0, rt_free, rte_update_pool, silent);
}
static void
-do_rt_notify(struct channel *c, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
+do_rt_notify(struct channel *c, net *net, rte *new, rte *old, int refeed)
{
struct proto *p = c->proto;
struct proto_stats *stats = &c->stats;
else if (old)
rte_trace_out(D_ROUTES, p, old, "removed");
}
- if (!new)
- p->rt_notify(p, c, net, NULL, old, NULL);
- else if (tmpa)
- {
- ea_list *t = tmpa;
- while (t->next)
- t = t->next;
- t->next = new->attrs->eattrs;
- p->rt_notify(p, c, net, new, old, tmpa);
- t->next = NULL;
- }
- else
- p->rt_notify(p, c, net, new, old, new->attrs->eattrs);
+ p->rt_notify(p, c, net, new, old);
}
static void
rte *old = old0;
rte *new_free = NULL;
rte *old_free = NULL;
- ea_list *tmpa = NULL;
if (new)
c->stats.exp_updates_received++;
c->stats.exp_withdraws_received++;
/*
- * This is a tricky part - we don't know whether route 'old' was
- * exported to protocol 'p' or was filtered by the export filter.
- * We try to run the export filter to know this to have a correct
- * value in 'old' argument of rte_update (and proper filter value)
+ * This is a tricky part - we don't know whether route 'old' was exported to
+ * protocol 'p' or was filtered by the export filter. We try to run the export
+ * filter to know this to have a correct value in 'old' argument of rte_update
+ * (and proper filter value).
*
- * FIXME - this is broken because 'configure soft' may change
- * filters but keep routes. Refeed is expected to be called after
- * change of the filters and with old == new, therefore we do not
- * even try to run the filter on an old route, This may lead to
- * 'spurious withdraws' but ensure that there are no 'missing
+ * This is broken because 'configure soft' may change filters but keep routes.
+ * Refeed cycle is expected to be called after change of the filters and with
+ * old == new, therefore we do not even try to run the filter on an old route.
+ * This may lead to 'spurious withdraws' but ensure that there are no 'missing
* withdraws'.
*
- * This is not completely safe as there is a window between
- * reconfiguration and the end of refeed - if a newly filtered
- * route disappears during this period, proper withdraw is not
- * sent (because old would be also filtered) and the route is
- * not refeeded (because it disappeared before that).
+ * This is not completely safe as there is a window between reconfiguration
+ * and the end of refeed - if a newly filtered route disappears during this
+ * period, proper withdraw is not sent (because old would be also filtered)
+ * and the route is not refeeded (because it disappeared before that).
+ * This is handled below as a special case.
*/
if (new)
- new = export_filter(c, new, &new_free, &tmpa, 0);
+ new = export_filter(c, new, &new_free, 0);
if (old && !refeed)
- old = export_filter(c, old, &old_free, NULL, 1);
+ old = export_filter(c, old, &old_free, 1);
if (!new && !old)
{
/*
* As mentioned above, 'old' value may be incorrect in some race conditions.
- * We generally ignore it with the exception of withdraw to pipe protocol.
- * In that case we rather propagate unfiltered withdraws regardless of
- * export filters to ensure that when a protocol is flushed, its routes are
- * removed from all tables. Possible spurious unfiltered withdraws are not
- * problem here as they are ignored if there is no corresponding route at
- * the other end of the pipe. We directly call rt_notify() hook instead of
+ * We generally ignore it with two exceptions:
+ *
+ * First, withdraw to pipe protocol. In that case we rather propagate
+ * unfiltered withdraws regardless of export filters to ensure that when a
+ * protocol is flushed, its routes are removed from all tables. Possible
+ * spurious unfiltered withdraws are not problem here as they are ignored if
+ * there is no corresponding route at the other end of the pipe.
+ *
+ * Second, recent filter change. If old route is older than filter change,
+ * then it was previously evaluated by a different filter and we do not know
+ * whether it was really propagated. In that case we rather send spurious
+ * withdraw than do nothing and possibly cause phantom routes.
+ *
+ * In both cases wqe directly call rt_notify() hook instead of
* do_rt_notify() to avoid logging and stat counters.
*/
+ int pipe_withdraw = 0, filter_change = 0;
#ifdef CONFIG_PIPE
- if ((p->proto == &proto_pipe) && !new0 && (p != old0->sender->proto))
- p->rt_notify(p, c, net, NULL, old0, NULL);
+ pipe_withdraw = (p->proto == &proto_pipe) && !new0;
#endif
+ filter_change = old0 && (old0->lastmod <= c->last_tx_filter_change);
+
+ if ((pipe_withdraw || filter_change) && (p != old0->sender->proto))
+ {
+ c->stats.exp_withdraws_accepted++;
+ p->rt_notify(p, c, net, NULL, old0);
+ }
return;
}
- do_rt_notify(c, net, new, old, tmpa, refeed);
+ do_rt_notify(c, net, new, old, refeed);
/* Discard temporary rte's */
if (new_free)
static void
rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_changed, rte *before_old, int feed)
{
- // struct proto *p = c->proto;
+ struct proto *p = c->proto;
rte *r;
rte *new_best = NULL;
rte *old_best = NULL;
rte *new_free = NULL;
rte *old_free = NULL;
- ea_list *tmpa = NULL;
/* 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. */
/* First, find the new_best route - first accepted by filters */
for (r=net->routes; rte_is_valid(r); r=r->next)
{
- if (new_best = export_filter(c, r, &new_free, &tmpa, 0))
+ if (new_best = export_filter(c, r, &new_free, 0))
break;
/* Note if we walked around the position of old_changed route */
old_meet = 1;
}
- /*
+ /*
* Second, handle the feed case. That means we do not care for
- * old_best. It is NULL for feed, and the new_best for refeed.
+ * old_best. It is NULL for feed, and the new_best for refeed.
* For refeed, there is a hack similar to one in rt_notify_basic()
* to ensure withdraws in case of changed filters
*/
*
* - We found new_best the same as new_changed, therefore it cannot
* be old_best and we have to continue search for old_best.
+ *
+ * There is also a hack to ensure consistency in case of changed filters.
+ * It does not find the proper old_best, just selects a non-NULL route.
*/
+ /* Hack for changed filters */
+ if (old_changed &&
+ (p != old_changed->sender->proto) &&
+ (old_changed->lastmod <= c->last_tx_filter_change))
+ {
+ old_best = old_changed;
+ goto found;
+ }
+
/* First case */
if (old_meet)
- if (old_best = export_filter(c, old_changed, &old_free, NULL, 1))
+ if (old_best = export_filter(c, old_changed, &old_free, 1))
goto found;
/* Second case */
/* Fourth case */
for (r=r->next; rte_is_valid(r); r=r->next)
{
- if (old_best = export_filter(c, r, &old_free, NULL, 1))
+ if (old_best = export_filter(c, r, &old_free, 1))
goto found;
if (r == before_old)
- if (old_best = export_filter(c, old_changed, &old_free, NULL, 1))
+ if (old_best = export_filter(c, old_changed, &old_free, 1))
goto found;
}
/* Implicitly, old_best is NULL and new_best is non-NULL */
found:
- do_rt_notify(c, net, new_best, old_best, tmpa, (feed == 2));
+ do_rt_notify(c, net, new_best, old_best, (feed == 2));
/* Discard temporary rte's */
if (new_free)
}
rte *
-rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, linpool *pool, int silent)
+rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent)
{
// struct proto *p = c->proto;
struct nexthop *nhs = NULL;
if (!rte_is_valid(best0))
return NULL;
- best = export_filter_(c, best0, rt_free, tmpa, pool, silent);
+ best = export_filter_(c, best0, rt_free, pool, silent);
if (!best || !rte_is_reachable(best))
return best;
if (!rte_mergable(best0, rt0))
continue;
- rt = export_filter_(c, rt0, &tmp, NULL, pool, 1);
+ rt = export_filter_(c, rt0, &tmp, pool, 1);
if (!rt)
continue;
rte *old_best_free = NULL;
rte *new_changed_free = NULL;
rte *old_changed_free = NULL;
- ea_list *tmpa = NULL;
/* We assume that all rte arguments are either NULL or rte_is_valid() */
if ((new_best == old_best) && !refeed)
{
new_changed = rte_mergable(new_best, new_changed) ?
- export_filter(c, new_changed, &new_changed_free, NULL, 1) : NULL;
+ export_filter(c, new_changed, &new_changed_free, 1) : NULL;
old_changed = rte_mergable(old_best, old_changed) ?
- export_filter(c, old_changed, &old_changed_free, NULL, 1) : NULL;
+ export_filter(c, old_changed, &old_changed_free, 1) : NULL;
if (!new_changed && !old_changed)
return;
/* Prepare new merged route */
if (new_best)
- new_best = rt_export_merged(c, net, &new_best_free, &tmpa, rte_update_pool, 0);
+ new_best = rt_export_merged(c, net, &new_best_free, rte_update_pool, 0);
/* Prepare old merged route (without proper merged next hops) */
/* There are some issues with running filter on old route - see rt_notify_basic() */
if (old_best && !refeed)
- old_best = export_filter(c, old_best, &old_best_free, NULL, 1);
+ old_best = export_filter(c, old_best, &old_best_free, 1);
if (new_best || old_best)
- do_rt_notify(c, net, new_best, old_best, tmpa, refeed);
+ do_rt_notify(c, net, new_best, old_best, refeed);
/* Discard temporary rte's */
if (new_best_free)
* @new_best: the new best route for the same network
* @old_best: the previous best route for the same network
* @before_old: The previous route before @old for the same network.
- * If @before_old is NULL @old was the first.
+ * If @before_old is NULL @old was the first.
*
* This function gets a routing table update and announces it
* to all protocols that acccepts given type of route announcement
* 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()
+ * For each appropriate protocol, we first call its preexport()
* 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
if (!old && !new)
return;
- if ((type == RA_OPTIMAL) && tab->hostcache)
- rt_notify_hostcache(tab, net);
+ if (type == RA_OPTIMAL)
+ {
+ if (new)
+ new->sender->stats.pref_routes++;
+ if (old)
+ old->sender->stats.pref_routes--;
+
+ if (tab->hostcache)
+ rt_notify_hostcache(tab, net);
+ }
struct channel *c; node *n;
WALK_LIST2(c, n, tab->channels, table_node)
int c;
net *n = e->net;
- // (n->n.pxlen > BITS_PER_IP_ADDRESS) || !ip_is_prefix(n->n.prefix,n->n.pxlen))
if (!net_validate(n->n.addr))
{
log(L_WARN "Ignoring bogus prefix %N received via %s",
return 0;
}
- c = net_classify(n->n.addr);
+ /* FIXME: better handling different nettypes */
+ c = !net_is_flow(n->n.addr) ?
+ net_classify(n->n.addr): (IADDR_HOST | SCOPE_UNIVERSE);
if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
{
log(L_WARN "Ignoring bogus route %N received via %s",
return 0;
}
+ if (net_type_match(n->n.addr, NB_DEST) == !e->attrs->dest)
+ {
+ log(L_WARN "Ignoring route %N with invalid dest %d received via %s",
+ n->n.addr, e->attrs->dest, e->sender->proto->name);
+ return 0;
+ }
+
if ((e->attrs->dest == RTD_UNICAST) && !nexthop_is_sorted(&(e->attrs->nh)))
- {
- log(L_WARN "Ignoring unsorted multipath route %N received via %s",
- n->n.addr, e->sender->proto->name);
- return 0;
- }
+ {
+ log(L_WARN "Ignoring unsorted multipath route %N received via %s",
+ n->n.addr, e->sender->proto->name);
+ return 0;
+ }
return 1;
}
static int
rte_same(rte *x, rte *y)
{
+ /* rte.flags are not checked, as they are mostly internal to rtable */
return
x->attrs == y->attrs &&
- x->flags == y->flags &&
x->pflags == y->pflags &&
x->pref == y->pref &&
- (!x->attrs->src->proto->rte_same || x->attrs->src->proto->rte_same(x, y));
+ (!x->attrs->src->proto->rte_same || x->attrs->src->proto->rte_same(x, y)) &&
+ rte_is_filtered(x) == rte_is_filtered(y);
}
static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); }
if (new && rte_same(old, new))
{
- /* No changes, ignore the new route */
+ /* No changes, ignore the new route and refresh the old one */
+
+ old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY);
if (!rte_is_filtered(new))
{
return;
}
*k = old->next;
+ table->rt_count--;
break;
}
k = &old->next;
int old_ok = rte_is_ok(old);
struct channel_limit *l = &c->rx_limit;
- if (l->action && !old && new)
+ if (l->action && !old && new && !c->in_table)
{
u32 all_routes = stats->imp_routes + stats->filt_routes;
new->next = *k;
*k = new;
+ table->rt_count++;
}
}
else
new->next = net->routes;
net->routes = new;
+ table->rt_count++;
}
else if (old == old_best)
{
{
new->next = net->routes;
net->routes = new;
+ table->rt_count++;
}
/* Find a new optimal route (if there is any) */
ASSERT(net->routes != NULL);
new->next = net->routes->next;
net->routes->next = new;
+ table->rt_count++;
}
/* The fourth (empty) case - suboptimal route was removed, nothing to do */
}
if (new)
- new->lastmod = now;
+ new->lastmod = current_time();
/* Log the route change */
if (p->debug & D_ROUTES)
if (!net->routes &&
(table->gc_counter++ >= table->config->gc_max_ops) &&
- (table->gc_time + table->config->gc_min_time <= now))
+ (table->gc_time + table->config->gc_min_time <= current_time()))
rt_schedule_prune(table);
if (old_ok && p->rte_remove)
struct proto *p = c->proto;
struct proto_stats *stats = &c->stats;
struct filter *filter = c->in_filter;
- ea_list *tmpa = NULL;
rte *dummy = NULL;
net *nn;
rte_update_lock();
if (new)
{
- nn = net_get(c->table, n);
+ /* Create a temporary table node */
+ nn = alloca(sizeof(net) + n->length);
+ memset(nn, 0, sizeof(net) + n->length);
+ net_copy(nn->n.addr, n);
new->net = nn;
new->sender = c;
}
else
{
- tmpa = make_tmp_attrs(new, rte_update_pool);
+ rte_make_tmp_attrs(&new, rte_update_pool);
if (filter && (filter != FILTER_REJECT))
{
- ea_list *old_tmpa = tmpa;
- int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0);
+ ea_list *oldea = new->attrs->eattrs;
+ int fr = f_run(filter, &new, rte_update_pool, 0);
if (fr > F_ACCEPT)
{
stats->imp_updates_filtered++;
new->flags |= REF_FILTERED;
}
- if (tmpa != old_tmpa && src->proto->store_tmp_attrs)
- src->proto->store_tmp_attrs(new, tmpa);
+ if (new->attrs->eattrs != oldea && src->proto->store_tmp_attrs)
+ src->proto->store_tmp_attrs(new);
}
}
if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
new->attrs = rta_lookup(new->attrs);
new->flags |= REF_COW;
+
+ /* Use the actual struct network, not the dummy one */
+ nn = net_get(c->table, n);
+ new->net = nn;
}
else
{
}
recalc:
+ /* And recalculate the best route */
rte_hide_dummy_routes(nn, &dummy);
rte_recalculate(c, nn, new, src);
rte_unhide_dummy_routes(nn, &dummy);
+
rte_update_unlock();
return;
drop:
rte_free(new);
new = NULL;
- goto recalc;
+ if (nn = net_find(c->table, n))
+ goto recalc;
+
+ rte_update_unlock();
}
/* Independent call to rte_announce(), used from next hop
recalculation, outside of rte_update(). new must be non-NULL */
-static inline void
+static inline void
rte_announce_i(rtable *tab, unsigned type, net *net, rte *new, rte *old,
rte *new_best, rte *old_best)
{
rte_update_unlock();
}
+/* Modify existing route by protocol hook, used for long-lived graceful restart */
+static inline void
+rte_modify(rte *old)
+{
+ rte_update_lock();
+
+ rte *new = old->sender->proto->rte_modify(old, rte_update_pool);
+ if (new != old)
+ {
+ if (new)
+ {
+ if (!rta_is_cached(new->attrs))
+ new->attrs = rta_lookup(new->attrs);
+ new->flags = (old->flags & ~REF_MODIFY) | REF_COW;
+ }
+
+ rte_recalculate(old->sender, old->net, new, old->attrs->src);
+ }
+
+ rte_update_unlock();
+}
+
/* Check rtable for best route to given net whether it would be exported do p */
int
rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter)
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;
+ int v = p->preexport ? p->preexport(p, &rt, rte_update_pool) : 0;
if (v == RIC_PROCESS)
- v = (f_run(filter, &rt, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
+ {
+ rte_make_tmp_attrs(&rt, rte_update_pool);
+ v = (f_run(filter, &rt, rte_update_pool, FF_SILENT) <= F_ACCEPT);
+ }
- /* Discard temporary rte */
+ /* Discard temporary rte */
if (rt != n->routes)
rte_free(rt);
rt_schedule_prune(t);
}
+void
+rt_modify_stale(rtable *t, struct channel *c)
+{
+ int prune = 0;
+
+ FIB_WALK(&t->fib, net, n)
+ {
+ rte *e;
+ for (e = n->routes; e; e = e->next)
+ if ((e->sender == c) && (e->flags & REF_STALE) && !(e->flags & REF_FILTERED))
+ {
+ e->flags |= REF_MODIFY;
+ prune = 1;
+ }
+ }
+ FIB_WALK_END;
+
+ if (prune)
+ rt_schedule_prune(t);
+}
/**
* rte_dump - dump a route
{
net *n = e->net;
debug("%-1N ", n->n.addr);
- debug("KF=%02x PF=%02x pref=%d lm=%d ", n->n.flags, e->pflags, e->pref, now-e->lastmod);
+ debug("KF=%02x PF=%02x pref=%d ", n->n.flags, e->pflags, e->pref);
rta_dump(e->attrs);
if (e->attrs->src->proto->proto->dump_attrs)
e->attrs->src->proto->proto->dump_attrs(e);
}
void
-rt_setup(pool *p, rtable *t, char *name, struct rtable_config *cf)
+rt_setup(pool *p, rtable *t, struct rtable_config *cf)
{
bzero(t, sizeof(*t));
- t->name = name;
+ t->name = cf->name;
t->config = cf;
- t->addr_type = cf ? cf->addr_type : NET_IP4;
+ t->addr_type = cf->addr_type;
fib_init(&t->fib, p, t->addr_type, sizeof(net), OFFSETOF(net, n), 0, NULL);
init_list(&t->channels);
- if (cf)
- {
- t->rt_event = ev_new(p);
- t->rt_event->hook = rt_event;
- t->rt_event->data = t;
- t->gc_time = now;
- }
+ t->rt_event = ev_new_init(p, rt_event, t);
+ t->gc_time = current_time();
}
/**
{
rta_init();
rt_table_pool = rp_new(&root_pool, "Routing tables");
- rte_update_pool = lp_new(rt_table_pool, 4080);
+ rte_update_pool = lp_new_default(rt_table_pool);
rte_slab = sl_new(rt_table_pool, sizeof(rte));
init_list(&routing_tables);
}
rescan:
for (e=n->routes; e; e=e->next)
+ {
if (e->sender->flush_active || (e->flags & REF_DISCARD))
{
if (limit <= 0)
goto rescan;
}
+ if (e->flags & REF_MODIFY)
+ {
+ if (limit <= 0)
+ {
+ FIB_ITERATE_PUT(fit);
+ ev_schedule(tab->rt_event);
+ return;
+ }
+
+ rte_modify(e);
+ limit--;
+
+ goto rescan;
+ }
+ }
+
if (!n->routes) /* Orphaned FIB entry */
{
FIB_ITERATE_PUT(fit);
#endif
tab->gc_counter = 0;
- tab->gc_time = now;
+ tab->gc_time = current_time();
/* state change 2->0, 3->1 */
tab->prune_state &= 1;
(!he->nexthop_linkable) || !nexthop_same(&(a->nh), &(he->src->nh));
}
-static inline void
-rta_apply_hostentry(rta *a, struct hostentry *he)
+void
+rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls)
{
a->hostentry = he;
a->dest = he->dest;
a->igp_metric = he->igp_metric;
- if ((a->nh.labels_orig == 0) && (!a->nh.next) && he->nexthop_linkable)
+ if (a->dest != RTD_UNICAST)
{
- a->nh = he->src->nh;
+ /* No nexthop */
+no_nexthop:
+ a->nh = (struct nexthop) {};
+ if (mls)
+ { /* Store the label stack for later changes */
+ a->nh.labels_orig = a->nh.labels = mls->len;
+ memcpy(a->nh.label, mls->stack, mls->len * sizeof(u32));
+ }
return;
}
- struct nexthop *nhp = alloca(NEXTHOP_MAX_SIZE);
-
- for (struct nexthop *nhe = &(a->nh); nhe; nhe = nhe->next)
+ if (((!mls) || (!mls->len)) && he->nexthop_linkable)
+ { /* Just link the nexthop chain, no label append happens. */
+ memcpy(&(a->nh), &(he->src->nh), nexthop_size(&(he->src->nh)));
+ return;
+ }
+
+ struct nexthop *nhp = NULL, *nhr = NULL;
+ int skip_nexthop = 0;
+
+ for (struct nexthop *nh = &(he->src->nh); nh; nh = nh->next)
{
- int labels_orig = nhe->labels_orig; /* Number of labels (at the bottom of stack) */
- u32 label_stack[MPLS_MAX_LABEL_STACK];
- memcpy(label_stack, nhe->label, labels_orig * sizeof(u32));
+ if (skip_nexthop)
+ skip_nexthop--;
+ else
+ {
+ nhr = nhp;
+ nhp = (nhp ? (nhp->next = lp_allocz(rte_update_pool, NEXTHOP_MAX_SIZE)) : &(a->nh));
+ }
- for (struct nexthop *nh = &(he->src->nh); nh; nh = nh->next)
+ nhp->iface = nh->iface;
+ nhp->weight = nh->weight;
+ if (mls)
{
- nhp->iface = nh->iface;
- nhp->weight = nh->weight; /* FIXME: Ignoring the recursive nexthop's weight */
- nhp->labels = nh->labels + labels_orig;
- nhp->labels_orig = labels_orig;
+ nhp->labels = nh->labels + mls->len;
+ nhp->labels_orig = mls->len;
if (nhp->labels <= MPLS_MAX_LABEL_STACK)
{
memcpy(nhp->label, nh->label, nh->labels * sizeof(u32)); /* First the hostentry labels */
- memcpy(&(nhp->label[nh->labels]), label_stack, labels_orig * sizeof(u32)); /* Then the bottom labels */
+ memcpy(&(nhp->label[nh->labels]), mls->stack, mls->len * sizeof(u32)); /* Then the bottom labels */
}
else
{
log(L_WARN "Sum of label stack sizes %d + %d = %d exceedes allowed maximum (%d)",
- nh->labels, labels_orig, nhp->labels, MPLS_MAX_LABEL_STACK);
+ nh->labels, mls->len, nhp->labels, MPLS_MAX_LABEL_STACK);
+ skip_nexthop++;
continue;
}
- if (ipa_nonzero(nh->gw))
- nhp->gw = nh->gw; /* Router nexthop */
- else if (ipa_nonzero(he->link))
- nhp->gw = he->link; /* Device nexthop with link-local address known */
- else
- nhp->gw = he->addr; /* Device nexthop with link-local address unknown */
-
- nhp = (nhp->next = lp_alloc(rte_update_pool, NEXTHOP_MAX_SIZE));
}
+ if (ipa_nonzero(nh->gw))
+ {
+ nhp->gw = nh->gw; /* Router nexthop */
+ nhp->flags |= (nh->flags & RNF_ONLINK);
+ }
+ else if (ipa_nonzero(he->link))
+ nhp->gw = he->link; /* Device nexthop with link-local address known */
+ else
+ nhp->gw = he->addr; /* Device nexthop with link-local address unknown */
}
- memcpy(&(a->nh), nhp, nexthop_size(nhp));
+ if (skip_nexthop)
+ if (nhr)
+ nhr->next = NULL;
+ else
+ {
+ a->dest = RTD_UNREACHABLE;
+ log(L_WARN "No valid nexthop remaining, setting route unreachable");
+ goto no_nexthop;
+ }
}
static inline rte *
{
rta *a = alloca(RTA_MAX_SIZE);
memcpy(a, old->attrs, rta_size(old->attrs));
- rta_apply_hostentry(a, old->attrs->hostentry);
+
+ mpls_label_stack mls = { .len = a->nh.labels_orig };
+ memcpy(mls.stack, &a->nh.label[a->nh.labels - mls.len], mls.len * sizeof(u32));
+
+ rta_apply_hostentry(a, old->attrs->hostentry, &mls);
a->aflags = 0;
rte *e = sl_alloc(rte_slab);
}
}
+static struct rtable_config *
+rt_find_table_config(struct config *cf, char *name)
+{
+ struct symbol *sym = cf_find_symbol(cf, name);
+ return (sym && (sym->class == SYM_TABLE)) ? sym->def : NULL;
+}
+
/**
* rt_commit - commit new routing table configuration
* @new: new configuration
rtable *ot = o->table;
if (!ot->deleted)
{
- struct symbol *sym = cf_find_symbol(new, o->name);
- if (sym && sym->class == SYM_TABLE && !new->shutdown)
+ r = rt_find_table_config(new, o->name);
+ if (r && (r->addr_type == o->addr_type) && !new->shutdown)
{
DBG("\t%s: same\n", o->name);
- r = sym->def;
r->table = ot;
ot->name = r->name;
ot->config = r;
{
rtable *t = mb_alloc(rt_table_pool, sizeof(struct rtable));
DBG("\t%s: created\n", r->name);
- rt_setup(rt_table_pool, t, r->name, r);
+ rt_setup(rt_table_pool, t, r);
add_tail(&routing_tables, &t->n);
r->table = t;
}
}
}
-static inline unsigned
-ptr_hash(void *ptr)
+
+int
+rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
{
- uintptr_t p = (uintptr_t) ptr;
- return p ^ (p << 8) ^ (p >> 16);
+ struct rtable *tab = c->in_table;
+ rte *old, **pos;
+ net *net;
+
+ if (new)
+ {
+ net = net_get(tab, n);
+
+ if (!new->pref)
+ new->pref = c->preference;
+
+ if (!rta_is_cached(new->attrs))
+ new->attrs = rta_lookup(new->attrs);
+ }
+ else
+ {
+ net = net_find(tab, n);
+
+ if (!net)
+ goto drop_withdraw;
+ }
+
+ /* Find the old rte */
+ for (pos = &net->routes; old = *pos; pos = &old->next)
+ if (old->attrs->src == src)
+ {
+ if (new && rte_same(old, new))
+ {
+ /* Refresh the old rte, continue with update to main rtable */
+ if (old->flags & (REF_STALE | REF_DISCARD | REF_MODIFY))
+ {
+ old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY);
+ return 1;
+ }
+
+ goto drop_update;
+ }
+
+ /* Remove the old rte */
+ *pos = old->next;
+ rte_free_quick(old);
+ tab->rt_count--;
+
+ break;
+ }
+
+ if (!new)
+ {
+ if (!old)
+ goto drop_withdraw;
+
+ return 1;
+ }
+
+ struct channel_limit *l = &c->rx_limit;
+ if (l->action && !old)
+ {
+ if (tab->rt_count >= l->limit)
+ channel_notify_limit(c, l, PLD_RX, tab->rt_count);
+
+ if (l->state == PLS_BLOCKED)
+ {
+ rte_trace_in(D_FILTERS, c->proto, new, "ignored [limit]");
+ goto drop_update;
+ }
+ }
+
+ /* Insert the new rte */
+ rte *e = rte_do_cow(new);
+ e->flags |= REF_COW;
+ e->net = net;
+ e->sender = c;
+ e->lastmod = current_time();
+ e->next = *pos;
+ *pos = e;
+ tab->rt_count++;
+ return 1;
+
+drop_update:
+ c->stats.imp_updates_received++;
+ c->stats.imp_updates_ignored++;
+ rte_free(new);
+ return 0;
+
+drop_withdraw:
+ c->stats.imp_withdraws_received++;
+ c->stats.imp_withdraws_ignored++;
+ return 0;
+}
+
+int
+rt_reload_channel(struct channel *c)
+{
+ struct rtable *tab = c->in_table;
+ struct fib_iterator *fit = &c->reload_fit;
+ int max_feed = 64;
+
+ ASSERT(c->channel_state == CS_UP);
+
+ if (!c->reload_active)
+ {
+ FIB_ITERATE_INIT(fit, &tab->fib);
+ c->reload_active = 1;
+ }
+
+ FIB_ITERATE_START(&tab->fib, fit, net, n)
+ {
+ if (max_feed <= 0)
+ {
+ FIB_ITERATE_PUT(fit);
+ return 0;
+ }
+
+ for (rte *e = n->routes; e; e = e->next)
+ {
+ rte_update2(c, n->n.addr, rte_do_cow(e), e->attrs->src);
+ max_feed--;
+ }
+ }
+ FIB_ITERATE_END;
+
+ c->reload_active = 0;
+ return 1;
+}
+
+void
+rt_reload_channel_abort(struct channel *c)
+{
+ if (c->reload_active)
+ {
+ /* Unlink the iterator */
+ fit_get(&c->in_table->fib, &c->reload_fit);
+ c->reload_active = 0;
+ }
+}
+
+void
+rt_prune_sync(rtable *t, int all)
+{
+ FIB_WALK(&t->fib, net, n)
+ {
+ rte *e, **ee = &n->routes;
+ while (e = *ee)
+ {
+ if (all || (e->flags & (REF_STALE | REF_DISCARD)))
+ {
+ *ee = e->next;
+ rte_free_quick(e);
+ t->rt_count--;
+ }
+ else
+ ee = &e->next;
+ }
+ }
+ FIB_WALK_END;
}
+
static inline u32
hc_hash(ip_addr a, rtable *dep)
{
hc_alloc_table(hc, HC_DEF_ORDER);
hc->slab = sl_new(rt_table_pool, sizeof(struct hostentry));
- hc->lp = lp_new(rt_table_pool, 1008);
+ hc->lp = lp_new(rt_table_pool, LP_GOOD_SIZE(1024));
hc->trie = f_new_trie(hc->lp, sizeof(struct f_trie_node));
tab->hostcache = hc;
return 0;
}
-static u32
+static u32
rt_get_igp_metric(rte *rt)
{
eattr *ea = ea_find(rt->attrs->eattrs, EA_GEN_IGP_METRIC);
rt_update_hostentry(rtable *tab, struct hostentry *he)
{
rta *old_src = he->src;
+ int direct = 0;
int pxlen = 0;
/* Reset the hostentry */
he->src = NULL;
- he->nexthop_linkable = 0;
he->dest = RTD_UNREACHABLE;
+ he->nexthop_linkable = 0;
he->igp_metric = 0;
net_addr he_addr;
goto done;
}
- he->nexthop_linkable = 1;
- for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
- if (ipa_zero(nh->gw))
- {
- he->nexthop_linkable = 0;
- break;
- }
-
+ if (a->dest == RTD_UNICAST)
+ {
+ for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
+ if (ipa_zero(nh->gw))
+ {
+ if (if_local_addr(he->addr, nh->iface))
+ {
+ /* The host address is a local address, this is not valid */
+ log(L_WARN "Next hop address %I is a local address of iface %s",
+ he->addr, nh->iface->name);
+ goto done;
+ }
+
+ direct++;
+ }
+ }
+
he->src = rta_clone(a);
+ he->dest = a->dest;
+ he->nexthop_linkable = !direct;
he->igp_metric = rt_get_igp_metric(e);
}
- done:
+done:
/* Add a prefix range to the trie */
trie_add_prefix(tab->hostcache->trie, &he_addr, pxlen, he_addr.pxlen);
tab->hcu_scheduled = 0;
}
-static struct hostentry *
+struct hostentry *
rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
{
struct hostentry *he;
if (ipa_equal(he->addr, a) && (he->tab == dep))
return he;
- he = hc_new_hostentry(hc, a, ll, dep, k);
+ he = hc_new_hostentry(hc, a, ipa_zero(ll) ? a : ll, dep, k);
rt_update_hostentry(tab, he);
return he;
}
-void
-rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll)
-{
- rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ipa_zero(ll) ? gw : ll, dep));
-}
-
-
-/*
- * CLI commands
- */
-
-static byte *
-rt_format_via(rte *e)
-{
- rta *a = e->attrs;
-
- /* Max text length w/o IP addr and interface name is 16 */
- static byte via[IPA_MAX_TEXT_LENGTH+sizeof(a->nh.iface->name)+16];
-
- switch (a->dest)
- {
- case RTD_UNICAST: bsprintf(via, "unicast"); break;
- case RTD_BLACKHOLE: bsprintf(via, "blackhole"); break;
- case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
- case RTD_PROHIBIT: bsprintf(via, "prohibited"); break;
- default: bsprintf(via, "???");
- }
- return via;
-}
-
-static void
-rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tmpa)
-{
- byte from[IPA_MAX_TEXT_LENGTH+8];
- byte tm[TM_DATETIME_BUFFER_SIZE], info[256];
- 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 nexthop *nh;
-
- tm_format_datetime(tm, &config->tf_route, e->lastmod);
- if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->nh.gw))
- bsprintf(from, " from %I", a->from);
- else
- from[0] = 0;
-
- 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;
- t = ea_append(t, a->eattrs);
- tmpa = alloca(ea_scan(t));
- ea_merge(t, tmpa);
- ea_sort(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, rt_format_via(e), a->src->proto->name,
- tm, from, primary ? (sync_error ? " !" : " *") : "", info);
- for (nh = &(a->nh); nh; nh = nh->next)
- {
- char ls[MPLS_MAX_LABEL_STACK*8 + 5]; char *lsp = ls;
- if (nh->labels)
- {
- lsp += bsprintf(lsp, " mpls %d", nh->label[0]);
- for (int i=1;i<nh->labels; i++)
- lsp += bsprintf(lsp, "/%d", nh->label[i]);
- *lsp++ = '\0';
- }
- if (a->nh.next)
- cli_printf(c, -1007, "\tvia %I%s on %s weight %d", nh->gw, (nh->labels ? ls : ""), nh->iface->name, nh->weight + 1);
- else
- cli_printf(c, -1007, "\tvia %I%s on %s", nh->gw, (nh->labels ? ls : ""), nh->iface->name);
- }
- if (d->verbose)
- rta_show(c, a, tmpa);
-
-}
-
-static void
-rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
-{
- rte *e, *ee;
- byte ia[NET_MAX_TEXT_LENGTH+1];
- struct ea_list *tmpa;
- struct channel *ec = d->export_channel;
- int first = 1;
- int pass = 0;
-
- bsnprintf(ia, sizeof(ia), "%N", n->n.addr);
-
- for (e = n->routes; e; e = e->next)
- {
- if (rte_is_filtered(e) != d->filtered)
- continue;
-
- d->rt_counter++;
- d->net_counter += first;
- first = 0;
-
- if (pass)
- continue;
-
- ee = e;
- rte_update_lock(); /* We use the update buffer for filtering */
- tmpa = make_tmp_attrs(e, rte_update_pool);
-
- /* Special case for merged export */
- if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED))
- {
- rte *rt_free;
- e = rt_export_merged(ec, n, &rt_free, &tmpa, rte_update_pool, 1);
- pass = 1;
-
- if (!e)
- { e = ee; goto skip; }
- }
- else if (d->export_mode)
- {
- struct proto *ep = d->export_protocol;
- int ic = ep->import_control ? ep->import_control(ep, &e, &tmpa, rte_update_pool) : 0;
-
- if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED)
- pass = 1;
-
- if (ic < 0)
- goto skip;
-
- if (d->export_mode > RSEM_PREEXPORT)
- {
- /*
- * FIXME - This shows what should be exported according to current
- * filters, but not what was really exported. 'configure soft'
- * command may change the export filter and do not update routes.
- */
- int do_export = (ic > 0) ||
- (f_run(ec->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
-
- if (do_export != (d->export_mode == RSEM_EXPORT))
- goto skip;
-
- if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_ACCEPTED))
- pass = 1;
- }
- }
-
- if (d->show_protocol && (d->show_protocol != e->attrs->src->proto))
- goto skip;
-
- if (f_run(d->filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)
- goto skip;
-
- d->show_counter++;
- if (d->stats < 2)
- rt_show_rte(c, ia, e, d, tmpa);
- ia[0] = 0;
-
- skip:
- if (e != ee)
- {
- rte_free(e);
- e = ee;
- }
- rte_update_unlock();
-
- if (d->primary_only)
- break;
- }
-}
-
-static struct channel *
-rt_show_export_channel(struct rt_show_data *d)
-{
- if (! d->export_protocol->rt_notify)
- return NULL;
-
- return proto_find_channel_by_table(d->export_protocol, d->table);
-}
-
-static void
-rt_show_cont(struct cli *c)
-{
- struct rt_show_data *d = c->rover;
-#ifdef DEBUGGING
- unsigned max = 4;
-#else
- unsigned max = 64;
-#endif
- struct fib *fib = &d->table->fib;
- struct fib_iterator *it = &d->fit;
-
- if (d->export_mode)
- {
- /* Ensure we have current export channel */
- d->export_channel = rt_show_export_channel(d);
- if (!d->export_channel || (d->export_channel->export_state == ES_DOWN))
- {
- cli_printf(c, 8005, "Channel is down");
- goto done;
- }
- }
-
- FIB_ITERATE_START(fib, it, net, n)
- {
- if (!max--)
- {
- FIB_ITERATE_PUT(it);
- return;
- }
- rt_show_net(c, n, d);
- }
- FIB_ITERATE_END;
- if (d->stats)
- cli_printf(c, 14, "%d of %d routes for %d networks", d->show_counter, d->rt_counter, d->net_counter);
- else
- cli_printf(c, 0, "");
-done:
- c->cont = c->cleanup = NULL;
-}
-
-static void
-rt_show_cleanup(struct cli *c)
-{
- struct rt_show_data *d = c->rover;
-
- /* Unlink the iterator */
- fit_get(&d->table->fib, &d->fit);
-}
-
-static inline rtable *
-rt_show_get_table(struct proto *p)
-{
- /* FIXME: Use a better way to handle multi-channel protocols */
-
- if (p->main_channel)
- return p->main_channel->table;
-
- if (!EMPTY_LIST(p->channels))
- return ((struct channel *) HEAD(p->channels))->table;
-
- return NULL;
-}
-
-void
-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 = rt_show_get_table(d->export_protocol);
- if (!d->table && d->show_protocol) d->table = rt_show_get_table(d->show_protocol);
- if (!d->table) d->table = config->def_tables[NET_IP4]->table; /* FIXME: iterate through all tables ? */
-
- /* Filtered routes are neither exported nor have sensible ordering */
- if (d->filtered && (d->export_mode || d->primary_only))
- cli_msg(0, "");
-
- if (!d->addr)
- {
- FIB_ITERATE_INIT(&d->fit, &d->table->fib);
- this_cli->cont = rt_show_cont;
- this_cli->cleanup = rt_show_cleanup;
- this_cli->rover = d;
- }
- else
- {
- if (d->export_mode)
- {
- /* Find channel associated with the export protocol */
- d->export_channel = rt_show_export_channel(d);
- if (!d->export_channel || (d->export_channel->export_state == ES_DOWN))
- {
- cli_msg(8005, "Channel is down");
- return;
- }
- }
-
- if (d->table->addr_type != d->addr->type)
- {
- cli_msg(8001, "Incompatible type of prefix/ip with table");
- return;
- }
-
- if (d->show_for)
- n = net_route(d->table, d->addr);
- else
- n = net_find(d->table, d->addr);
-
- if (n)
- rt_show_net(this_cli, n, d);
-
- if (d->rt_counter)
- cli_msg(0, "");
- else
- cli_msg(8001, "Network not in table");
- }
-}
/*
* Documentation for functions declared inline in route.h