0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 09:41:54 +00:00

Merge commit 'bc10975a' into thread-merge-2.16

This commit is contained in:
Maria Matejka 2024-11-28 08:56:27 +01:00
commit b8f7200ee9
7 changed files with 131 additions and 1 deletions

View File

@ -368,7 +368,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
FOR, IN, DO,
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
FROM, GW, NET, PROTO, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
ROA_CHECK,
ROA_CHECK, ASPA_CHECK,
DEFINED,
ADD, DELETE, RESET,
PREPEND,
@ -954,6 +954,7 @@ term:
| 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); }
| FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }

View File

@ -43,6 +43,7 @@ static const char * const f_type_str[] = {
[T_ENUM_SCOPE] = "enum scope",
[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",

View File

@ -1522,6 +1522,21 @@
}
INST(FI_ASPA_CHECK_EXPLICIT, 1, 1) { /* ASPA Check */
NEVER_CONSTANT;
ARG(1, T_PATH);
RTC(2);
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);

View File

@ -91,6 +91,7 @@ enum btype {
T_ENUM_LO = 0x12,
T_ENUM_HI = 0x3f,
T_ENUM_ASPA = 0x2f, /* ASPA validation result */
T_ENUM_RTS = 0x31,
T_ENUM_SCOPE = 0x33,
T_ENUM_MPLS_POLICY = 0x35,

View File

@ -177,6 +177,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)

View File

@ -33,6 +33,7 @@
#include <stdatomic.h>
struct ea_list;
struct adata;
struct protocol;
struct proto;
struct channel;
@ -928,6 +929,12 @@ void ea_show_nexthop_list(struct cli *c, struct nexthop_adata *nhad);
#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
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
int aspa_check(rtable *tab, const struct adata *path);
#endif

View File

@ -717,6 +717,110 @@ net_roa_check(rtable *tp, const net_addr *n, u32 asn)
#undef TW
}
/**
* 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)
{
/* 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))
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;
RT_READ(tab, tr);
for (int ap=0; ap<nsz; ap++)
{
net_addr_union nau = { .aspa = NET_ADDR_ASPA(asns[ap]), };
bool seen = false;
/* 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;
/* Walk the existing records */
NET_READ_WALK_ROUTES(tr, n, ep, e)
{
if (!rte_is_valid(&e->rte))
continue;
eattr *ea = ea_find(e->rte.attrs, &ea_gen_aspa_providers);
if (!ea)
continue;
seen = true;
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])
break;
}
if (down[ap] && up[ap])
break;
}
/* No ASPA for this ASN, therefore UNKNOWN */
if (!seen)
unknown_flag = up[ap] = down[ap] = true;
}
/* 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;
}
struct rte_storage *
rte_store(const rte *r, struct netindex *i, struct rtable_private *tab)
{