]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Merge commit '38195ac6' into thread-merge-2.16
authorMaria Matejka <mq@ucw.cz>
Fri, 29 Nov 2024 09:49:53 +0000 (10:49 +0100)
committerMaria Matejka <mq@ucw.cz>
Fri, 29 Nov 2024 09:49:53 +0000 (10:49 +0100)
1  2 
filter/config.Y
filter/f-inst.c
filter/test.conf
lib/net.h
nest/config.Y
nest/route.h
nest/rt-table.c
proto/bgp/config.Y

diff --cc filter/config.Y
index 6bc4217409a8bb45046c947f0bf2a76e6795eef9,72996850da10eb67a303ef2881334448a7cf7992..144bbdef662f19faabc1a9f7304383089a083e4d
@@@ -963,9 -949,9 +963,9 @@@ term
   | DELETE '(' term ',' term ')' { $$ = f_dispatch_method_x("delete", $3->type, $3, $5); }
   | FILTER '(' term ',' term ')' { $$ = f_dispatch_method_x("filter", $3->type, $3, $5); }
  
 - | ROA_CHECK '(' rtable ')' { $$ = f_new_inst(FI_ROA_CHECK_IMPLICIT, $3); }
 - | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK_EXPLICIT, $5, $7, $3); }
 + | ROA_CHECK '(' rtable ')' { $$ = f_implicit_roa_check($3); }
 + | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK, $5, $7, $3); }
-  | ASPA_CHECK '(' rtable ',' term ')' { $$ = f_new_inst(FI_ASPA_CHECK_EXPLICIT, $5, $3); }
+  | ASPA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ASPA_CHECK_EXPLICIT, $5, $7, $3); }
  
   | FORMAT '(' term ')' {  $$ = f_new_inst(FI_FORMAT, $3); }
  
diff --cc filter/f-inst.c
index 5bf96a87b2d775765ab18538033227f521101d0c,8529ad621f9ea6467a5966e6d1d5e3a100c9854d..b1b0c31683b8c30818e4d3bb6d7d1a52d7286ab5
  
    }
  
