* at the end of f_run()), also the lock of hostentry is inherited (we
* suppose hostentry is not changed by filters).
*/
- fs->rte->attrs = rta_do_cow(fs->rte->attrs, fs->pool);
- (*fs->rte)->attrs = rta_do_cow((*fs->rte)->attrs, tmp_linpool);
++ fs->rte->attrs = rta_do_cow(fs->rte->attrs, tmp_linpool);
/* Re-cache the ea_list */
f_cache_eattrs(fs);
* @tmp_pool: all filter allocations go from this pool
* @flags: flags
*
- * If filter needs to modify the route, there are several
- * posibilities. @rte might be read-only (with REF_COW flag), in that
- * case rw copy is obtained by rte_cow() and @rte is replaced. If
- * @rte is originally rw, it may be directly modified (and it is never
- * copied).
- *
- * The returned rte may reuse the (possibly cached, cloned) rta, or
- * (if rta was modified) contains a modified uncached rta, which
- * uses parts allocated from @tmp_pool and parts shared from original
- * rta. There is one exception - if @rte is rw but contains a cached
- * rta and that is modified, rta in returned rte is also cached.
- *
- * Ownership of cached rtas is consistent with rte, i.e.
- * if a new rte is returned, it has its own clone of cached rta
- * (and cached rta of read-only source rte is intact), if rte is
- * modified in place, old cached rta is possibly freed.
+ * If @rte->attrs is cached, the returned rte allocates a new rta on
+ * tmp_pool, otherwise the filters may modify it.
*/
enum filter_return
- f_run(const struct filter *filter, struct rte *rte, struct linpool *tmp_pool, int flags)
-f_run(const struct filter *filter, struct rte **rte, int flags)
++f_run(const struct filter *filter, struct rte *rte, int flags)
{
if (filter == FILTER_ACCEPT)
return F_ACCEPT;
*/
enum filter_return
- f_eval_rte(const struct f_line *expr, struct rte *rte, struct linpool *tmp_pool)
-f_eval_rte(const struct f_line *expr, struct rte **rte)
++f_eval_rte(const struct f_line *expr, struct rte *rte)
{
filter_state = (struct filter_state) {
.rte = rte,
}
static rte *
- export_filter_(struct channel *c, rte *rt, linpool *pool, int silent)
-export_filter(struct channel *c, rte *rt0, rte **rt_free, int silent)
++export_filter(struct channel *c, rte *rt, int silent)
{
struct proto *p = c->proto;
const struct filter *filter = c->out_filter;
}
v = filter && ((filter == FILTER_REJECT) ||
- (f_run(filter, rt, pool,
- (f_run(filter, &rt,
++ (f_run(filter, rt,
(silent ? FF_SILENT : 0)) > F_ACCEPT));
if (v)
{
return NULL;
}
- static inline rte *
- export_filter(struct channel *c, rte *rt, int silent)
- {
- return export_filter_(c, rt, rte_update_pool, silent);
- }
-
static void
-do_rt_notify(struct channel *c, net *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 proto_stats *stats = &c->stats;
+ struct channel_export_stats *stats = &c->export_stats;
- if (refeed && new)
+ if (c->refeeding && new)
c->refeed_count++;
- /* Apply export limit */
- struct channel_limit *l = &c->out_limit;
- if (l->action && !old && new)
- {
- if (stats->exp_routes >= l->limit)
- channel_notify_limit(c, l, PLD_OUT, stats->exp_routes);
-
- if (l->state == PLS_BLOCKED)
+ if (!old && new)
+ if (CHANNEL_LIMIT_PUSH(c, OUT))
{
- stats->exp_updates_rejected++;
- rte_trace_out(D_FILTERS, c, new, "rejected [limit]");
+ stats->updates_rejected++;
+ channel_rte_trace_out(D_FILTERS, c, new, "rejected [limit]");
return;
}
- }
+
+ if (!new && old)
+ CHANNEL_LIMIT_POP(c, OUT);
/* Apply export table */
- struct rte *old_exported = NULL;
+ struct rte_storage *old_exported = NULL;
if (c->out_table)
{
- if (!rte_update_out(c, net->n.addr, new, old, &old_exported, refeed))
+ if (!rte_update_out(c, net, new, old, &old_exported))
+ {
+ channel_rte_trace_out(D_ROUTES, c, new, "idempotent");
return;
+ }
}
- else if (c->out_filter == FILTER_ACCEPT)
- old_exported = old;
if (new)
- stats->exp_updates_accepted++;
+ stats->updates_accepted++;
else
- stats->exp_withdraws_accepted++;
+ stats->withdraws_accepted++;
if (old)
- {
bmap_clear(&c->export_map, old->id);
- stats->exp_routes--;
- }
if (new)
- {
bmap_set(&c->export_map, new->id);
- stats->exp_routes++;
- }
if (p->debug & D_ROUTES)
{
if (!rte_is_valid(best0))
return NULL;
- best = export_filter(c, best0, rt_free, silent);
+ /* Already rejected, no need to re-run the filter */
+ if (!c->refeeding && bmap_test(&c->export_reject_map, best0->id))
+ return NULL;
+
+ rloc = *best0;
- best = export_filter_(c, &rloc, pool, silent);
++ best = export_filter(c, &rloc, silent);
+
+ if (!best)
+ /* Best route doesn't pass the filter */
+ return NULL;
- if (!best || !rte_is_reachable(best))
+ if (!rte_is_reachable(best))
+ /* Unreachable routes can't be merged */
return best;
- for (rt0 = best0->next; rt0; rt0 = rt0->next)
+ for (uint i = 1; i < count; i++)
{
- if (!rte_mergable(best0, rt0))
+ if (!rte_mergable(best0, feed[i]))
continue;
- rt = export_filter(c, rt0, &tmp, 1);
+ rte tmp0 = *feed[i];
- rte *tmp = export_filter_(c, &tmp0, pool, 1);
++ rte *tmp = export_filter(c, &tmp0, 1);
- if (!rt)
+ if (!tmp || !rte_is_reachable(tmp))
continue;
- if (rte_is_reachable(rt))
- nhs = nexthop_merge_rta(nhs, rt->attrs, pool, c->merge_limit);
-
- if (tmp)
- rte_free(tmp);
+ nhs = nexthop_merge_rta(nhs, tmp->attrs, pool, c->merge_limit);
}
if (nhs)
rte_update_lock();
if (new)
{
- /* 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 = n;
- new->net = nn;
- new->sender = c;
+ int fr;
- stats->imp_updates_received++;
- if (!rte_validate(new))
+ stats->updates_received++;
+ if (!rte_validate(c, new))
{
- rte_trace_in(D_FILTERS, c, new, "invalid");
- stats->imp_updates_invalid++;
- goto drop;
+ channel_rte_trace_in(D_FILTERS, c, new, "invalid");
+ stats->updates_invalid++;
+ new = NULL;
}
-
- if (filter == FILTER_REJECT)
+ else if ((filter == FILTER_REJECT) ||
- ((fr = f_run(filter, new, rte_update_pool, 0)) > F_ACCEPT))
++ ((fr = f_run(filter, new, 0)) > F_ACCEPT))
{
- stats->imp_updates_filtered++;
- rte_trace_in(D_FILTERS, c, new, "filtered out");
+ stats->updates_filtered++;
+ 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, 0);
- if (fr > F_ACCEPT)
- {
- stats->imp_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;
- }
- }
- if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
- new->attrs = rta_lookup(new->attrs);
- new->flags |= REF_COW;
+ 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);
- new->net = nn;
+ nn = net_get(hook->table, n);
+ new->net = nn->n.addr;
+ new->sender = hook;
}
- else
+ else if (!(nn = net_find(hook->table, n)))
{
- stats->imp_withdraws_received++;
-
- if (!(nn = net_find(c->table, n)) || !src)
- {
- stats->imp_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:
- rte_free(new);
- 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
rte_update_lock();
/* Rest is stripped down export_filter() */
- int v = p->preexport ? p->preexport(p, rt) : 0;
+ int v = c->proto->preexport ? c->proto->preexport(c, &rt) : 0;
if (v == RIC_PROCESS)
- v = (f_run(filter, &rt, rte_update_pool, FF_SILENT) <= F_ACCEPT);
+ v = (f_run(filter, &rt, FF_SILENT) <= F_ACCEPT);
- /* Discard temporary rte */
- if (rt != n->routes)
- rte_free(rt);
-
rte_update_unlock();
return v > 0;