diff options
author | Maria Matejka <mq@ucw.cz> | 2021-06-21 17:07:31 +0200 |
---|---|---|
committer | Maria Matejka <mq@ucw.cz> | 2021-11-22 18:33:53 +0100 |
commit | f81702b7e44ba7f2b264fe14f0f4e1e30913e475 (patch) | |
tree | a40097fcf4687f1b9d33a3ba794e38898bfbab4f /nest/rt-table.c | |
parent | 3a8197a9dce6fc5d38b089a291ac79d8d394fea1 (diff) |
Table import and export are now explicit hooks.
Channels have now included rt_import_req and rt_export_req to hook into
the table instead of just one list node. This will (in future) allow for:
* channel import and export bound to different tables
* more efficient pipe code (dropping most of the channel code)
* conversion of 'show route' to a special kind of export
* temporary static routes from CLI
The import / export states are also updated to the new algorithms.
Diffstat (limited to 'nest/rt-table.c')
-rw-r--r-- | nest/rt-table.c | 843 |
1 files changed, 504 insertions, 339 deletions
diff --git a/nest/rt-table.c b/nest/rt-table.c index 66e63acf..e7ff2816 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -50,13 +50,7 @@ pool *rt_table_pool; static linpool *rte_update_pool; list routing_tables; - -struct rt_pending_export { - struct rte_storage *new; /* New route */ - struct rte_storage *new_best; /* New best route */ - struct rte_storage *old; /* Old route */ - struct rte_storage *old_best; /* Old best route */ -}; +list deleted_routing_tables; static void rt_free_hostcache(rtable *tab); static void rt_notify_hostcache(rtable *tab, net *net); @@ -64,6 +58,40 @@ static void rt_update_hostcache(rtable *tab); static void rt_next_hop_update(rtable *tab); static inline void rt_prune_table(rtable *tab); static inline void rt_schedule_notify(rtable *tab); +static void rt_feed_channel(void *); + +const char *rt_import_state_name_array[TIS_MAX] = { + [TIS_DOWN] = "DOWN", + [TIS_UP] = "UP", + [TIS_STOP] = "STOP", + [TIS_FLUSHING] = "FLUSHING", + [TIS_WAITING] = "WAITING", + [TIS_CLEARED] = "CLEARED", +}; + +const char *rt_export_state_name_array[TES_MAX] = { + [TES_DOWN] = "DOWN", + [TES_HUNGRY] = "HUNGRY", + [TES_FEEDING] = "FEEDING", + [TES_READY] = "READY", + [TES_STOP] = "STOP" +}; + +const char *rt_import_state_name(u8 state) +{ + if (state >= TIS_MAX) + return "!! INVALID !!"; + else + return rt_import_state_name_array[state]; +} + +const char *rt_export_state_name(u8 state) +{ + if (state >= TES_MAX) + return "!! INVALID !!"; + else + return rt_export_state_name_array[state]; +} /* Like fib_route(), but skips empty net entries */ @@ -356,26 +384,43 @@ rte_mergable(rte *pri, rte *sec) } static void -rte_trace(struct channel *c, rte *e, int dir, const char *msg) +rte_trace(const char *name, const rte *e, int dir, const char *msg) { - log(L_TRACE "%s.%s %c %s %N %uL %uG %s", - c->proto->name, c->name ?: "?", dir, msg, e->net, e->src->private_id, e->src->global_id, + log(L_TRACE "%s %c %s %N %uL %uG %s", + name, dir, msg, e->net, e->src->private_id, e->src->global_id, rta_dest_name(e->attrs->dest)); } static inline void -rte_trace_in(uint flag, struct channel *c, rte *e, const char *msg) +channel_rte_trace_in(uint flag, struct channel *c, const rte *e, const char *msg) { if ((c->debug & flag) || (c->proto->debug & flag)) - rte_trace(c, e, '>', msg); + rte_trace(c->in_req.name, e, '>', msg); } static inline void -rte_trace_out(uint flag, struct channel *c, rte *e, const char *msg) +channel_rte_trace_out(uint flag, struct channel *c, const rte *e, const char *msg) { if ((c->debug & flag) || (c->proto->debug & flag)) - rte_trace(c, e, '<', msg); + rte_trace(c->out_req.name, e, '<', msg); +} + +static inline void +rt_rte_trace_in(uint flag, struct rt_import_request *req, const rte *e, const char *msg) +{ + if (req->trace_routes & flag) + rte_trace(req->name, e, '>', msg); +} + +#if 0 +// seems to be unused at all +static inline void +rt_rte_trace_out(uint flag, struct rt_export_request *req, const rte *e, const char *msg) +{ + if (req->trace_routes & flag) + rte_trace(req->name, e, '<', msg); } +#endif static uint rte_feed_count(net *n) @@ -405,7 +450,7 @@ export_filter_(struct channel *c, rte *rt, linpool *pool, int silent) { struct proto *p = c->proto; const struct filter *filter = c->out_filter; - struct export_stats *stats = &c->export_stats; + struct channel_export_stats *stats = &c->export_stats; /* Do nothing if we have already rejected the route */ if (silent && bmap_test(&c->export_reject_map, rt->id)) @@ -419,14 +464,14 @@ export_filter_(struct channel *c, rte *rt, linpool *pool, int silent) stats->updates_rejected++; if (v == RIC_REJECT) - rte_trace_out(D_FILTERS, c, rt, "rejected by protocol"); + channel_rte_trace_out(D_FILTERS, c, rt, "rejected by protocol"); goto reject_noset; } if (v > 0) { if (!silent) - rte_trace_out(D_FILTERS, c, rt, "forced accept by protocol"); + channel_rte_trace_out(D_FILTERS, c, rt, "forced accept by protocol"); goto accept; } @@ -439,7 +484,7 @@ export_filter_(struct channel *c, rte *rt, linpool *pool, int silent) goto reject; stats->updates_filtered++; - rte_trace_out(D_FILTERS, c, rt, "filtered out"); + channel_rte_trace_out(D_FILTERS, c, rt, "filtered out"); goto reject; } @@ -453,7 +498,6 @@ export_filter_(struct channel *c, rte *rt, linpool *pool, int silent) bmap_set(&c->export_reject_map, rt->id); reject_noset: - /* Invalidate the route */ /* Discard temporary rte */ return NULL; } @@ -465,19 +509,19 @@ export_filter(struct channel *c, rte *rt, int silent) } static void -do_rt_notify(struct channel *c, const net_addr *net, rte *new, rte *old, int refeed) +do_rt_notify(struct channel *c, const net_addr *net, rte *new, const rte *old) { struct proto *p = c->proto; - struct export_stats *stats = &c->export_stats; + struct channel_export_stats *stats = &c->export_stats; - if (refeed && new) + if (c->refeeding && new) c->refeed_count++; if (!old && new) if (CHANNEL_LIMIT_PUSH(c, OUT)) { stats->updates_rejected++; - rte_trace_out(D_FILTERS, c, new, "rejected [limit]"); + channel_rte_trace_out(D_FILTERS, c, new, "rejected [limit]"); return; } @@ -490,7 +534,7 @@ do_rt_notify(struct channel *c, const net_addr *net, rte *new, rte *old, int ref { if (!rte_update_out(c, net, new, old, &old_exported)) { - rte_trace_out(D_ROUTES, c, new, "idempotent"); + channel_rte_trace_out(D_ROUTES, c, new, "idempotent"); return; } } @@ -509,11 +553,11 @@ do_rt_notify(struct channel *c, const net_addr *net, rte *new, rte *old, int ref if (p->debug & D_ROUTES) { if (new && old) - rte_trace_out(D_ROUTES, c, new, "replaced"); + channel_rte_trace_out(D_ROUTES, c, new, "replaced"); else if (new) - rte_trace_out(D_ROUTES, c, new, "added"); + channel_rte_trace_out(D_ROUTES, c, new, "added"); else if (old) - rte_trace_out(D_ROUTES, c, old, "removed"); + channel_rte_trace_out(D_ROUTES, c, old, "removed"); } p->rt_notify(p, c, net, new, old_exported ? &old_exported->rte : old); @@ -523,14 +567,9 @@ do_rt_notify(struct channel *c, const net_addr *net, rte *new, rte *old, int ref } static void -rt_notify_basic(struct channel *c, const net_addr *net, rte *new, rte *old, int refeed) +rt_notify_basic(struct channel *c, const net_addr *net, rte *new, rte *old) { if (new) - c->export_stats.updates_received++; - else - c->export_stats.withdraws_received++; - - if (new) new = export_filter(c, new, 0); if (old && !bmap_test(&c->export_map, old->id)) @@ -539,14 +578,17 @@ rt_notify_basic(struct channel *c, const net_addr *net, rte *new, rte *old, int if (!new && !old) return; - do_rt_notify(c, net, new, old, refeed); + do_rt_notify(c, net, new, old); } -static void -rt_notify_accepted(struct channel *c, const net_addr *n, struct rt_pending_export *rpe, - struct rte **feed, uint count, int refeed) +void +rt_notify_accepted(struct rt_export_request *req, const net_addr *n, struct rt_pending_export *rpe, + struct rte **feed, uint count) { - rte nb0, *new_best = NULL, *old_best = NULL; + struct channel *c = SKIP_BACK(struct channel, out_req, req); + + rte nb0, *new_best = NULL; + const rte *old_best = NULL; for (uint i = 0; i < count; i++) { @@ -554,7 +596,7 @@ rt_notify_accepted(struct channel *c, const net_addr *n, struct rt_pending_expor continue; /* Has been already rejected, won't bother with it */ - if (!refeed && bmap_test(&c->export_reject_map, feed[i]->id)) + if (!c->refeeding && bmap_test(&c->export_reject_map, feed[i]->id)) continue; /* Previously exported */ @@ -564,7 +606,7 @@ rt_notify_accepted(struct channel *c, const net_addr *n, struct rt_pending_expor if (!new_best) { DBG("rt_notify_accepted: idempotent\n"); - return; + goto done; } /* is superseded */ @@ -604,10 +646,16 @@ rt_notify_accepted(struct channel *c, const net_addr *n, struct rt_pending_expor if (!new_best && !old_best) { DBG("rt_notify_accepted: nothing to export\n"); - return; + goto done; } - do_rt_notify(c, n, new_best, old_best, refeed); + do_rt_notify(c, n, new_best, old_best); + +done: + /* Drop the old stored rejection if applicable. + * new->id == old->id happens when updating hostentries. */ + if (rpe && rpe->old && (!rpe->new || (rpe->new->rte.id != rpe->old->rte.id))) + bmap_clear(&c->export_reject_map, rpe->old->rte.id); } @@ -617,20 +665,21 @@ nexthop_merge_rta(struct nexthop *nhs, rta *a, linpool *pool, int max) return nexthop_merge(nhs, &(a->nh), 1, 0, max, pool); } -static rte * -rt_export_merged(struct channel *c, struct rte **feed, uint count, linpool *pool, int silent, int refeed) +rte * +rt_export_merged(struct channel *c, struct rte **feed, uint count, linpool *pool, int silent) { _Thread_local static rte rloc; // struct proto *p = c->proto; struct nexthop *nhs = NULL; - rte *best0 = feed[0], *best = NULL; + rte *best0 = feed[0]; + rte *best = NULL; if (!rte_is_valid(best0)) return NULL; /* Already rejected, no need to re-run the filter */ - if (!refeed && bmap_test(&c->export_reject_map, best0->id)) + if (!c->refeeding && bmap_test(&c->export_reject_map, best0->id)) return NULL; rloc = *best0; @@ -672,19 +721,12 @@ rt_export_merged(struct channel *c, struct rte **feed, uint count, linpool *pool return best; } -rte * -rt_export_merged_show(struct channel *c, net *n, linpool *pool) +void +rt_notify_merged(struct rt_export_request *req, const net_addr *n, struct rt_pending_export *rpe, + struct rte **feed, uint count) { - uint count = rte_feed_count(n); - rte **feed = alloca(count * sizeof(rte *)); - rte_feed_obtain(n, feed, count); - return rt_export_merged(c, feed, count, pool, 1, 0); -} + struct channel *c = SKIP_BACK(struct channel, out_req, req); -static void -rt_notify_merged(struct channel *c, const net_addr *n, struct rt_pending_export *rpe, - struct rte **feed, uint count, int refeed) -{ // struct proto *p = c->proto; #if 0 /* TODO: Find whether this check is possible when processing multiple changes at once. */ @@ -724,28 +766,59 @@ rt_notify_merged(struct channel *c, const net_addr *n, struct rt_pending_export */ /* Prepare new merged route */ - rte *new_merged = count ? rt_export_merged(c, feed, count, rte_update_pool, 0, refeed) : NULL; + rte *new_merged = count ? rt_export_merged(c, feed, count, rte_update_pool, 0) : NULL; - if (!new_merged && !old_best) - return; + if (new_merged || old_best) + do_rt_notify(c, n, new_merged, old_best); - do_rt_notify(c, n, new_merged, old_best, refeed); + /* Drop the old stored rejection if applicable. + * new->id == old->id happens when updating hostentries. */ + if (rpe && rpe->old && (!rpe->new || (rpe->new->rte.id != rpe->old->rte.id))) + bmap_clear(&c->export_reject_map, rpe->old->rte.id); } -static void -rt_notify_bulk(struct channel *c, const net_addr *n, struct rt_pending_export *rpe, - struct rte **feed, uint count, int refeed) +void +rt_notify_optimal(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe) +{ + struct channel *c = SKIP_BACK(struct channel, out_req, req); + rte n0; + + if (rpe->new_best != rpe->old_best) + rt_notify_basic(c, net, RTE_COPY(rpe->new_best, &n0), RTE_OR_NULL(rpe->old_best)); + + /* Drop the old stored rejection if applicable. + * new->id == old->id happens when updating hostentries. */ + if (rpe->old && (!rpe->new || (rpe->new->rte.id != rpe->old->rte.id))) + bmap_clear(&c->export_reject_map, rpe->old->rte.id); +} + +void +rt_notify_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe) { - switch (c->ra_mode) + struct channel *c = SKIP_BACK(struct channel, out_req, req); + rte n0; + + if (rpe->new != rpe->old) + rt_notify_basic(c, net, RTE_COPY(rpe->new, &n0), RTE_OR_NULL(rpe->old)); + + /* Drop the old stored rejection if applicable. + * new->id == old->id happens when updating hostentries. */ + if (rpe->old && (!rpe->new || (rpe->new->rte.id != rpe->old->rte.id))) + bmap_clear(&c->export_reject_map, rpe->old->rte.id); +} + +void +rt_feed_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe UNUSED, rte **feed, uint count) +{ + struct channel *c = SKIP_BACK(struct channel, out_req, req); + + for (uint i=0; i<count; i++) { - case RA_ACCEPTED: - return rt_notify_accepted(c, n, rpe, feed, count, refeed); - case RA_MERGED: - return rt_notify_merged(c, n, rpe, feed, count, refeed); + rte n0 = *feed[i]; + rt_notify_basic(c, net, &n0, NULL); } } - /** * rte_announce - announce a routing table change * @tab: table the route has been added to @@ -799,9 +872,9 @@ rte_announce(rtable *tab, net *net, struct rte_storage *new, struct rte_storage if (new_best != old_best) { if (new_best) - new_best->rte.sender->import_stats.pref++; + new_best->rte.sender->stats.pref++; if (old_best) - old_best->rte.sender->import_stats.pref--; + old_best->rte.sender->stats.pref--; if (tab->hostcache) rt_notify_hostcache(tab, net); @@ -809,46 +882,37 @@ rte_announce(rtable *tab, net *net, struct rte_storage *new, struct rte_storage rt_schedule_notify(tab); - struct channel *c; node *n; - WALK_LIST2(c, n, tab->channels, table_node) + struct rt_pending_export rpe = { .new = new, .old = old, .new_best = new_best, .old_best = old_best }; + uint count = rte_feed_count(net); + rte **feed = NULL; + if (count) { - if (c->export_state == ES_DOWN) - continue; - - rte n0; - switch (c->ra_mode) - { - case RA_OPTIMAL: - if (new_best != old_best) - rt_notify_basic(c, net->n.addr, RTE_COPY(new_best, &n0), RTE_OR_NULL(old_best), 0); - break; + feed = alloca(count * sizeof(rte *)); + rte_feed_obtain(net, feed, count); + } - case RA_ANY: - if (new != old) - rt_notify_basic(c, net->n.addr, RTE_COPY(new, &n0), RTE_OR_NULL(old), 0); - break; + struct rt_export_hook *eh; + WALK_LIST(eh, tab->exports) + { + if (eh->export_state == TES_STOP) + continue; - case RA_ACCEPTED: - case RA_MERGED: - { - struct rt_pending_export rpe = { .new = new, .old = old, .new_best = new_best, .old_best = old_best }; - uint count = rte_feed_count(net); - rte **feed = alloca(count * sizeof(rte *)); - rte_feed_obtain(net, feed, count); - rt_notify_bulk(c, net->n.addr, &rpe, feed, count, 0); - break; - } - } + if (new) + eh->stats.updates_received++; + else + eh->stats.withdraws_received++; - /* Drop the old stored rejection if applicable. - * new->id == old->id happens when updating hostentries. */ - if (old && (!new || (new->rte.id != old->rte.id))) - bmap_clear(&c->export_reject_map, old->rte.id); + if (eh->req->export_one) + eh->req->export_one(eh->req, net->n.addr, &rpe); + else if (eh->req->export_bulk) + eh->req->export_bulk(eh->req, net->n.addr, &rpe, feed, count); + else + bug("Export request must always provide an export method"); } } static inline int -rte_validate(rte *e) +rte_validate(struct channel *ch, rte *e) { int c; const net_addr *n = e->net; @@ -856,7 +920,7 @@ rte_validate(rte *e) if (!net_validate(n)) { log(L_WARN "Ignoring bogus prefix %N received via %s", - n, e->sender->proto->name); + n, ch->proto->name); return 0; } @@ -866,21 +930,21 @@ rte_validate(rte *e) if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) { log(L_WARN "Ignoring bogus route %N received via %s", - n, e->sender->proto->name); + n, ch->proto->name); return 0; } if (net_type_match(n, NB_DEST) == !e->attrs->dest) { log(L_WARN "Ignoring route %N with invalid dest %d received via %s", - n, e->attrs->dest, e->sender->proto->name); + n, e->attrs->dest, ch->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, e->sender->proto->name); + n, ch->proto->name); return 0; } @@ -901,11 +965,11 @@ rte_same(rte *x, rte *y) static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); } static void -rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) +rte_recalculate(struct rt_import_hook *c, net *net, rte *new, struct rte_src *src) { - struct proto *p = c->proto; + struct rt_import_request *req = c->req; struct rtable *table = c->table; - struct import_stats *stats = &c->import_stats; + struct rt_import_stats *stats = &c->stats; struct rte_storage *old_best_stored = net->routes, *old_stored = NULL; rte *old_best = old_best_stored ? &old_best_stored->rte : NULL; rte *old = NULL; @@ -922,7 +986,7 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) * source protocol to this routing table through transparent * pipes, which is not allowed. * We log that and ignore the route. */ - if (old->sender->proto != p) + if (old->sender != c) { if (!old->generation && !new->generation) bug("Two protocols claim to author a route with the same rte_src in table %s: %N %s/%u:%u", @@ -930,8 +994,6 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) log_rl(&table->rl_pipe, L_ERR "Route source collision in table %s: %N %s/%u:%u", c->table->name, net->n.addr, old->src->proto->name, old->src->private_id, old->src->global_id); - - return; } if (new && rte_same(old, new)) @@ -943,13 +1005,10 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) if (!rte_is_filtered(new)) { stats->updates_ignored++; - rte_trace_in(D_ROUTES, c, new, "ignored"); + rt_rte_trace_in(D_ROUTES, req, new, "ignored"); } - - return; } - *before_old = (*before_old)->next; table->rt_count--; } @@ -960,57 +1019,12 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) return; } + if (req->preimport) + new = req->preimport(req, new, old); + int new_ok = rte_is_ok(new); int old_ok = rte_is_ok(old); - if (!c->in_table) - { - if (!old && new) - if (CHANNEL_LIMIT_PUSH(c, RX)) - { - /* In receive limit the situation is simple, old is NULL so - we just free new and exit like nothing happened */ - - stats->updates_ignored++; - rte_trace_in(D_FILTERS, c, new, "ignored [limit]"); - return; - } - - if (old && !new) - CHANNEL_LIMIT_POP(c, RX); - } - - if (!old_ok && new_ok) - if (CHANNEL_LIMIT_PUSH(c, IN)) - { - /* 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->updates_ignored++; - rte_trace_in(D_FILTERS, c, new, "ignored [limit]"); - - if (c->in_keep_filtered) - new->flags |= REF_FILTERED; - else - new = NULL; - - /* Note that old && !new could be possible when - c->in_keep_filtered changed in the recent past. */ - - if (!old && !new) - return; - - new_ok = 0; - goto skip_stats1; - } - - if (old_ok && !new_ok) - CHANNEL_LIMIT_POP(c, IN); - if (new_ok) stats->updates_accepted++; else if (old_ok) @@ -1021,7 +1035,6 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) if (old_ok || new_ok) table->last_rt_change = current_time(); - skip_stats1:; struct rte_storage *new_stored = new ? rte_store(new, net, table) : NULL; if (table->config->sorted) @@ -1126,19 +1139,16 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) } /* Log the route change */ - if ((c->debug & D_ROUTES) || (p->debug & D_ROUTES)) + if (new_ok) + rt_rte_trace_in(D_ROUTES, req, &new_stored->rte, new_stored == net->routes ? "added [best]" : "added"); + else if (old_ok) { - if (new_ok) - rte_trace(c, &new_stored->rte, '>', new_stored == net->routes ? "added [best]" : "added"); - else if (old_ok) - { - if (old != old_best) - rte_trace(c, old, '>', "removed"); - else if (net->routes && rte_is_ok(&net->routes->rte)) - rte_trace(c, old, '>', "removed [replaced]"); - else - rte_trace(c, old, '>', "removed [sole]"); - } + if (old != old_best) + rt_rte_trace_in(D_ROUTES, req, old, "removed"); + else if (net->routes && rte_is_ok(&net->routes->rte)) + rt_rte_trace_in(D_ROUTES, req, old, "removed [replaced]"); + else + rt_rte_trace_in(D_ROUTES, req, old, "removed [sole]"); } /* Propagate the route change */ @@ -1150,10 +1160,13 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) (table->gc_time + table->config->gc_min_time <= current_time())) rt_schedule_prune(table); +#if 0 + /* Enable and reimplement these callbacks if anybody wants to use them */ if (old_ok && p->rte_remove) p->rte_remove(net, old); if (new_ok && p->rte_insert) p->rte_insert(net, &new_stored->rte); +#endif if (old) { @@ -1179,89 +1192,116 @@ rte_update_unlock(void) lp_flush(rte_update_pool); } -static int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src); +rte * +channel_preimport(struct rt_import_request *req, rte *new, rte *old) +{ + struct channel *c = SKIP_BACK(struct channel, in_req, req); + + if (new && !old) + if (CHANNEL_LIMIT_PUSH(c, RX)) + return NULL; + + if (!new && old) + CHANNEL_LIMIT_POP(c, RX); + + int new_in = new && !rte_is_filtered(new); + int old_in = old && !rte_is_filtered(old); + + if (new_in && !old_in) + if (CHANNEL_LIMIT_PUSH(c, IN)) + if (c->in_keep_filtered) + { + new->flags |= REF_FILTERED; + return new; + } + else + return NULL; + + if (!new_in && old_in) + CHANNEL_LIMIT_POP(c, IN); + + return new; +} + +static void rte_update_direct(struct channel *c, const net_addr *n, rte *new, struct rte_src *src); void rte_update(struct channel *c, const net_addr *n, rte *new, struct rte_src *src) { + if (!c->in_req.hook) + return; + + ASSERT(c->channel_state == CS_UP); + if (c->in_table && !rte_update_in(c, n, new, src)) return; - struct import_stats *stats = &c->import_stats; - const struct filter *filter = c->in_filter; - net *nn; + return rte_update_direct(c, n, new, src); +} - ASSERT(c->channel_state == CS_UP); +static void +rte_update_direct(struct channel *c, const net_addr *n, rte *new, struct rte_src *src) +{ + const struct filter *filter = c->in_filter; + struct channel_import_stats *stats = &c->import_stats; rte_update_lock(); if (new) { new->net = n; - new->sender = c; + + int fr; stats->updates_received++; - if (!rte_validate(new)) + if (!rte_validate(c, new)) { - rte_trace_in(D_FILTERS, c, new, "invalid"); + channel_rte_trace_in(D_FILTERS, c, new, "invalid"); stats->updates_invalid++; - goto drop; + new = NULL; } - - if (filter == FILTER_REJECT) + else if ((filter == FILTER_REJECT) || + ((fr = f_run(filter, new, rte_update_pool, 0)) > F_ACCEPT)) { stats->updates_filtered++; - rte_trace_in(D_FILTERS, c, new, "filtered out"); + channel_rte_trace_in(D_FILTERS, c, new, "filtered out"); - if (! c->in_keep_filtered) - goto drop; - - /* new is a private copy, i could modify it */ - new->flags |= REF_FILTERED; + if (c->in_keep_filtered) + new->flags |= REF_FILTERED; + else + new = NULL; } - else if (filter) - { - int fr = f_run(filter, new, rte_update_pool, 0); - if (fr > F_ACCEPT) - { - stats->updates_filtered++; - rte_trace_in(D_FILTERS, c, new, "filtered out"); + } + else + stats->withdraws_received++; - if (! c->in_keep_filtered) - goto drop; + rte_import(&c->in_req, n, new, src); - new->flags |= REF_FILTERED; - } - } + rte_update_unlock(); +} +void +rte_import(struct rt_import_request *req, const net_addr *n, rte *new, struct rte_src *src) +{ + struct rt_import_hook *hook = req->hook; + if (!hook) + return; + + net *nn; + if (new) + { /* Use the actual struct network, not the dummy one */ - nn = net_get(c->table, n); + nn = net_get(hook->table, n); new->net = nn->n.addr; + new->sender = hook; } - else + else if (!(nn = net_find(hook->table, n))) { - stats->withdraws_received++; - - if (!(nn = net_find(c->table, n)) || !src) - { - stats->withdraws_ignored++; - rte_update_unlock(); - return; - } + req->hook->stats.withdraws_ignored++; + return; } - recalc: /* And recalculate the best route */ - rte_recalculate(c, nn, new, src); - - rte_update_unlock(); - return; - - drop: - new = NULL; - if (nn = net_find(c->table, n)) - goto recalc; - - rte_update_unlock(); + rte_recalculate(hook, nn, new, src); } /* Independent call to rte_announce(), used from next hop @@ -1289,7 +1329,7 @@ rte_modify(net *net, rte *old) { rte_update_lock(); - rte *new = old->sender->proto->rte_modify(old, rte_update_pool); + rte *new = old->sender->req->rte_modify(old, rte_update_pool); if (new != old) { if (new) @@ -1327,6 +1367,127 @@ rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filte return v > 0; } +static void +rt_export_stopped(void *data) +{ + struct rt_export_hook *hook = data; + rtable *tab = hook->table; + + /* Unlist */ + rem_node(&hook->n); + + /* Reporting the channel as stopped. */ + hook->stopped(hook->req); + + /* Freeing the hook together with its coroutine. */ + rfree(hook->pool); + rt_unlock_table(tab); + + DBG("Export hook %p in table %s finished uc=%u\n", hook, tab->name, tab->use_count); +} + + +static inline void +rt_set_import_state(struct rt_import_hook *hook, u8 state) +{ + hook->last_state_change = current_time(); + hook->import_state = state; + + if (hook->req->log_state_change) + hook->req->log_state_change(hook->req, state); +} + +static inline void +rt_set_export_state(struct rt_export_hook *hook, u8 state) +{ + hook->last_state_change = current_time(); + hook->export_state = state; + + if (hook->req->log_state_change) + hook->req->log_state_change(hook->req, state); +} + +void +rt_request_import(rtable *tab, struct rt_import_request *req) +{ + rt_lock_table(tab); + + struct rt_import_hook *hook = req->hook = mb_allocz(tab->rp, sizeof(struct rt_import_hook)); + + DBG("Lock table %s for import %p req=%p uc=%u\n", tab->name, hook, req, tab->use_count); + + hook->req = req; + hook->table = tab; + + rt_set_import_state(hook, TIS_UP); + + hook->n = (node) {}; + add_tail(&tab->imports, &hook->n); +} + +void +rt_stop_import(struct rt_import_request *req, void (*stopped)(struct rt_import_request *)) +{ + ASSERT_DIE(req->hook); + struct rt_import_hook *hook = req->hook; + + rt_schedule_prune(hook->table); + + rt_set_import_state(hook, TIS_STOP); + + hook->stopped = stopped; +} + +void +rt_request_export(rtable *tab, struct rt_export_request *req) +{ + rt_lock_table(tab); + + pool *p = rp_new(tab->rp, "Export hook"); + struct rt_export_hook *hook = req->hook = mb_allocz(p, sizeof(struct rt_export_hook)); + hook->pool = p; + hook->lp = lp_new_default(p); + + hook->req = req; + hook->table = tab; + + /* stats zeroed by mb_allocz */ + + rt_set_export_state(hook, TES_HUNGRY); + + hook->n = (node) {}; + add_tail(&tab->exports, &hook->n); + + FIB_ITERATE_INIT(&hook->feed_fit, &tab->fib); + + DBG("New export hook %p req %p in table %s uc=%u\n", hook, req, tab->name, tab->use_count); + + rt_set_export_state(hook, TES_FEEDING); + + hook->event = ev_new_init(p, rt_feed_channel, hook); + ev_schedule_work(hook->event); +} + +void +rt_stop_export(struct rt_export_request *req, void (*stopped)(struct rt_export_request *)) +{ + ASSERT_DIE(req->hook); + struct rt_export_hook *hook = req->hook; + + rtable *tab = hook->table; + + /* Stop feeding */ + ev_postpone(hook->event); + + if (hook->export_state == TES_FEEDING) + fit_get(&tab->fib, &hook->feed_fit); + + hook->event->hook = rt_export_stopped; + hook->stopped = stopped; + + rt_set_export_state(hook, TES_STOP); + ev_schedule(hook->event); +} /** * rt_refresh_begin - start a refresh cycle @@ -1343,12 +1504,12 @@ rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filte * flag in rt_refresh_end() and then removing such routes in the prune loop. */ void -rt_refresh_begin(rtable *t, struct channel *c) +rt_refresh_begin(rtable *t, struct rt_import_request *req) { FIB_WALK(&t->fib, net, n) { for (struct rte_storage *e = n->routes; e; e = e->next) - if (e->rte.sender == c) + if (e->rte.sender == req->hook) e->rte.flags |= REF_STALE; } FIB_WALK_END; @@ -1363,14 +1524,14 @@ rt_refresh_begin(rtable *t, struct channel *c) * hook. See rt_refresh_begin() for description of refresh cycles. */ void -rt_refresh_end(rtable *t, struct channel *c) +rt_refresh_end(rtable *t, struct rt_import_request *req) { int prune = 0; FIB_WALK(&t->fib, net, n) { for (struct rte_storage *e = n->routes; e; e = e->next) - if ((e->rte.sender == c) && (e->rte.flags & REF_STALE)) + if ((e->rte.sender == req->hook) && (e->rte.flags & REF_STALE)) { e->rte.flags |= REF_DISCARD; prune = 1; @@ -1383,14 +1544,14 @@ rt_refresh_end(rtable *t, struct channel *c) } void -rt_modify_stale(rtable *t, struct channel *c) +rt_modify_stale(rtable *t, struct rt_import_request *req) { int prune = 0; FIB_WALK(&t->fib, net, n) { for (struct rte_storage *e = n->routes; e; e = e->next) - if ((e->rte.sender == c) && (e->rte.flags & REF_STALE) && !(e->rte.flags & REF_FILTERED)) + if ((e->rte.sender == req->hook) && (e->rte.flags & REF_STALE) && !(e->rte.flags & REF_FILTERED)) { e->rte.flags |= REF_MODIFY; prune = 1; @@ -1426,7 +1587,7 @@ rte_dump(struct rte_storage *e) void rt_dump(rtable *t) { - debug("Dump of routing table <%s>\n", t->name); + debug("Dump of routing table <%s>%s\n", t->name, t->deleted ? " (deleted)" : ""); #ifdef DEBUGGING fib_check(&t->fib); #endif @@ -1452,6 +1613,54 @@ rt_dump_all(void) WALK_LIST2(t, n, routing_tables, n) rt_dump(t); + + WALK_LIST2(t, n, deleted_routing_tables, n) + rt_dump(t); +} + +void +rt_dump_hooks(rtable *tab) +{ + debug("Dump of hooks in routing table <%s>%s\n", tab->name, tab->deleted ? " (deleted)" : ""); + debug(" nhu_state=%u hcu_scheduled=%u use_count=%d rt_count=%u\n", + tab->nhu_state, tab->hcu_scheduled, tab->use_count, tab->rt_count); + debug(" last_rt_change=%t gc_time=%t gc_counter=%d prune_state=%u\n", + tab->last_rt_change, tab->gc_time, tab->gc_counter, tab->prune_state); + + struct rt_import_hook *ih; + WALK_LIST(ih, tab->imports) + { + ih->req->dump_req(ih->req); + debug(" Import hook %p requested by %p: pref=%u" + " last_state_change=%t import_state=%u stopped=%p\n", + ih, ih->req, ih->stats.pref, + ih->last_state_change, ih->import_state, ih->stopped); + } + + struct rt_export_hook *eh; + WALK_LIST(eh, tab->exports) + { + eh->req->dump_req(eh->req); + debug(" Export hook %p requested by %p:" + " refeed_pending=%u last_state_change=%t export_state=%u stopped=%p\n", + eh, eh->req, eh->refeed_pending, eh->last_state_change, eh->export_state, eh->stopped); + } + debug("\n"); +} + +void +rt_dump_hooks_all(void) +{ + rtable *t; + node *n; + + debug("Dump of all table hooks\n"); + + WALK_LIST2(t, n, routing_tables, n) + rt_dump_hooks(t); + + WALK_LIST2(t, n, deleted_routing_tables, n) + rt_dump_hooks(t); } static inline void @@ -1569,6 +1778,7 @@ rt_subscribe(rtable *tab, struct rt_subscription *s) { s->tab = tab; rt_lock_table(tab); + DBG("rt_subscribe(%s)\n", tab->name); add_tail(&tab->subscribers, &s->n); } @@ -1645,7 +1855,8 @@ rt_setup(pool *pp, struct rtable_config *cf) if (!(t->internal = cf->internal)) { - init_list(&t->channels); + init_list(&t->imports); + init_list(&t->exports); hmap_init(&t->id_map, p, 1024); hmap_set(&t->id_map, 0); @@ -1673,6 +1884,7 @@ rt_init(void) rt_table_pool = rp_new(&root_pool, "Routing tables"); rte_update_pool = lp_new_default(rt_table_pool); init_list(&routing_tables); + init_list(&deleted_routing_tables); } @@ -1696,7 +1908,7 @@ rt_prune_table(rtable *tab) struct fib_iterator *fit = &tab->prune_fit; int limit = 512; - struct channel *c; + struct rt_import_hook *ih; node *n, *x; DBG("Pruning route table %s\n", tab->name); @@ -1710,9 +1922,9 @@ rt_prune_table(rtable *tab) if (tab->prune_state == 1) { /* Mark channels to flush */ - WALK_LIST2(c, n, tab->channels, table_node) - if (c->channel_state == CS_FLUSHING) - c->flush_active = 1; + WALK_LIST2(ih, n, tab->imports, n) + if (ih->import_state == TIS_STOP) + rt_set_import_state(ih, TIS_FLUSHING); FIB_ITERATE_INIT(fit, &tab->fib); tab->prune_state = 2; @@ -1724,7 +1936,7 @@ again: rescan: for (struct rte_storage *e=n->routes; e; e=e->next) { - if (e->rte.sender->flush_active || (e->rte.flags & REF_DISCARD)) + if ((e->rte.sender->import_state == TIS_FLUSHING) || (e->rte.flags & REF_DISCARD)) { if (limit <= 0) { @@ -1774,21 +1986,18 @@ again: /* state change 2->0, 3->1 */ tab->prune_state &= 1; - if (tab->prune_state > 0) - ev_schedule(tab->rt_event); - - /* FIXME: This should be handled in a better way */ rt_prune_sources(); /* Close flushed channels */ - WALK_LIST2_DELSAFE(c, n, x, tab->channels, table_node) - if (c->flush_active) - { - c->flush_active = 0; - channel_set_state(c, CS_DOWN); - } - - return; + WALK_LIST2_DELSAFE(ih, n, x, tab->imports, n) + if (ih->import_state == TIS_FLUSHING) + { + rt_set_import_state(ih, TIS_CLEARED); + ih->stopped(ih->req); + rem_node(&ih->n); + mb_free(ih); + rt_unlock_table(tab); + } } void @@ -1996,7 +2205,7 @@ rt_next_hop_update_net(rtable *tab, net *n) { _Bool nb = (new == updates[i].new), ob = (old_best == updates[i].old); const char *best_indicator[2][2] = { { "updated", "updated [-best]" }, { "updated [+best]", "updated [best]" } }; - rte_trace_in(D_ROUTES, new->rte.sender, &updates[i].new->rte, best_indicator[nb][ob]); + rt_rte_trace_in(D_ROUTES, updates[i].new->rte.sender->req, &updates[i].new->rte, best_indicator[nb][ob]); rte_announce_i(tab, n, updates[i].new, updates[i].old, new, old_best); } @@ -2171,25 +2380,6 @@ rt_commit(struct config *new, struct config *old) DBG("\tdone\n"); } -static inline void -do_feed_channel(struct channel *c, net *n, rte *e) -{ - rte_update_lock(); - if ((c->ra_mode == RA_ACCEPTED) || (c->ra_mode == RA_MERGED)) - { - uint count = rte_feed_count(n); - rte **feed = alloca(count * sizeof(rte *)); - rte_feed_obtain(n, feed, count); - rt_notify_bulk(c, n->n.addr, NULL, feed, count, c->refeeding); - } - else /* RA_BASIC */ - { - rte e0 = *e; - rt_notify_basic(c, n->n.addr, &e0, &e0, c->refeeding); - } - rte_update_unlock(); -} - /** * rt_feed_channel - advertise all routes to a channel * @c: channel to be fed @@ -2199,79 +2389,55 @@ do_feed_channel(struct channel *c, net *n, rte *e) * has something to do. (We avoid transferring all the routes in single pass in * order not to monopolize CPU time.) */ -int -rt_feed_channel(struct channel *c) +static void +rt_feed_channel(void *data) { + struct rt_export_hook *c = data; + struct fib_iterator *fit = &c->feed_fit; int max_feed = 256; - ASSERT(c->export_state == ES_FEEDING); - - if (!c->feed_active) - { - FIB_ITERATE_INIT(fit, &c->table->fib); - c->feed_active = 1; - } + ASSERT(c->export_state == TES_FEEDING); FIB_ITERATE_START(&c->table->fib, fit, net, n) { - struct rte_storage *e = n->routes; if (max_feed <= 0) { FIB_ITERATE_PUT(fit); - return 0; + ev_schedule_work(c->event); + return; } - if ((c->ra_mode == RA_OPTIMAL) || - (c->ra_mode == RA_ACCEPTED) || - (c->ra_mode == RA_MERGED)) - if (e && rte_is_valid(&e->rte)) - { - /* In the meantime, the protocol may fell down */ - if (c->export_state != ES_FEEDING) - goto done; + if (c->export_state != TES_FEEDING) + goto done; - do_feed_channel(c, n, &e->rte); - max_feed--; - } - - if (c->ra_mode == RA_ANY) - for(e = n->routes; e; e = e->next) - { - /* In the meantime, the protocol may fell down */ - if (c->export_state != ES_FEEDING) - goto done; - - if (!rte_is_valid(&e->rte)) - continue; - - do_feed_channel(c, n, &e->rte); - max_feed--; - } + if (c->req->export_bulk) + { + uint count = rte_feed_count(n); + if (count) + { + rte_update_lock(); + rte **feed = alloca(count * sizeof(rte *)); + rte_feed_obtain(n, feed, count); + struct rt_pending_export rpe = { .new_best = n->routes }; + c->req->export_bulk(c->req, n->n.addr, &rpe, feed, count); + max_feed -= count; + rte_update_unlock(); + } + } + else if (n->routes && rte_is_valid(&n->routes->rte)) + { + rte_update_lock(); + struct rt_pending_export rpe = { .new = n->routes, .new_best = n->routes }; + c->req->export_one(c->req, n->n.addr, &rpe); + max_feed--; + rte_update_unlock(); + } } FIB_ITERATE_END; done: - c->feed_active = 0; - return 1; -} - -/** - * rt_feed_baby_abort - abort protocol feeding - * @c: channel - * - * This function is called by the protocol code when the protocol stops or - * ceases to exist during the feeding. - */ -void -rt_feed_channel_abort(struct channel *c) -{ - if (c->feed_active) - { - /* Unlink the iterator */ - fit_get(&c->table->fib, &c->feed_fit); - c->feed_active = 0; - } + rt_set_export_state(c, TES_READY); } @@ -2279,7 +2445,7 @@ rt_feed_channel_abort(struct channel *c) * Import table */ -static int +int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src) { struct rtable *tab = c->in_table; @@ -2332,7 +2498,7 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr /* Required by rte_trace_in() */ new->net = n; - rte_trace_in(D_FILTERS, c, new, "ignored [limit]"); + channel_rte_trace_in(D_FILTERS, c, new, "ignored [limit]"); goto drop_update; } } @@ -2349,7 +2515,6 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr /* Insert the new rte */ struct rte_storage *e = rte_store(new, net, tab); - e->rte.sender = c; e->rte.lastmod = current_time(); e->next = *pos; *pos = e; @@ -2358,7 +2523,7 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr drop_update: c->import_stats.updates_received++; - c->import_stats.updates_ignored++; + c->in_req.hook->stats.updates_ignored++; if (!net->routes) fib_delete(&tab->fib, net); @@ -2367,7 +2532,7 @@ drop_update: drop_withdraw: c->import_stats.withdraws_received++; - c->import_stats.withdraws_ignored++; + c->in_req.hook->stats.withdraws_ignored++; return 0; } @@ -2397,7 +2562,7 @@ rt_reload_channel(struct channel *c) } rte r = e->rte; - rte_update(c, r.net, &r, r.src); + rte_update_direct(c, r.net, &r, r.src); } c->reload_next_rte = NULL; @@ -2470,7 +2635,7 @@ again: */ int -rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, struct rte_storage **old_exported) +rte_update_out(struct channel *c, const net_addr *n, rte *new, const rte *old0, struct rte_storage **old_exported) { struct rtable *tab = c->out_table; struct rte_src *src; |