aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaria Matejka <mq@ucw.cz>2023-10-15 23:52:46 +0200
committerMaria Matejka <mq@ucw.cz>2024-03-25 14:15:30 +0100
commitbc10975adbb0aef772496f334dd0bbd23251c1d8 (patch)
tree3a5fa98e3b2dbccaa0b359440bee902295222bc0
parent08571b20598c58877e1565403d970efc2b90dba6 (diff)
downloadbird-bc10975adbb0aef772496f334dd0bbd23251c1d8.tar.gz
ASPA: checks done in filters; no autoreload yet
-rw-r--r--filter/config.Y3
-rw-r--r--filter/data.c1
-rw-r--r--filter/data.h1
-rw-r--r--filter/f-inst.c15
-rw-r--r--nest/config.Y1
-rw-r--r--nest/route.h7
-rw-r--r--nest/rt-table.c89
7 files changed, 116 insertions, 1 deletions
diff --git a/filter/config.Y b/filter/config.Y
index 09d4fd89..5c3a1c16 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -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); }
diff --git a/filter/data.c b/filter/data.c
index e268a8ec..282206eb 100644
--- a/filter/data.c
+++ b/filter/data.c
@@ -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",
diff --git a/filter/data.h b/filter/data.h
index df8d6a8f..bd1fa9a0 100644
--- a/filter/data.h
+++ b/filter/data.h
@@ -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 */
diff --git a/filter/f-inst.c b/filter/f-inst.c
index 6593a381..bebd13c5 100644
--- a/filter/f-inst.c
+++ b/filter/f-inst.c
@@ -1606,6 +1606,21 @@
}
+ 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);
diff --git a/nest/config.Y b/nest/config.Y
index 5d2f8d99..594ab3fc 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -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)
diff --git a/nest/route.h b/nest/route.h
index 9140b9e1..a17ae696 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -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
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 1b30e7dc..2b930914 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -347,6 +347,95 @@ net_roa_check(rtable *tab, const net_addr *n, u32 asn)
}
/**
+ * 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
* @src: route source