From: Maria Matejka Date: Wed, 1 Nov 2023 09:58:44 +0000 (+0100) Subject: Merge branch 'mq-aggregator-for-v3' into HEAD X-Git-Tag: v3.0.0~361 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=11faa7d36be79b73d9a7ad94208ec2d1dfe1d132;p=thirdparty%2Fbird.git Merge branch 'mq-aggregator-for-v3' into HEAD --- 11faa7d36be79b73d9a7ad94208ec2d1dfe1d132 diff --cc nest/rt-table.c index 3e5def686,a8f6226da..6ff4d336e --- a/nest/rt-table.c +++ b/nest/rt-table.c @@@ -1593,186 -1292,69 +1593,186 @@@ rt_export_hook(void *_data return; } - int new_ok = rte_is_ok(new); - int old_ok = rte_is_ok(old); + RT_UNLOCK(tab); + } + + int used = 0; + int no_next = 0; - struct channel_limit *l = &c->rx_limit; - if (l->action && !old && new && !c->in_table) + /* Process the export */ + for (uint i=0; irpe_next); + + if (!c->rpe_next) { - u32 all_routes = stats->imp_routes + stats->filt_routes; + no_next = 1; + break; + } + } - if (all_routes >= l->limit) - channel_notify_limit(c, l, PLD_RX, all_routes); + if (used) + RT_LOCKED(tab, t) + if (no_next || t->cork_active) + rt_export_used(c->table, c->h.req->name, no_next ? "finished export bulk" : "cork active"); - 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 */ + rt_send_export_event(&c->h); +} - stats->imp_updates_ignored++; - rte_trace_in(D_FILTERS, c, new, "ignored [limit]"); - rte_free_quick(new); - return; - } + +static inline int +rte_validate(struct channel *ch, rte *e) +{ + int c; + const net_addr *n = e->net; + + if (!net_validate(n)) + { + log(L_WARN "Ignoring bogus prefix %N received via %s", + n, ch->proto->name); + return 0; + } + + /* FIXME: better handling different nettypes */ + c = !net_is_flow(n) ? + net_classify(n): (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", + n, ch->proto->name); + return 0; + } + + if (net_type_match(n, NB_DEST)) + { + eattr *nhea = ea_find(e->attrs, &ea_gen_nexthop); + int dest = nhea_dest(nhea); + + if (dest == RTD_NONE) + { + log(L_WARN "Ignoring route %N with no destination received via %s", + n, ch->proto->name); + return 0; } - l = &c->in_limit; - if (l->action && !old_ok && new_ok) + if ((dest == RTD_UNICAST) && + !nexthop_is_sorted((struct nexthop_adata *) nhea->u.ptr)) { - if (stats->imp_routes >= l->limit) - channel_notify_limit(c, l, PLD_IN, stats->imp_routes); + log(L_WARN "Ignoring unsorted multipath route %N received via %s", + n, ch->proto->name); + return 0; + } + } + else if (ea_find(e->attrs, &ea_gen_nexthop)) + { + log(L_WARN "Ignoring route %N having a nexthop attribute received via %s", + n, ch->proto->name); + return 0; + } - 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. */ + return 1; +} - stats->imp_updates_ignored++; - rte_trace_in(D_FILTERS, c, new, "ignored [limit]"); +int +rte_same(const rte *x, const rte *y) +{ + /* rte.flags / rte.pflags are not checked, as they are internal to rtable */ + return - ( ++ (x == y) || ( + (x->attrs == y->attrs) || + ((!(x->attrs->flags & EALF_CACHED) || !(y->attrs->flags & EALF_CACHED)) && ea_same(x->attrs, y->attrs)) + ) && + x->src == y->src && + rte_is_filtered(x) == rte_is_filtered(y); +} - if (c->in_keep_filtered) - new->flags |= REF_FILTERED; - else - { rte_free_quick(new); new = NULL; } +static inline int rte_is_ok(const rte *e) { return e && !rte_is_filtered(e); } - /* Note that old && !new could be possible when - c->in_keep_filtered changed in the recent past. */ +static int +rte_recalculate(struct rtable_private *table, struct rt_import_hook *c, net *net, rte *new, struct rte_src *src) +{ + struct rt_import_request *req = c->req; + struct rt_import_stats *stats = &c->stats; + struct rte_storage *old_best_stored = net->routes, *old_stored = NULL; + const rte *old_best = old_best_stored ? &old_best_stored->rte : NULL; + const rte *old = NULL; + + /* If the new route is identical to the old one, we find the attributes in + * cache and clone these with no performance drop. OTOH, if we were to lookup + * the attributes, such a route definitely hasn't been anywhere yet, + * therefore it's definitely worth the time. */ + struct rte_storage *new_stored = NULL; + if (new) + { + new_stored = rte_store(new, net, table); + new = RTES_WRITE(new_stored); + } - if (!old && !new) - return; + /* Find and remove original route from the same protocol */ + struct rte_storage **before_old = rte_find(net, src); - new_ok = 0; - goto skip_stats1; + if (*before_old) + { + old = &(old_stored = (*before_old))->rte; + + /* If there is the same route in the routing table but from + * a different sender, then there are two paths from the + * source protocol to this routing table through transparent + * pipes, which is not allowed. + * We log that and ignore the route. */ + 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", + c->table->name, net->n.addr, old->src->owner->name, old->src->private_id, old->src->global_id); + + 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->owner->name, old->src->private_id, old->src->global_id); } + + if (new && rte_same(old, &new_stored->rte)) + { + /* No changes, ignore the new route and refresh the old one */ + old_stored->stale_cycle = new->stale_cycle; + + if (!rte_is_filtered(new)) + { + stats->updates_ignored++; + rt_rte_trace_in(D_ROUTES, req, new, "ignored"); + } + + /* We need to free the already stored route here before returning */ + rte_free(new_stored); + return 0; + } + + *before_old = (*before_old)->next; + table->rt_count--; } + if (!old && !new) + { + stats->withdraws_ignored++; + return 0; + } + + /* If rejected by import limit, we need to pretend there is no route */ + if (req->preimport && (req->preimport(req, new, old) == 0)) + { + rte_free(new_stored); + new_stored = NULL; + new = NULL; + } + + int new_ok = rte_is_ok(new); + int old_ok = rte_is_ok(old); + if (new_ok) - stats->imp_updates_accepted++; + stats->updates_accepted++; else if (old_ok) - stats->imp_withdraws_accepted++; + stats->withdraws_accepted++; else - stats->imp_withdraws_ignored++; + stats->withdraws_ignored++; if (old_ok || new_ok) table->last_rt_change = current_time();