-   INST(FI_ASPA_CHECK_EXPLICIT, 1, 1) {        /* ASPA Check */
+   INST(FI_ASPA_CHECK_EXPLICIT, 2, 1) {        /* ASPA Check */
      NEVER_CONSTANT;
      ARG(1, T_PATH);
-     RTC(2);
+     ARG(2, T_BOOL);
+     RTC(3);
 -    struct rtable *table = rtc->table;
 +    rtable *table = rtc->table;
  
      if (!table)
        runtime("Missing ASPA table");
Simple merge
diff --cc lib/net.h
Simple merge
diff --cc nest/config.Y
Simple merge
diff --cc nest/route.h
index 648e46b463207859d5d790ec211535396e69aaf4,c74c410f10fce51550192210c6009bfd5d39d920..c6446446161f2d7ae594f7998340d45ca3e3fd22
@@@ -929,12 -783,12 +929,15 @@@ void ea_show_nexthop_list(struct cli *c
  #define ROA_VALID     1
  #define ROA_INVALID   2
  
- #define ASPA_UNKNOWN  0
- #define ASPA_VALID    1
- #define ASPA_INVALID  2
- #define ASPA_CONTAINS_CONFED  3
+ enum aspa_result {
+   ASPA_UNKNOWN = 0,
+   ASPA_VALID,
+   ASPA_INVALID_EMPTY,
+   ASPA_INVALID_CONFED,
+   ASPA_INVALID_LEAK,
+ };
  
- int aspa_check(rtable *tab, const struct adata *path);
 +int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
++enum aspa_result aspa_check(rtable *tab, const struct adata *path, bool force_upstream);
 +
  #endif
diff --cc nest/rt-table.c
index ac5e364ed6299dbce1b6216c46f7b6753c860bd1,8cee48d6933962d5c9d90b99b2896aa0d5e9a924..d74dfad91f669cc970e128eb40d5c98de5e1a42b
@@@ -724,10 -353,10 +724,10 @@@ net_roa_check(rtable *tp, const net_add
   *
   * Implements draft-ietf-sidrops-aspa-verification-16.
   */
int aspa_check(rtable *tab, const adata *path)
enum aspa_result aspa_check(rtable *tab, const adata *path, bool force_upstream)
  {
 -  struct lp_state lps;
 -  lp_save(tmp_linpool, &lps);
 +  /* Restore tmp linpool state after this check */
 +  CLEANUP(lp_saved_cleanup) struct lp_state *_lps = lp_save(tmp_linpool);
  
    /* No support for confed paths */
    if (as_path_contains_confed(path))
  
    /* Find the provider blocks for every AS on the path
     * and check allowed directions */
-   bool *up = alloca(sizeof(bool) * nsz);
-   bool *down = alloca(sizeof(bool) * nsz);
-   bool unknown_flag = false;
+   uint max_up = 0, min_up = 0, max_down = 0, min_down = 0;
  
-   for (int ap=0; ap<nsz; ap++)
 +  RT_READ(tab, tr);
 +
+   for (uint ap=0; ap<nsz; ap++)
    {
      net_addr_union nau = { .aspa = NET_ADDR_ASPA(asns[ap]), };
-     bool seen = false;
 -    net *n = net_find(tab, &nau.n);
 +
 +    /* Find some ASPAs */
 +    struct netindex *ni = net_find_index(tr->t->netindex, &nau.n);
 +    net *n = ni ? net_find(tr, ni) : NULL;
  
-     if (!n)
-     {
-       unknown_flag = up[ap] = down[ap] = true;
-       continue;
-     }
-     up[ap] = down[ap] = false;
+     bool found = false, down = false, up = false;
  
-     /* Walk the existing records */
-     NET_READ_WALK_ROUTES(tr, n, ep, e)
 -    for (rte *e = (n ? n->routes: NULL); e; e = e->next)
++    if (n) NET_READ_WALK_ROUTES(tr, n, ep, e)
      {
 -      if (!rte_is_valid(e))
 +      if (!rte_is_valid(&e->rte))
        continue;
  
 -      eattr *ea = ea_find(e->attrs->eattrs, EA_ASPA_PROVIDERS);
 +      eattr *ea = ea_find(e->rte.attrs, &ea_gen_aspa_providers);
        if (!ea)
        continue;
  
        for (uint i=0; i * sizeof(u32) < ea->u.ptr->length; i++)
        {
        if ((ap > 0) && ((u32 *) ea->u.ptr->data)[i] == asns[ap-1])
-         down[ap] = true;
+         up = true;
        if ((ap + 1 < nsz) && ((u32 *) ea->u.ptr->data)[i] == asns[ap+1])
-         up[ap] = true;
+         down = true;
  
-       if (down[ap] && up[ap])
-         break;
+       if (down && up)
+         /* Both peers found */
+         goto end_of_aspa;
        }
+     }
+ end_of_aspa:;
  
-       if (down[ap] && up[ap])
-       break;
+     /* Fast path for the upstream check */
+     if (force_upstream)
+     {
+       if (!found)
+       /* Move min-upstream */
+       min_up = ap;
+       else if (ap && !up)
+       /* Exists but doesn't allow this upstream */
+       return ASPA_INVALID_LEAK;
      }
  
-     /* No ASPA for this ASN, therefore UNKNOWN */
-     if (!seen)
-       unknown_flag = up[ap] = down[ap] = true;
-   }
+     /* Fast path for no ASPA here */
+     else if (!found)
+     {
+       /* Extend max-downstream (min-downstream is stopped by unknown) */
+       max_down = ap+1;
  
-   /* Check whether the topology is first ramp up and then ramp down. */
-   int up_end = 0;
-   while (up_end < nsz && up[up_end])
-     up_end++;
+       /* Move min-upstream (can't include unknown) */
+       min_up = ap;
+     }
  
-   int down_end = nsz - 1;
-   while (down_end > 0 && down[down_end])
-     down_end--;
+     /* ASPA exists and downstream may be extended */
+     else if (down)
+     {
+       /* Extending max-downstream always */
+       max_down = ap+1;
  
-   /* A significant overlap of obvious unknowns or misconfigured ASPAs. */
-   if (up_end - down_end >= 2)
-     return ASPA_UNKNOWN;
+       /* Extending min-downstream unless unknown seen */
+       if (min_down == ap)
+       min_down = ap+1;
  
-   /* The path has either a single transit provider, or a peering pair on top */
-   else if (up_end - down_end >= 0)
-     return unknown_flag ? ASPA_UNKNOWN : ASPA_VALID;
+       /* Downstream only */
+       if (!up)
+       min_up = max_up = ap;
+     }
  
-   /* There is a gap between valid ramp up and valid ramp down */
-   else
-     return ASPA_INVALID;
+     /* No extension for downstream, force upstream only from now */
+     else
+     {
+       force_upstream = 1;
+       /* Not even upstream, move the ending here */
+       if (!up)
+       min_up = max_up = ap;
+     }
+   }
+   /* Is the path surely valid? */
+   if (min_up <= min_down)
+     return ASPA_VALID;
+   /* Is the path maybe valid? */
+   if (max_up <= max_down)
+     return ASPA_UNKNOWN;
+   /* Now there is surely a valley there. */
+   return ASPA_INVALID_LEAK;
  }
  
 -/**
 - * rte_find - find a route
 - * @net: network node
 - * @src: route source
 - *
 - * The rte_find() function returns a route for destination @net
 - * which is from route source @src.
 - */
 -rte *
 -rte_find(net *net, struct rte_src *src)
 +struct rte_storage *
 +rte_store(const rte *r, struct netindex *i, struct rtable_private *tab)
  {
 -  rte *e = net->routes;
 +  struct rte_storage *s = sl_alloc(tab->rte_slab);
 +  struct rte *e = RTES_WRITE(s);
 +
 +  *e = *r;
 +  e->net = i->addr;
 +  net_lock_index(tab->netindex, i);
 +
 +  rt_lock_source(e->src);
 +
 +  e->attrs = ea_lookup(e->attrs, BIT32_ALL(EALS_PREIMPORT, EALS_FILTERED), EALS_IN_TABLE);
 +
 +#if 0
 +  debug("(store) %N ", i->addr);
 +  ea_dump(e->attrs);
 +  debug("\n");
 +#endif
  
 -  while (e && e->src != src)
 -    e = e->next;
 -  return e;
 +  return s;
  }
  
 +static void rte_free_deferred(struct deferred_call *dc);
 +
 +struct rte_free_deferred_item {
 +  struct deferred_call dc;
 +  struct rte_storage *e;
 +  rtable *tab;
 +};
 +
  /**
 - * rte_get_temp - get a temporary &rte
 - * @a: attributes to assign to the new route (a &rta; in case it's
 - * un-cached, rte_update() will create a cached copy automatically)
 - * @src: route source
 + * rte_free_defer - delete a &rte (happens later)
 + * @e: &struct rte_storage to be deleted
 + * @tab: the table which the rte belongs to
   *
 - * Create a temporary &rte and bind it with the attributes @a.
 + * rte_free() deletes the given &rte from the routing table it's linked to.
   */
 -rte *
 -rte_get_temp(rta *a, struct rte_src *src)
 +
 +static void
 +rte_free(struct rte_storage *e, struct rtable_private *tab)
  {
 -  rte *e = sl_alloc(rte_slab);
 +  struct rte_free_deferred_item rfdi = {
 +    .dc.hook = rte_free_deferred,
 +    .e = e,
 +    .tab = RT_PUB(tab),
 +  };
  
 -  e->attrs = a;
 -  e->id = 0;
 -  e->flags = 0;
 -  e->pflags = 0;
 -  rt_lock_source(e->src = src);
 -  return e;
 +  if (!tab->rte_free_deferred++)
 +    rt_lock_table(tab);
 +
 +  rt_rte_trace_in(D_ROUTES, e->rte.sender->req, &e->rte, "freeing");
 +  defer_call(&rfdi.dc, sizeof rfdi);
  }
  
 -rte *
 -rte_do_cow(rte *r)
 +static void
 +rte_free_deferred(struct deferred_call *dc)
  {
 -  rte *e = sl_alloc(rte_slab);
 +  SKIP_BACK_DECLARE(struct rte_free_deferred_item, rfdi, dc, dc);
  
 -  memcpy(e, r, sizeof(rte));
 +  struct rte_storage *e = rfdi->e;
 +  RT_LOCK(rfdi->tab, tab);
  
 -  rt_lock_source(e->src);
 -  e->attrs = rta_clone(r->attrs);
 -  e->flags = 0;
 -  return e;
 -}
 +  /* No need for synchronize_rcu, implied by the deferred_call */
  
 -/**
 - * rte_cow_rta - get a private writable copy of &rte with writable &rta
 - * @r: a route entry to be copied
 - * @lp: a linpool from which to allocate &rta
 - *
 - * rte_cow_rta() takes a &rte and prepares it and associated &rta for
 - * modification. There are three possibilities: First, both &rte and &rta are
 - * private copies, in that case they are returned unchanged.  Second, &rte is
 - * private copy, but &rta is cached, in that case &rta is duplicated using
 - * rta_do_cow(). Third, both &rte is shared and &rta is cached, in that case
 - * both structures are duplicated by rte_do_cow() and rta_do_cow().
 - *
 - * Note that in the second case, cached &rta loses one reference, while private
 - * copy created by rta_do_cow() is a shallow copy sharing indirect data (eattrs,
 - * nexthops, ...) with it. To work properly, original shared &rta should have
 - * another reference during the life of created private copy.
 - *
 - * Result: a pointer to the new writable &rte with writable &rta.
 - */
 -rte *
 -rte_cow_rta(rte *r, linpool *lp)
 -{
 -  if (!rta_is_cached(r->attrs))
 -    return r;
 +  struct netindex *i = RTE_GET_NETINDEX(&e->rte);
 +  net_unlock_index(tab->netindex, i);
  
 -  r = rte_cow(r);
 -  rta *a = rta_do_cow(r->attrs, lp);
 -  rta_free(r->attrs);
 -  r->attrs = a;
 -  return r;
 +  rt_unlock_source(e->rte.src);
 +
 +  ea_free(e->rte.attrs);
 +  sl_free(e);
 +
 +  if (!--tab->rte_free_deferred)
 +    rt_unlock_table(tab);
  }
  
  static int                            /* Actually better or at least as good as */
index 85cbd3848e52ce7043124175e001be05ce8eb193,7ae6cde97ff20a6b189f3c8a100afcaa341c98c8..ab6b899c648b49c7c9b3c6744532659a544c3c75
@@@ -354,35 -347,77 +354,58 @@@ bgp_channel_end
  
  bgp_proto_channel: bgp_channel_start bgp_channel_opt_list bgp_channel_end;
  
 -
 -dynamic_attr: BGP_ORIGIN
 -      { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_ENUM_BGP_ORIGIN, EA_CODE(PROTOCOL_BGP, BA_ORIGIN));   $$.flags = BAF_TRANSITIVE; } ;
 -dynamic_attr: BGP_PATH
 -      { $$ = f_new_dynamic_attr(EAF_TYPE_AS_PATH, T_PATH, EA_CODE(PROTOCOL_BGP, BA_AS_PATH));         $$.flags = BAF_TRANSITIVE; } ;
 -dynamic_attr: BGP_NEXT_HOP
 -      { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_CODE(PROTOCOL_BGP, BA_NEXT_HOP));       $$.flags = BAF_TRANSITIVE; } ;
 -dynamic_attr: BGP_MED
 -      { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC));      $$.flags = BAF_OPTIONAL; } ;
 -dynamic_attr: BGP_LOCAL_PREF
 -      { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF));           $$.flags = BAF_TRANSITIVE; } ;
 -dynamic_attr: BGP_ATOMIC_AGGR
 -      { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_ATOMIC_AGGR)); $$.flags = BAF_TRANSITIVE; } ;
 -dynamic_attr: BGP_AGGREGATOR
 -      { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AGGREGATOR)); $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ;
 -dynamic_attr: BGP_COMMUNITY
 -      { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));      $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ;
 -dynamic_attr: BGP_ORIGINATOR_ID
 -      { $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID, T_QUAD, EA_CODE(PROTOCOL_BGP, BA_ORIGINATOR_ID)); $$.flags = BAF_OPTIONAL; } ;
 -dynamic_attr: BGP_CLUSTER_LIST
 -      { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_CLUSTER_LIST));   $$.flags = BAF_OPTIONAL; } ;
 -dynamic_attr: BGP_EXT_COMMUNITY
 -      { $$ = f_new_dynamic_attr(EAF_TYPE_EC_SET, T_ECLIST, EA_CODE(PROTOCOL_BGP, BA_EXT_COMMUNITY));  $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ;
 -dynamic_attr: BGP_AIGP
 -      { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AIGP));       $$.flags = BAF_OPTIONAL; } ;
 -dynamic_attr: BGP_LARGE_COMMUNITY
 -      { $$ = f_new_dynamic_attr(EAF_TYPE_LC_SET, T_LCLIST, EA_CODE(PROTOCOL_BGP, BA_LARGE_COMMUNITY)); $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ;
 -dynamic_attr: BGP_OTC
 -      { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_ONLY_TO_CUSTOMER));     $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ;
 -
 -custom_attr: ATTRIBUTE BGP expr type symbol ';' {
 +custom_attr: ATTRIBUTE BGP NUM type symbol ';' {
    if ($3 > 255 || $3 < 1)
 -    cf_error("Invalid attribute number (Given %i, must be 1-255)", $3);
 -  if ($4 != T_BYTESTRING)
 -    cf_error("Attribute type must be bytestring, not %s", f_type_name($4));
 -  if (bgp_attr_name($3))
 -    cf_error("Attribute BGP.%d already known as %s", $3, bgp_attr_name($3));
 -
 -  struct f_dynamic_attr *a = cfg_alloc(sizeof(struct f_dynamic_attr));
 -  *a = f_new_dynamic_attr(f_type_attr($4), T_BYTESTRING, EA_CODE(PROTOCOL_BGP, $3));
 -  a->flags = BAF_TRANSITIVE | BAF_OPTIONAL;
 -  cf_define_symbol(new_config, $5, SYM_ATTRIBUTE, attribute, a);
 +    cf_error("Invalid attribute number. (Given %i, must be 1-255.)", $3);
 +
 +  struct ea_class *ac = bgp_find_ea_class_by_id($3);
 +  ASSERT_DIE(ac);
 +  if ($4 != ac->type)
 +    cf_error("Attribute %d type must be %s, not %s.", $3, f_type_name(ac->type), f_type_name($4));
 +
 +  ea_ref_class(new_config->pool, ac);
 +  cf_define_symbol(new_config, $5, SYM_ATTRIBUTE, attribute, ac);
  };
  
 -CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
 +CF_CLI(RELOAD BGP, proto_patt, [<name>], [[Send and request route refresh to/from neighbor]])
 +{
 +  proto_apply_cmd($3, bgp_reload_in, 1, 0);
 +  proto_apply_cmd($3, bgp_reload_out, 1, 0);
 +};
 +
 +CF_CLI(RELOAD BGP IN, proto_patt, [<name>], [[Request route refresh from neighbor]])
 +{
 +  proto_apply_cmd($4, bgp_reload_in, 1, 0);
 +}
 +
 +CF_CLI(RELOAD BGP OUT, proto_patt, [<name>], [[Refresh routes to neighbor]])
 +{
 +  proto_apply_cmd($4, bgp_reload_out, 1, 0);
 +}
  
 -      f_new_dynamic_attr(EAF_TYPE_AS_PATH, T_PATH,
 -        EA_CODE(PROTOCOL_BGP, BA_AS_PATH))
+ /* ASPA shortcuts */
+ term: ASPA_CHECK '(' rtable ')' { $$ =
+   f_new_inst(FI_ASPA_CHECK_EXPLICIT,
+       f_new_inst(FI_EA_GET,
+       f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL, }),
 -      f_new_dynamic_attr(EAF_TYPE_AS_PATH, T_PATH,
 -        EA_CODE(PROTOCOL_BGP, BA_AS_PATH))
 -      ),
++      ea_class_find_by_name("bgp_path")
+       ),
+       f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 0, }),
+       $3
+   );
+ }
+ term: ASPA_CHECK_CUSTOMER '(' rtable ')' { $$ =
+   f_new_inst(FI_ASPA_CHECK_EXPLICIT,
+       f_new_inst(FI_EA_GET,
+       f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL, }),
++      ea_class_find_by_name("bgp_path")
++      ),
+       f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 1, }),
+       $3
+   );
+ }
  CF_CODE
  
  CF_END