mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
Merge commit '38195ac6' into thread-merge-2.16
This commit is contained in:
commit
c1ad3e58c0
@ -965,7 +965,7 @@ term:
|
|||||||
|
|
||||||
| ROA_CHECK '(' rtable ')' { $$ = f_implicit_roa_check($3); }
|
| ROA_CHECK '(' rtable ')' { $$ = f_implicit_roa_check($3); }
|
||||||
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK, $5, $7, $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); }
|
| FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
|
||||||
|
|
||||||
|
@ -1522,10 +1522,11 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INST(FI_ASPA_CHECK_EXPLICIT, 1, 1) { /* ASPA Check */
|
INST(FI_ASPA_CHECK_EXPLICIT, 2, 1) { /* ASPA Check */
|
||||||
NEVER_CONSTANT;
|
NEVER_CONSTANT;
|
||||||
ARG(1, T_PATH);
|
ARG(1, T_PATH);
|
||||||
RTC(2);
|
ARG(2, T_BOOL);
|
||||||
|
RTC(3);
|
||||||
rtable *table = rtc->table;
|
rtable *table = rtc->table;
|
||||||
|
|
||||||
if (!table)
|
if (!table)
|
||||||
@ -1534,7 +1535,7 @@
|
|||||||
if (table->addr_type != NET_ASPA)
|
if (table->addr_type != NET_ASPA)
|
||||||
runtime("Table type must be ASPA");
|
runtime("Table type must be ASPA");
|
||||||
|
|
||||||
RESULT(T_ENUM_ASPA, i, [[ aspa_check(table, v1.val.ad) ]]);
|
RESULT(T_ENUM_ASPA, i, [[ aspa_check(table, v1.val.ad, v2.val.i) ]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
INST(FI_FROM_HEX, 1, 1) { /* Convert hex text to bytestring */
|
INST(FI_FROM_HEX, 1, 1) { /* Convert hex text to bytestring */
|
||||||
|
@ -2414,7 +2414,56 @@ prefix pfx;
|
|||||||
bt_test_suite(t_roa_check, "Testing ROA");
|
bt_test_suite(t_roa_check, "Testing ROA");
|
||||||
|
|
||||||
|
|
||||||
|
aspa table at;
|
||||||
|
|
||||||
|
protocol static
|
||||||
|
{
|
||||||
|
aspa { table at; };
|
||||||
|
route aspa 65540 provider 65544;
|
||||||
|
route aspa 65541 provider 65545;
|
||||||
|
route aspa 65542 provider 65544, 65545;
|
||||||
|
route aspa 65543 provider 65544, 65545;
|
||||||
|
route aspa 65544 transit;
|
||||||
|
route aspa 65545 transit;
|
||||||
|
route aspa 65550 provider 65540;
|
||||||
|
route aspa 65551 provider 65543;
|
||||||
|
}
|
||||||
|
|
||||||
|
function t_aspa_check()
|
||||||
|
{
|
||||||
|
bgppath p1 = +empty+;
|
||||||
|
p1.prepend(65540);
|
||||||
|
p1.prepend(65544);
|
||||||
|
bt_assert(aspa_check(at, p1, false) = ASPA_VALID);
|
||||||
|
bt_assert(aspa_check(at, p1, true) = ASPA_VALID);
|
||||||
|
|
||||||
|
p1.prepend(65542);
|
||||||
|
bt_assert(aspa_check(at, p1, false) = ASPA_VALID);
|
||||||
|
bt_assert(aspa_check(at, p1, true) = ASPA_INVALID_LEAK);
|
||||||
|
|
||||||
|
p1.prepend(65555);
|
||||||
|
bt_assert(aspa_check(at, p1, false) = ASPA_UNKNOWN);
|
||||||
|
bt_assert(aspa_check(at, p1, true) = ASPA_INVALID_LEAK);
|
||||||
|
|
||||||
|
bgppath p2 = +empty+;
|
||||||
|
p2.prepend(65554);
|
||||||
|
p2.prepend(65541);
|
||||||
|
p2.prepend(65545);
|
||||||
|
bt_assert(aspa_check(at, p2, false) = ASPA_UNKNOWN);
|
||||||
|
bt_assert(aspa_check(at, p2, true) = ASPA_UNKNOWN);
|
||||||
|
|
||||||
|
p2.prepend(65543);
|
||||||
|
bt_assert(aspa_check(at, p2, false) = ASPA_UNKNOWN);
|
||||||
|
bt_assert(aspa_check(at, p2, true) = ASPA_INVALID_LEAK);
|
||||||
|
|
||||||
|
bgppath p3 = +empty+;
|
||||||
|
p3.prepend(65541);
|
||||||
|
p3.prepend(65544);
|
||||||
|
bt_assert(aspa_check(at, p3, false) = ASPA_INVALID_LEAK);
|
||||||
|
bt_assert(aspa_check(at, p3, true) = ASPA_INVALID_LEAK);
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_test_suite(t_aspa_check, "Testing ASPA");
|
||||||
|
|
||||||
filter vpn_filter
|
filter vpn_filter
|
||||||
{
|
{
|
||||||
|
@ -214,9 +214,6 @@ extern const u16 net_max_text_length[];
|
|||||||
#define NET_ADDR_ASPA(asn) \
|
#define NET_ADDR_ASPA(asn) \
|
||||||
((net_addr_aspa) { NET_ASPA, 32, sizeof(net_addr_aspa), asn })
|
((net_addr_aspa) { NET_ASPA, 32, sizeof(net_addr_aspa), asn })
|
||||||
|
|
||||||
#define NET_ADDR_ASPA_EXISTS(asn) NET_ADDR_ASPA(asn, asn)
|
|
||||||
#define NET_ADDR_ASPA_TRANSIT(asn) NET_ADDR_ASPA(asn, 0)
|
|
||||||
|
|
||||||
#define NET_ADDR_MPLS(label) \
|
#define NET_ADDR_MPLS(label) \
|
||||||
((net_addr_mpls) { NET_MPLS, 20, sizeof(net_addr_mpls), label })
|
((net_addr_mpls) { NET_MPLS, 20, sizeof(net_addr_mpls), label })
|
||||||
|
|
||||||
|
@ -177,7 +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_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
|
||||||
CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
|
CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
|
||||||
CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
|
CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
|
||||||
CF_ENUM(T_ENUM_ASPA, ASPA_, UNKNOWN, VALID, INVALID)
|
CF_ENUM(T_ENUM_ASPA, ASPA_, UNKNOWN, VALID, INVALID_EMPTY, INVALID_LEAK, INVALID_CONFED)
|
||||||
CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
|
CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
|
||||||
CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY_, NONE, STATIC, PREFIX, AGGREGATE, VRF)
|
CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY_, NONE, STATIC, PREFIX, AGGREGATE, VRF)
|
||||||
|
|
||||||
|
13
nest/route.h
13
nest/route.h
@ -929,12 +929,15 @@ void ea_show_nexthop_list(struct cli *c, struct nexthop_adata *nhad);
|
|||||||
#define ROA_VALID 1
|
#define ROA_VALID 1
|
||||||
#define ROA_INVALID 2
|
#define ROA_INVALID 2
|
||||||
|
|
||||||
#define ASPA_UNKNOWN 0
|
enum aspa_result {
|
||||||
#define ASPA_VALID 1
|
ASPA_UNKNOWN = 0,
|
||||||
#define ASPA_INVALID 2
|
ASPA_VALID,
|
||||||
#define ASPA_CONTAINS_CONFED 3
|
ASPA_INVALID_EMPTY,
|
||||||
|
ASPA_INVALID_CONFED,
|
||||||
|
ASPA_INVALID_LEAK,
|
||||||
|
};
|
||||||
|
|
||||||
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
|
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
|
||||||
int aspa_check(rtable *tab, const struct adata *path);
|
enum aspa_result aspa_check(rtable *tab, const struct adata *path, bool force_upstream);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
116
nest/rt-table.c
116
nest/rt-table.c
@ -724,51 +724,45 @@ net_roa_check(rtable *tp, const net_addr *n, u32 asn)
|
|||||||
*
|
*
|
||||||
* Implements draft-ietf-sidrops-aspa-verification-16.
|
* 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)
|
||||||
{
|
{
|
||||||
/* Restore tmp linpool state after this check */
|
/* Restore tmp linpool state after this check */
|
||||||
CLEANUP(lp_saved_cleanup) struct lp_state *_lps = lp_save(tmp_linpool);
|
CLEANUP(lp_saved_cleanup) struct lp_state *_lps = lp_save(tmp_linpool);
|
||||||
|
|
||||||
/* No support for confed paths */
|
/* No support for confed paths */
|
||||||
if (as_path_contains_confed(path))
|
if (as_path_contains_confed(path))
|
||||||
return ASPA_CONTAINS_CONFED;
|
return ASPA_INVALID_CONFED;
|
||||||
|
|
||||||
|
/* Check path length */
|
||||||
|
uint len = as_path_getlen(path);
|
||||||
|
if (len == 0)
|
||||||
|
return ASPA_INVALID_EMPTY;
|
||||||
|
|
||||||
/* Normalize the AS Path: drop stuffings */
|
/* Normalize the AS Path: drop stuffings */
|
||||||
uint len = as_path_getlen(path);
|
|
||||||
u32 *asns = alloca(sizeof(u32) * len);
|
u32 *asns = alloca(sizeof(u32) * len);
|
||||||
uint ppos = 0;
|
uint ppos = 0;
|
||||||
int nsz = 0;
|
uint nsz = 0;
|
||||||
while (as_path_walk(path, &ppos, &asns[nsz]))
|
while (as_path_walk(path, &ppos, &asns[nsz]))
|
||||||
if ((nsz == 0) || (asns[nsz] != asns[nsz-1]))
|
if ((nsz == 0) || (asns[nsz] != asns[nsz-1]))
|
||||||
nsz++;
|
nsz++;
|
||||||
|
|
||||||
/* Find the provider blocks for every AS on the path
|
/* Find the provider blocks for every AS on the path
|
||||||
* and check allowed directions */
|
* and check allowed directions */
|
||||||
bool *up = alloca(sizeof(bool) * nsz);
|
uint max_up = 0, min_up = 0, max_down = 0, min_down = 0;
|
||||||
bool *down = alloca(sizeof(bool) * nsz);
|
|
||||||
bool unknown_flag = false;
|
|
||||||
|
|
||||||
RT_READ(tab, tr);
|
RT_READ(tab, tr);
|
||||||
|
|
||||||
for (int ap=0; ap<nsz; ap++)
|
for (uint ap=0; ap<nsz; ap++)
|
||||||
{
|
{
|
||||||
net_addr_union nau = { .aspa = NET_ADDR_ASPA(asns[ap]), };
|
net_addr_union nau = { .aspa = NET_ADDR_ASPA(asns[ap]), };
|
||||||
bool seen = false;
|
|
||||||
|
|
||||||
/* Find some ASPAs */
|
/* Find some ASPAs */
|
||||||
struct netindex *ni = net_find_index(tr->t->netindex, &nau.n);
|
struct netindex *ni = net_find_index(tr->t->netindex, &nau.n);
|
||||||
net *n = ni ? net_find(tr, ni) : NULL;
|
net *n = ni ? net_find(tr, ni) : NULL;
|
||||||
|
|
||||||
if (!n)
|
bool found = false, down = false, up = false;
|
||||||
{
|
|
||||||
unknown_flag = up[ap] = down[ap] = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
up[ap] = down[ap] = false;
|
if (n) NET_READ_WALK_ROUTES(tr, n, ep, e)
|
||||||
|
|
||||||
/* Walk the existing records */
|
|
||||||
NET_READ_WALK_ROUTES(tr, n, ep, e)
|
|
||||||
{
|
{
|
||||||
if (!rte_is_valid(&e->rte))
|
if (!rte_is_valid(&e->rte))
|
||||||
continue;
|
continue;
|
||||||
@ -777,48 +771,80 @@ int aspa_check(rtable *tab, const adata *path)
|
|||||||
if (!ea)
|
if (!ea)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
seen = true;
|
/* Actually found some ASPA */
|
||||||
|
found = true;
|
||||||
|
|
||||||
for (uint i=0; i * sizeof(u32) < ea->u.ptr->length; i++)
|
for (uint i=0; i * sizeof(u32) < ea->u.ptr->length; i++)
|
||||||
{
|
{
|
||||||
if ((ap > 0) && ((u32 *) ea->u.ptr->data)[i] == asns[ap-1])
|
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])
|
if ((ap + 1 < nsz) && ((u32 *) ea->u.ptr->data)[i] == asns[ap+1])
|
||||||
up[ap] = true;
|
down = true;
|
||||||
|
|
||||||
if (down[ap] && up[ap])
|
if (down && up)
|
||||||
break;
|
/* Both peers found */
|
||||||
|
goto end_of_aspa;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
end_of_aspa:;
|
||||||
|
|
||||||
if (down[ap] && up[ap])
|
/* Fast path for the upstream check */
|
||||||
break;
|
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 */
|
/* Fast path for no ASPA here */
|
||||||
if (!seen)
|
else if (!found)
|
||||||
unknown_flag = up[ap] = down[ap] = true;
|
{
|
||||||
|
/* Extend max-downstream (min-downstream is stopped by unknown) */
|
||||||
|
max_down = ap+1;
|
||||||
|
|
||||||
|
/* Move min-upstream (can't include unknown) */
|
||||||
|
min_up = ap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ASPA exists and downstream may be extended */
|
||||||
|
else if (down)
|
||||||
|
{
|
||||||
|
/* Extending max-downstream always */
|
||||||
|
max_down = ap+1;
|
||||||
|
|
||||||
|
/* Extending min-downstream unless unknown seen */
|
||||||
|
if (min_down == ap)
|
||||||
|
min_down = ap+1;
|
||||||
|
|
||||||
|
/* Downstream only */
|
||||||
|
if (!up)
|
||||||
|
min_up = max_up = ap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check whether the topology is first ramp up and then ramp down. */
|
/* Is the path surely valid? */
|
||||||
int up_end = 0;
|
if (min_up <= min_down)
|
||||||
while (up_end < nsz && up[up_end])
|
return ASPA_VALID;
|
||||||
up_end++;
|
|
||||||
|
|
||||||
int down_end = nsz - 1;
|
/* Is the path maybe valid? */
|
||||||
while (down_end > 0 && down[down_end])
|
if (max_up <= max_down)
|
||||||
down_end--;
|
|
||||||
|
|
||||||
/* A significant overlap of obvious unknowns or misconfigured ASPAs. */
|
|
||||||
if (up_end - down_end >= 2)
|
|
||||||
return ASPA_UNKNOWN;
|
return ASPA_UNKNOWN;
|
||||||
|
|
||||||
/* The path has either a single transit provider, or a peering pair on top */
|
/* Now there is surely a valley there. */
|
||||||
else if (up_end - down_end >= 0)
|
return ASPA_INVALID_LEAK;
|
||||||
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 *
|
struct rte_storage *
|
||||||
|
@ -40,7 +40,7 @@ CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
|
|||||||
|
|
||||||
CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
|
CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
|
||||||
CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION,
|
CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION,
|
||||||
OUT, OF, RESOURCES)
|
OUT, OF, RESOURCES, ASPA_CHECK_CUSTOMER)
|
||||||
|
|
||||||
%type<i> bgp_cease_mask bgp_cease_list bgp_cease_flag bgp_role_name
|
%type<i> bgp_cease_mask bgp_cease_list bgp_cease_flag bgp_role_name
|
||||||
|
|
||||||
@ -383,6 +383,29 @@ CF_CLI(RELOAD BGP OUT, proto_patt, [<name>], [[Refresh routes to neighbor]])
|
|||||||
proto_apply_cmd($4, bgp_reload_out, 1, 0);
|
proto_apply_cmd($4, bgp_reload_out, 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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, }),
|
||||||
|
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_CODE
|
||||||
|
|
||||||
CF_END
|
CF_END
|
||||||
|
Loading…
Reference in New Issue
Block a user