]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
ASPA: checks done in filters; no autoreload yet
authorMaria Matejka <mq@ucw.cz>
Sun, 15 Oct 2023 21:52:46 +0000 (23:52 +0200)
committerMaria Matejka <mq@ucw.cz>
Mon, 25 Mar 2024 13:15:30 +0000 (14:15 +0100)
filter/config.Y
filter/data.c
filter/data.h
filter/f-inst.c
nest/config.Y
nest/route.h
nest/rt-table.c

index 09d4fd8909326b1c87253ac46a8fd41c13dbd45a..5c3a1c167a6d78bf2a6be7d39c47675facbb0ec0 100644 (file)
@@ -364,7 +364,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
        TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
        FROM, GW, NET, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS, GW_MPLS_STACK, ONLINK,
        PREFERENCE,
-       ROA_CHECK,
+       ROA_CHECK, ASPA_CHECK,
        DEFINED,
        ADD, DELETE, RESET,
        PREPEND,
@@ -946,6 +946,7 @@ term:
 
  | 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); }
+ | ASPA_CHECK '(' rtable ',' term ')' { $$ = f_new_inst(FI_ASPA_CHECK_EXPLICIT, $5, $3); }
 
  | FORMAT '(' term ')' {  $$ = f_new_inst(FI_FORMAT, $3); }
 
index e268a8ec30aaf274468b05db4fe625b3e5142bcb..282206eb6e8a39d268de1539ad3a3174b8443679 100644 (file)
@@ -40,6 +40,7 @@ static const char * const f_type_str[] = {
   [T_ENUM_RTC] = "enum rtc",
   [T_ENUM_RTD] = "enum rtd",
   [T_ENUM_ROA] = "enum roa",
+  [T_ENUM_ASPA] = "enum aspa",
   [T_ENUM_NETTYPE] = "enum nettype",
   [T_ENUM_RA_PREFERENCE] = "enum ra_preference",
   [T_ENUM_AF]  = "enum af",
index df8d6a8f7cd75205d3e48dd036145ac932b81fe2..bd1fa9a008487116f65de7920507efd0cdb92de3 100644 (file)
@@ -43,6 +43,7 @@ enum f_type {
   T_ENUM_RA_PREFERENCE = 0x37,
   T_ENUM_AF = 0x38,
   T_ENUM_MPLS_POLICY = 0x39,
+  T_ENUM_ASPA = 0x3a,
 
 /* new enums go here */
   T_ENUM_EMPTY = 0x3f, /* Special hack for atomic_aggr */
index 6593a3812e66d0fb8e745eb81f2b58b2284c3a6e..bebd13c539bee761a5746ba3af6b96758653a8b4 100644 (file)
 
   }
 
+  INST(FI_ASPA_CHECK_EXPLICIT, 1, 1) { /* ASPA Check */
+    NEVER_CONSTANT;
+    ARG(1, T_PATH);
+    RTC(2);
+    struct rtable *table = rtc->table;
+
+    if (!table)
+      runtime("Missing ASPA table");
+
+    if (table->addr_type != NET_ASPA)
+      runtime("Table type must be ASPA");
+
+    RESULT(T_ENUM_ASPA, i, [[ aspa_check(table, v1.val.ad) ]]);
+  }
+
   INST(FI_FROM_HEX, 1, 1) {    /* Convert hex text to bytestring */
     ARG(1, T_STRING);
 
index 5d2f8d990361df7728183a65ed5f1132440f8962..594ab3fca4a318985fbfda8734d03c472fc2d702 100644 (file)
@@ -139,6 +139,7 @@ CF_ENUM(T_ENUM_RTS, RTS_, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
 CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
 CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
 CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
+CF_ENUM(T_ENUM_ASPA, ASPA_, UNKNOWN, VALID, INVALID)
 CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
 CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY_, NONE, STATIC, PREFIX, AGGREGATE, VRF)
 
index 9140b9e16bb2c1c36b3be2e90711e392bf27e976..a17ae696824fb75d160629c0cbd6d4d41753d20b 100644 (file)
@@ -15,6 +15,7 @@
 #include "lib/net.h"
 
 struct ea_list;
+struct adata;
 struct protocol;
 struct proto;
 struct rte_src;
@@ -320,6 +321,7 @@ static inline net *net_get(rtable *tab, const net_addr *addr) { return (net *) f
 net *net_get(rtable *tab, const net_addr *addr);
 net *net_route(rtable *tab, const net_addr *n);
 int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
+int aspa_check(rtable *tab, const struct adata *path);
 rte *rte_find(net *net, struct rte_src *src);
 rte *rte_get_temp(struct rta *, struct rte_src *src);
 void rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
@@ -781,4 +783,9 @@ int rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta *
 #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
+
 #endif
index 1b30e7dc794e84a4ea994e66ad94d774d10952cb..2b930914eea456a2c2d2082d32f856e5f6f9d4e2 100644 (file)
@@ -346,6 +346,95 @@ net_roa_check(rtable *tab, const net_addr *n, u32 asn)
 #undef FW
 }
 
+/**
+ * aspa_check - check validity of AS Path in an ASPA table
+ * @tab: ASPA table
+ * @path: AS Path to check
+ *
+ * Implements draft-ietf-sidrops-aspa-verification-16.
+ */
+int aspa_check(rtable *tab, const adata *path)
+{
+  struct lp_state lps;
+  lp_save(tmp_linpool, &lps);
+
+  /* No support for confed paths */
+  if (as_path_contains_confed(path))
+    return ASPA_CONTAINS_CONFED;
+
+  /* Normalize the AS Path: drop stuffings */
+  uint len = as_path_getlen(path);
+  u32 *asns = alloca(sizeof(u32) * len);
+  uint ppos = 0;
+  int nsz = 0;
+  while (as_path_walk(path, &ppos, &asns[nsz]))
+    if ((nsz == 0) || (asns[nsz] != asns[nsz-1]))
+      nsz++;
+
+  /* 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;
+
+  for (int ap=0; ap<nsz; ap++)
+  {
+    net_addr_union nau = { .aspa = NET_ADDR_ASPA(asns[ap]), };
+    net *n = net_find(tab, &nau.n);
+    if (!n || !n->routes)
+    {
+      /* No ASPA for this ASN, therefore UNKNOWN */
+      unknown_flag = up[ap] = down[ap] = true;
+      continue;
+    }
+
+    up[ap] = down[ap] = false;
+
+    for (rte *e = n->routes; e; e = e->next)
+    {
+      if (!rte_is_valid(e))
+       continue;
+
+      eattr *ea = ea_find(e->attrs->eattrs, EA_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;
+       if ((ap + 1 < nsz) && ((u32 *) ea->u.ptr->data)[i] == asns[ap+1])
+         up[ap] = true;
+
+       if (down[ap] || up[ap])
+         goto peering_found;
+      }
+    }
+peering_found:;
+  }
+
+  /* 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++;
+
+  int down_end = nsz - 1;
+  while (down_end > 0 && down[down_end])
+    down_end--;
+
+  /* A significant overlap of obvious unknowns or misconfigured ASPAs. */
+  if (up_end - down_end >= 2)
+    return ASPA_UNKNOWN;
+
+  /* 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;
+
+  /* There is a gap between valid ramp up and valid ramp down */
+  else
+    return ASPA_INVALID;
+}
+
 /**
  * rte_find - find a route
  * @net: network node