]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Merge branch 'mq-aggregator-for-v3' into HEAD
authorMaria Matejka <mq@ucw.cz>
Wed, 1 Nov 2023 09:58:44 +0000 (10:58 +0100)
committerMaria Matejka <mq@ucw.cz>
Wed, 1 Nov 2023 09:58:44 +0000 (10:58 +0100)
1  2 
nest/rt-table.c
proto/aggregator/config.Y

diff --cc nest/rt-table.c
index 3e5def6860cd48a77b53a8d56372910e25bac73a,a8f6226dafa0d437176667c85603ab7786be4a90..6ff4d336e8c23f23f420f685eee6e628690c30b9
@@@ -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; i<RT_EXPORT_BULK; i++)
 +  {
 +    used += rte_export(c, c->rpe_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();
Simple merge