mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-11-08 12:18:42 +00:00
Add ASPA functionality to BIRD
ASPA [1] is a new IETF draft for BGP that adds new RPKI objects and create an AS_PATH validation mechanism [2] based on this objects. [1] https://datatracker.ietf.org/doc/draft-ietf-sidrops-aspa-profile/ [2] https://datatracker.ietf.org/doc/draft-ietf-sidrops-aspa-verification/ (Minor changes done by commiter)
This commit is contained in:
parent
0edf0c8cd9
commit
e4df8a3e4c
@ -31,6 +31,8 @@ class BIRDFValPrinter(BIRDPrinter):
|
||||
"T_ENUM_NETTYPE": "i",
|
||||
"T_ENUM_RA_PREFERENCE": "i",
|
||||
"T_ENUM_AF": "i",
|
||||
"T_ENUM_ASPA": "i",
|
||||
"T_ENUM_BGP_DIR": "i",
|
||||
"T_IP": "ip",
|
||||
"T_NET": "net",
|
||||
"T_STRING": "s",
|
||||
|
@ -107,7 +107,7 @@ CF_DECLS
|
||||
%type <time> expr_us time
|
||||
%type <a> ipa
|
||||
%type <net> net_ip4_ net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa
|
||||
%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_
|
||||
%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_ net_aspa4_ net_aspa6_ net_aspa_
|
||||
%type <mls> label_stack_start label_stack
|
||||
|
||||
%type <t> text opttext
|
||||
@ -123,7 +123,7 @@ CF_DECLS
|
||||
|
||||
%start config
|
||||
|
||||
CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN, MPLS, FROM)
|
||||
CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN, MPLS, ASPA4, ASPA6, FROM)
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
@ -283,9 +283,22 @@ net_mpls_: MPLS NUM
|
||||
net_fill_mpls($$, $2);
|
||||
}
|
||||
|
||||
net_aspa4_: ASPA4 NUM NUM
|
||||
{
|
||||
$$ = cfg_alloc(sizeof(net_addr_aspa));
|
||||
net_fill_aspa($$, $2, $3, AFI_IPV4);
|
||||
};
|
||||
|
||||
net_aspa6_: ASPA6 NUM NUM
|
||||
{
|
||||
$$ = cfg_alloc(sizeof(net_addr_aspa));
|
||||
net_fill_aspa($$, $2, $3, AFI_IPV6);
|
||||
};
|
||||
|
||||
net_ip_: net_ip4_ | net_ip6_ ;
|
||||
net_vpn_: net_vpn4_ | net_vpn6_ ;
|
||||
net_roa_: net_roa4_ | net_roa6_ ;
|
||||
net_aspa_: net_aspa4_ | net_aspa6_ ;
|
||||
|
||||
net_:
|
||||
net_ip_ { $$ = cfg_alloc($1.length); net_copy($$, &($1)); }
|
||||
@ -294,6 +307,7 @@ net_:
|
||||
| net_flow_
|
||||
| net_ip6_sadr_
|
||||
| net_mpls_
|
||||
| net_aspa_
|
||||
;
|
||||
|
||||
|
||||
|
@ -434,6 +434,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX,
|
||||
PREFERENCE,
|
||||
ROA_CHECK, ASN, SRC,
|
||||
ASPA_CHECK,
|
||||
IS_V4, IS_V6,
|
||||
LEN, MAXLEN,
|
||||
DEFINED,
|
||||
@ -966,6 +967,9 @@ 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_IMPLICIT, $5, $3); }
|
||||
| ASPA_CHECK '(' rtable ',' term ',' term ',' term ')' { $$ = f_new_inst(FI_ASPA_CHECK_EXPLICIT, $5, $7, $9, $3); }
|
||||
|
||||
| FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
|
||||
|
||||
/* | term '.' LEN { $$->code = P('P','l'); } */
|
||||
|
@ -39,6 +39,8 @@ enum f_type {
|
||||
T_ENUM_NETTYPE = 0x36,
|
||||
T_ENUM_RA_PREFERENCE = 0x37,
|
||||
T_ENUM_AF = 0x38,
|
||||
T_ENUM_ASPA = 0x39,
|
||||
T_ENUM_BGP_DIR = 0x3a,
|
||||
|
||||
/* new enums go here */
|
||||
T_ENUM_EMPTY = 0x3f, /* Special hack for atomic_aggr */
|
||||
|
@ -1170,6 +1170,58 @@
|
||||
|
||||
}
|
||||
|
||||
INST(FI_ASPA_CHECK_IMPLICIT, 1, 1) { /* ASPA Check */
|
||||
NEVER_CONSTANT;
|
||||
ARG(1, T_ENUM_BGP_DIR);
|
||||
RTC(2);
|
||||
struct rtable *table = rtc->table;
|
||||
ACCESS_RTE;
|
||||
ACCESS_EATTRS;
|
||||
const net_addr *net = (*fs->rte)->net->n.addr;
|
||||
|
||||
if (!table)
|
||||
runtime("Missing ASPA table");
|
||||
|
||||
if (table->addr_type != NET_ASPA)
|
||||
runtime("Table type must be ASPA");
|
||||
|
||||
/* We ignore temporary attributes, probably not a problem here */
|
||||
/* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
|
||||
eattr *e = ea_find(*fs->eattrs, EA_CODE(PROTOCOL_BGP, 0x02));
|
||||
|
||||
if (!e || ((e->type & EAF_TYPE_MASK) != EAF_TYPE_AS_PATH))
|
||||
runtime("Missing AS_PATH attribute");
|
||||
|
||||
uint dir = v1.val.i;
|
||||
|
||||
if (!net_is_ip(net))
|
||||
runtime("Network type must be IPv4 or IPv6");
|
||||
|
||||
uint afi = (net->type == NET_IP4) ? AFI_IPV4 : AFI_IPV6;
|
||||
|
||||
RESULT(T_ENUM_ASPA, i, [[ net_aspa_check(fpool, table, e->u.ptr, dir, afi) ]]);
|
||||
}
|
||||
|
||||
INST(FI_ASPA_CHECK_EXPLICIT, 3, 1) { /* ASPA Check */
|
||||
NEVER_CONSTANT;
|
||||
ARG(1, T_PATH);
|
||||
ARG(2, T_ENUM_BGP_DIR);
|
||||
ARG(3, T_ENUM_AF);
|
||||
RTC(4);
|
||||
struct rtable *table = rtc->table;
|
||||
|
||||
if (!table)
|
||||
runtime("Missing ASPA table");
|
||||
|
||||
if (table->addr_type != NET_ASPA)
|
||||
runtime("Table type must be ASPA");
|
||||
|
||||
if ((v3.val.i != AFI_IPV4) && (v3.val.i != AFI_IPV6))
|
||||
runtime("Address family must be AF_IPV4 or AF_IPV6");
|
||||
|
||||
RESULT(T_ENUM_ASPA, i, [[ net_aspa_check(fpool, table, v1.val.ad, v2.val.i, v3.val.i) ]]);
|
||||
}
|
||||
|
||||
INST(FI_FORMAT, 1, 0) { /* Format */
|
||||
ARG_ANY(1);
|
||||
RESULT(T_STRING, s, val_format_str(fpool, &v1));
|
||||
|
@ -1245,6 +1245,14 @@ int j;
|
||||
accept "ok I take that";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Testing ROA checks
|
||||
* ------------------
|
||||
*/
|
||||
|
||||
filter roa_filter
|
||||
{
|
||||
if net ~ [ 10.0.0.0/8{16,24}, 2000::/3{16,96} ] then {
|
||||
@ -1331,6 +1339,85 @@ prefix pfx;
|
||||
|
||||
bt_test_suite(test_roa_check, "Testing ROA");
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Testing ASPA checks
|
||||
* -------------------
|
||||
*/
|
||||
|
||||
aspa table at;
|
||||
|
||||
protocol static
|
||||
{
|
||||
aspa;
|
||||
route aspa4 65000 65001;
|
||||
route aspa4 65000 65002;
|
||||
route aspa4 64999 65001;
|
||||
route aspa4 65004 64999;
|
||||
route aspa4 65001 0;
|
||||
route aspa4 65006 65100;
|
||||
|
||||
route aspa6 65000 65003;
|
||||
}
|
||||
|
||||
function t_aspa_check()
|
||||
bgppath p1;
|
||||
bgppath p2;
|
||||
{
|
||||
# empty path
|
||||
p1 = + empty + ;
|
||||
bt_assert(aspa_check(at, p1, BGP_DIR_UP, AF_IPV4) = ASPA_VALID);
|
||||
|
||||
# 65000
|
||||
p1 = prepend(p1, 65000);
|
||||
bt_assert(aspa_check(at, p1, BGP_DIR_UP, AF_IPV4) = ASPA_VALID);
|
||||
|
||||
# 65000 65000
|
||||
p1 = prepend(p1, 65000);
|
||||
bt_assert(aspa_check(at, p1, BGP_DIR_UP, AF_IPV4) = ASPA_VALID);
|
||||
|
||||
# 65001 65000 65000
|
||||
p1 = prepend(p1, 65001);
|
||||
bt_assert(aspa_check(at, p1, BGP_DIR_UP, AF_IPV4) = ASPA_VALID);
|
||||
|
||||
# 65003 65000
|
||||
p1 = prepend(+ empty +, 65000);
|
||||
p1 = prepend(p1, 65003);
|
||||
bt_assert(aspa_check(at, p1, BGP_DIR_UP, AF_IPV4) = ASPA_INVALID);
|
||||
bt_assert(aspa_check(at, p1, BGP_DIR_UP, AF_IPV6) = ASPA_VALID);
|
||||
|
||||
# 65000 65003
|
||||
p1 = prepend(+ empty +, 65003);
|
||||
p1 = prepend(p1, 65000);
|
||||
bt_assert(aspa_check(at, p1, BGP_DIR_UP, AF_IPV4) = ASPA_UNKNOWN);
|
||||
|
||||
# 65004 64999 65001 65001 65000
|
||||
p1 = prepend(+ empty +, 65000);
|
||||
p1 = prepend(p1, 65001);
|
||||
p1 = prepend(p1, 65001);
|
||||
p1 = prepend(p1, 64999);
|
||||
p1 = prepend(p1, 65004);
|
||||
bt_assert(aspa_check(at, p1, BGP_DIR_UP, AF_IPV4) = ASPA_INVALID);
|
||||
bt_assert(aspa_check(at, p1, BGP_DIR_DOWN, AF_IPV4) = ASPA_VALID);
|
||||
|
||||
# 65005 65004 64999 65001 65001 65000
|
||||
p2 = prepend(p1, 65005);
|
||||
bt_assert(aspa_check(at, p2, BGP_DIR_UP, AF_IPV4) = ASPA_INVALID);
|
||||
bt_assert(aspa_check(at, p2, BGP_DIR_DOWN, AF_IPV4) = ASPA_UNKNOWN);
|
||||
|
||||
# 65006 65004 64999 65001 65001 65000
|
||||
p2 = prepend(p1, 65006);
|
||||
bt_assert(aspa_check(at, p2, BGP_DIR_UP, AF_IPV4) = ASPA_INVALID);
|
||||
bt_assert(aspa_check(at, p2, BGP_DIR_DOWN, AF_IPV4) = ASPA_INVALID);
|
||||
}
|
||||
|
||||
bt_test_suite(t_aspa_check, "Testing ASPA");
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Testing Mixed Net Types
|
||||
* -----------------------
|
||||
|
15
lib/net.c
15
lib/net.c
@ -16,6 +16,7 @@ const char * const net_label[] = {
|
||||
[NET_FLOW6] = "flow6",
|
||||
[NET_IP6_SADR]= "ipv6-sadr",
|
||||
[NET_MPLS] = "mpls",
|
||||
[NET_ASPA] = "aspa",
|
||||
};
|
||||
|
||||
const u16 net_addr_length[] = {
|
||||
@ -29,6 +30,7 @@ const u16 net_addr_length[] = {
|
||||
[NET_FLOW6] = 0,
|
||||
[NET_IP6_SADR]= sizeof(net_addr_ip6_sadr),
|
||||
[NET_MPLS] = sizeof(net_addr_mpls),
|
||||
[NET_ASPA] = sizeof(net_addr_aspa),
|
||||
};
|
||||
|
||||
const u8 net_max_prefix_length[] = {
|
||||
@ -42,6 +44,7 @@ const u8 net_max_prefix_length[] = {
|
||||
[NET_FLOW6] = IP6_MAX_PREFIX_LENGTH,
|
||||
[NET_IP6_SADR]= IP6_MAX_PREFIX_LENGTH,
|
||||
[NET_MPLS] = 0,
|
||||
[NET_ASPA] = 0,
|
||||
};
|
||||
|
||||
const u16 net_max_text_length[] = {
|
||||
@ -55,6 +58,7 @@ const u16 net_max_text_length[] = {
|
||||
[NET_FLOW6] = 0, /* "flow6 { ... }" */
|
||||
[NET_IP6_SADR]= 92, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128 from ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
|
||||
[NET_MPLS] = 7, /* "1048575" */
|
||||
[NET_ASPA] = 31, /* "aspaX AS4294967295 AS4294967295" */
|
||||
};
|
||||
|
||||
|
||||
@ -110,6 +114,8 @@ net_format(const net_addr *N, char *buf, int buflen)
|
||||
return bsnprintf(buf, buflen, "%I6/%d from %I6/%d", n->ip6_sadr.dst_prefix, n->ip6_sadr.dst_pxlen, n->ip6_sadr.src_prefix, n->ip6_sadr.src_pxlen);
|
||||
case NET_MPLS:
|
||||
return bsnprintf(buf, buflen, "%u", n->mpls.label);
|
||||
case NET_ASPA:
|
||||
return bsnprintf(buf, buflen, "aspa%u AS%u AS%u", (n->aspa.afi == AFI_IPV4) ? 4 : 6, n->aspa.customer_asn, n->aspa.provider_asn);
|
||||
}
|
||||
|
||||
bug("unknown network type");
|
||||
@ -133,6 +139,7 @@ net_pxmask(const net_addr *a)
|
||||
case NET_IP6_SADR:
|
||||
return ipa_from_ip6(ip6_mkmask(net6_pxlen(a)));
|
||||
|
||||
case NET_ASPA:
|
||||
case NET_MPLS:
|
||||
default:
|
||||
return IPA_NONE;
|
||||
@ -167,6 +174,8 @@ net_compare(const net_addr *a, const net_addr *b)
|
||||
return net_compare_ip6_sadr((const net_addr_ip6_sadr *) a, (const net_addr_ip6_sadr *) b);
|
||||
case NET_MPLS:
|
||||
return net_compare_mpls((const net_addr_mpls *) a, (const net_addr_mpls *) b);
|
||||
case NET_ASPA:
|
||||
return net_compare_aspa((const net_addr_aspa *) a, (const net_addr_aspa *) b);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -188,6 +197,7 @@ net_hash(const net_addr *n)
|
||||
case NET_FLOW6: return NET_HASH(n, flow6);
|
||||
case NET_IP6_SADR: return NET_HASH(n, ip6_sadr);
|
||||
case NET_MPLS: return NET_HASH(n, mpls);
|
||||
case NET_ASPA: return NET_HASH(n, aspa);
|
||||
default: bug("invalid type");
|
||||
}
|
||||
}
|
||||
@ -210,6 +220,7 @@ net_validate(const net_addr *n)
|
||||
case NET_FLOW6: return NET_VALIDATE(n, flow6);
|
||||
case NET_IP6_SADR: return NET_VALIDATE(n, ip6_sadr);
|
||||
case NET_MPLS: return NET_VALIDATE(n, mpls);
|
||||
case NET_ASPA: return NET_VALIDATE(n, aspa);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
@ -237,6 +248,7 @@ net_normalize(net_addr *N)
|
||||
return net_normalize_ip6_sadr(&n->ip6_sadr);
|
||||
|
||||
case NET_MPLS:
|
||||
case NET_ASPA:
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -264,6 +276,7 @@ net_classify(const net_addr *N)
|
||||
return ip6_zero(n->ip6_sadr.dst_prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->ip6_sadr.dst_prefix);
|
||||
|
||||
case NET_MPLS:
|
||||
case NET_ASPA:
|
||||
return IADDR_HOST | SCOPE_UNIVERSE;
|
||||
}
|
||||
|
||||
@ -297,6 +310,7 @@ ipa_in_netX(const ip_addr a, const net_addr *n)
|
||||
ip6_mkmask(net6_pxlen(n))));
|
||||
|
||||
case NET_MPLS:
|
||||
case NET_ASPA:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -328,4 +342,5 @@ net_init(void)
|
||||
CHECK_NET(net_addr_flow6, 20);
|
||||
CHECK_NET(net_addr_ip6_sadr, 40);
|
||||
CHECK_NET(net_addr_mpls, 8);
|
||||
CHECK_NET(net_addr_aspa, 12);
|
||||
}
|
||||
|
44
lib/net.h
44
lib/net.h
@ -23,7 +23,8 @@
|
||||
#define NET_FLOW6 8
|
||||
#define NET_IP6_SADR 9
|
||||
#define NET_MPLS 10
|
||||
#define NET_MAX 11
|
||||
#define NET_ASPA 11
|
||||
#define NET_MAX 12
|
||||
|
||||
#define NB_IP4 (1 << NET_IP4)
|
||||
#define NB_IP6 (1 << NET_IP6)
|
||||
@ -35,6 +36,7 @@
|
||||
#define NB_FLOW6 (1 << NET_FLOW6)
|
||||
#define NB_IP6_SADR (1 << NET_IP6_SADR)
|
||||
#define NB_MPLS (1 << NET_MPLS)
|
||||
#define NB_ASPA (1 << NET_ASPA)
|
||||
|
||||
#define NB_IP (NB_IP4 | NB_IP6)
|
||||
#define NB_VPN (NB_VPN4 | NB_VPN6)
|
||||
@ -123,6 +125,15 @@ typedef struct net_addr_mpls {
|
||||
u32 label;
|
||||
} net_addr_mpls;
|
||||
|
||||
typedef struct net_addr_aspa {
|
||||
u8 type;
|
||||
u8 afi; /* Only IPv4 and IPv6 are allowed */
|
||||
u16 length;
|
||||
u32 customer_asn;
|
||||
u32 provider_asn;
|
||||
} net_addr_aspa;
|
||||
|
||||
|
||||
typedef struct net_addr_ip6_sadr {
|
||||
u8 type;
|
||||
u8 dst_pxlen;
|
||||
@ -144,6 +155,7 @@ typedef union net_addr_union {
|
||||
net_addr_flow6 flow6;
|
||||
net_addr_ip6_sadr ip6_sadr;
|
||||
net_addr_mpls mpls;
|
||||
net_addr_aspa aspa;
|
||||
} net_addr_union;
|
||||
|
||||
|
||||
@ -185,6 +197,8 @@ extern const u16 net_max_text_length[];
|
||||
#define NET_ADDR_MPLS(label) \
|
||||
((net_addr_mpls) { NET_MPLS, 20, sizeof(net_addr_mpls), label })
|
||||
|
||||
#define NET_ADDR_ASPA(customer_asn, provider_asn, afi) \
|
||||
((net_addr_aspa) { NET_ASPA, afi, sizeof(net_addr_aspa), customer_asn, provider_asn })
|
||||
|
||||
static inline void net_fill_ip4(net_addr *a, ip4_addr prefix, uint pxlen)
|
||||
{ *(net_addr_ip4 *)a = NET_ADDR_IP4(prefix, pxlen); }
|
||||
@ -210,6 +224,9 @@ static inline void net_fill_ip6_sadr(net_addr *a, ip6_addr dst_prefix, uint dst_
|
||||
static inline void net_fill_mpls(net_addr *a, u32 label)
|
||||
{ *(net_addr_mpls *)a = NET_ADDR_MPLS(label); }
|
||||
|
||||
static inline void net_fill_aspa(net_addr *a, u32 customer_asn, u32 provider_asn, u8 afi)
|
||||
{ *(net_addr_aspa *)a = NET_ADDR_ASPA(customer_asn, provider_asn, afi); }
|
||||
|
||||
static inline void net_fill_ipa(net_addr *a, ip_addr prefix, uint pxlen)
|
||||
{
|
||||
if (ipa_is_ip4(prefix))
|
||||
@ -271,6 +288,10 @@ static inline int net_is_flow(const net_addr *a)
|
||||
static inline int net_is_sadr(const net_addr *a)
|
||||
{ return (a->type == NET_IP6_SADR); }
|
||||
|
||||
static inline int net_is_aspa(const net_addr *a)
|
||||
{ return (a->type == NET_ASPA); }
|
||||
|
||||
|
||||
static inline ip4_addr net4_prefix(const net_addr *a)
|
||||
{ return ((net_addr_ip4 *) a)->prefix; }
|
||||
|
||||
@ -295,6 +316,7 @@ static inline ip_addr net_prefix(const net_addr *a)
|
||||
return ipa_from_ip6(net6_prefix(a));
|
||||
|
||||
case NET_MPLS:
|
||||
case NET_ASPA:
|
||||
default:
|
||||
return IPA_NONE;
|
||||
}
|
||||
@ -365,6 +387,9 @@ static inline int net_equal_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_
|
||||
static inline int net_equal_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
|
||||
{ return !memcmp(a, b, sizeof(net_addr_mpls)); }
|
||||
|
||||
static inline int net_equal_aspa(const net_addr_aspa *a, const net_addr_aspa *b)
|
||||
{ return !memcmp(a, b, sizeof(net_addr_aspa)); }
|
||||
|
||||
|
||||
static inline int net_equal_prefix_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
|
||||
{ return ip4_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); }
|
||||
@ -378,6 +403,9 @@ static inline int net_equal_dst_ip6_sadr(const net_addr_ip6_sadr *a, const net_a
|
||||
static inline int net_equal_src_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_ip6_sadr *b)
|
||||
{ return ip6_equal(a->src_prefix, b->src_prefix) && (a->src_pxlen == b->src_pxlen); }
|
||||
|
||||
static inline int net_equal_customer_aspa(const net_addr_aspa *a, const net_addr_aspa *b)
|
||||
{ return ((a->afi == b->afi) && (a->customer_asn == b->customer_asn)); }
|
||||
|
||||
|
||||
static inline int net_zero_ip4(const net_addr_ip4 *a)
|
||||
{ return !a->pxlen && ip4_zero(a->prefix); }
|
||||
@ -406,6 +434,9 @@ static inline int net_zero_flow6(const net_addr_flow6 *a)
|
||||
static inline int net_zero_mpls(const net_addr_mpls *a)
|
||||
{ return !a->label; }
|
||||
|
||||
static inline int net_zero_aspa(const net_addr_aspa *a)
|
||||
{ return !a->afi && !a->customer_asn && !a->provider_asn; }
|
||||
|
||||
|
||||
static inline int net_compare_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
|
||||
{ return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
|
||||
@ -441,6 +472,9 @@ static inline int net_compare_ip6_sadr(const net_addr_ip6_sadr *a, const net_add
|
||||
static inline int net_compare_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
|
||||
{ return uint_cmp(a->label, b->label); }
|
||||
|
||||
static inline int net_compare_aspa(const net_addr_aspa *a, const net_addr_aspa *b)
|
||||
{ return uint_cmp(a->afi, b->afi) ?: uint_cmp(a->customer_asn, b->customer_asn) ?: uint_cmp(a->provider_asn, b->provider_asn); }
|
||||
|
||||
int net_compare(const net_addr *a, const net_addr *b);
|
||||
|
||||
|
||||
@ -477,6 +511,8 @@ static inline void net_copy_ip6_sadr(net_addr_ip6_sadr *dst, const net_addr_ip6_
|
||||
static inline void net_copy_mpls(net_addr_mpls *dst, const net_addr_mpls *src)
|
||||
{ memcpy(dst, src, sizeof(net_addr_mpls)); }
|
||||
|
||||
static inline void net_copy_aspa(net_addr_aspa *dst, const net_addr_aspa *src)
|
||||
{ memcpy(dst, src, sizeof(net_addr_aspa)); }
|
||||
|
||||
/* XXXX */
|
||||
static inline u32 u64_hash(u64 a)
|
||||
@ -512,6 +548,9 @@ static inline u32 net_hash_ip6_sadr(const net_addr_ip6_sadr *n)
|
||||
static inline u32 net_hash_mpls(const net_addr_mpls *n)
|
||||
{ return n->label; }
|
||||
|
||||
static inline u32 net_hash_aspa(const net_addr_aspa *n)
|
||||
{ return n->afi ^ u32_hash(n->customer_asn); }
|
||||
|
||||
u32 net_hash(const net_addr *a);
|
||||
|
||||
|
||||
@ -564,6 +603,9 @@ static inline int net_validate_mpls(const net_addr_mpls *n)
|
||||
static inline int net_validate_ip6_sadr(const net_addr_ip6_sadr *n)
|
||||
{ return net_validate_px6(n->dst_prefix, n->dst_pxlen) && net_validate_px6(n->src_prefix, n->src_pxlen); }
|
||||
|
||||
static inline int net_validate_aspa(const net_addr_aspa *n)
|
||||
{ return (n->customer_asn > 0) && ((n->afi == 1) || (n->afi == 2)); }
|
||||
|
||||
int net_validate(const net_addr *N);
|
||||
|
||||
|
||||
|
@ -169,6 +169,27 @@ as_path_contains_confed(const struct adata *path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
as_path_contains_set(const struct adata *path)
|
||||
{
|
||||
const byte *pos = path->data;
|
||||
const byte *end = pos + path->length;
|
||||
|
||||
while (pos < end)
|
||||
{
|
||||
uint type = pos[0];
|
||||
uint slen = 2 + BS * pos[1];
|
||||
|
||||
if ((type == AS_PATH_SET) ||
|
||||
(type == AS_PATH_CONFED_SET))
|
||||
return 1;
|
||||
|
||||
pos += slen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct adata *
|
||||
as_path_strip_confed(struct linpool *pool, const struct adata *path)
|
||||
{
|
||||
@ -198,6 +219,55 @@ as_path_strip_confed(struct linpool *pool, const struct adata *path)
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
as_path_get_reverse_unique_asns(struct linpool *pool, const struct adata *path, u32 **data)
|
||||
{
|
||||
u32 *asns = lp_alloc(pool, as_path_getlen(path) * sizeof(u32));
|
||||
const byte *src = path->data;
|
||||
const byte *end = src + path->length;
|
||||
u32 *dst = asns;
|
||||
u32 prev_as = 0;
|
||||
u32 as;
|
||||
uint i;
|
||||
|
||||
/* Step 1: create array of unique asns */
|
||||
while (src < end)
|
||||
{
|
||||
uint t = src[0];
|
||||
uint l = src[1];
|
||||
src += 2;
|
||||
|
||||
if (t == AS_PATH_SEQUENCE)
|
||||
{
|
||||
for (i = 0; i < l; i++)
|
||||
{
|
||||
as = get_as(src);
|
||||
if (as != prev_as)
|
||||
*dst++ = as;
|
||||
prev_as = as;
|
||||
src += BS;
|
||||
}
|
||||
}
|
||||
else
|
||||
src += BS * l;
|
||||
}
|
||||
u32 num = dst - asns;
|
||||
|
||||
/* Step 2: Reverse the array */
|
||||
u32 as_1, as_2;
|
||||
for (i = 0; i < num/2; i++)
|
||||
{
|
||||
as_1 = asns[i];
|
||||
as_2 = asns[num-i-1];
|
||||
asns[i] = as_2;
|
||||
asns[num-i-1] = as_1;
|
||||
}
|
||||
|
||||
*data = asns;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
struct adata *
|
||||
as_path_prepend2(struct linpool *pool, const struct adata *op, int seq, u32 as)
|
||||
{
|
||||
|
@ -35,12 +35,14 @@ int as_path_16to32(byte *dst, const byte *src, uint len);
|
||||
int as_path_32to16(byte *dst, const byte *src, uint len);
|
||||
int as_path_contains_as4(const struct adata *path);
|
||||
int as_path_contains_confed(const struct adata *path);
|
||||
int as_path_contains_set(const struct adata *path);
|
||||
struct adata *as_path_strip_confed(struct linpool *pool, const struct adata *op);
|
||||
struct adata *as_path_prepend2(struct linpool *pool, const struct adata *op, int seq, u32 as);
|
||||
struct adata *as_path_to_old(struct linpool *pool, const struct adata *path);
|
||||
struct adata *as_path_cut(struct linpool *pool, const struct adata *path, uint num);
|
||||
const struct adata *as_path_merge(struct linpool *pool, const struct adata *p1, const struct adata *p2);
|
||||
void as_path_format(const struct adata *path, byte *buf, uint size);
|
||||
int as_path_get_reverse_unique_asns(struct linpool *pool, const struct adata *path, u32 **data);
|
||||
int as_path_getlen(const struct adata *path);
|
||||
int as_path_getlen_int(const struct adata *path, int bs);
|
||||
int as_path_get_first(const struct adata *path, u32 *orig_as);
|
||||
|
@ -66,7 +66,7 @@ CF_DECLS
|
||||
|
||||
CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
|
||||
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, DEFAULT, TABLE, STATES, ROUTES, FILTERS)
|
||||
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS)
|
||||
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS, ASPA)
|
||||
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED)
|
||||
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
|
||||
CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512)
|
||||
@ -78,13 +78,15 @@ CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
|
||||
CF_KEYWORDS(CHECK, LINK)
|
||||
|
||||
/* For r_args_channel */
|
||||
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
|
||||
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, ASPA, PRI, SEC)
|
||||
|
||||
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
|
||||
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL)
|
||||
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, UNVERIFIED)
|
||||
|
||||
CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
|
||||
|
||||
%type <i32> idval
|
||||
@ -144,9 +146,10 @@ net_type:
|
||||
| FLOW4{ $$ = NET_FLOW4; }
|
||||
| FLOW6{ $$ = NET_FLOW6; }
|
||||
| MPLS { $$ = NET_MPLS; }
|
||||
| ASPA { $$ = NET_ASPA; }
|
||||
;
|
||||
|
||||
CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, IP6_SADR)
|
||||
CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, IP6_SADR, ASPA)
|
||||
|
||||
|
||||
/* Creation of routing tables */
|
||||
@ -707,6 +710,7 @@ r_args_channel:
|
||||
| FLOW4 { $$ = "flow4"; }
|
||||
| FLOW6 { $$ = "flow6"; }
|
||||
| MPLS { $$ = "mpls"; }
|
||||
| ASPA { $$ = "aspa"; }
|
||||
| PRI { $$ = "pri"; }
|
||||
| SEC { $$ = "sec"; }
|
||||
;
|
||||
|
10
nest/route.h
10
nest/route.h
@ -535,6 +535,7 @@ lp_alloc_adata(struct linpool *pool, uint len)
|
||||
static inline int adata_same(const struct adata *a, const struct adata *b)
|
||||
{ return (a->length == b->length && !memcmp(a->data, b->data, a->length)); }
|
||||
|
||||
int net_aspa_check(struct linpool *lp, rtable *tab, const struct adata *path, uint dir, u8 afi);
|
||||
|
||||
typedef struct ea_list {
|
||||
struct ea_list *next; /* In case we have an override list */
|
||||
@ -708,4 +709,13 @@ static inline void rt_unlock_hostentry(struct hostentry *he) { if (he) he->uc--;
|
||||
#define ROA_VALID 1
|
||||
#define ROA_INVALID 2
|
||||
|
||||
/*
|
||||
* ASPA verification status
|
||||
*/
|
||||
|
||||
#define ASPA_UNKNOWN 0
|
||||
#define ASPA_VALID 1
|
||||
#define ASPA_INVALID 2
|
||||
#define ASPA_UNVERIFIED 3
|
||||
|
||||
#endif
|
||||
|
@ -279,6 +279,7 @@ fib_find(struct fib *f, const net_addr *a)
|
||||
case NET_FLOW6: return FIB_FIND(f, a, flow6);
|
||||
case NET_IP6_SADR: return FIB_FIND(f, a, ip6_sadr);
|
||||
case NET_MPLS: return FIB_FIND(f, a, mpls);
|
||||
case NET_ASPA: return FIB_FIND(f, a, aspa);
|
||||
default: bug("invalid type");
|
||||
}
|
||||
}
|
||||
@ -300,6 +301,7 @@ fib_insert(struct fib *f, const net_addr *a, struct fib_node *e)
|
||||
case NET_FLOW6: FIB_INSERT(f, a, e, flow6); return;
|
||||
case NET_IP6_SADR: FIB_INSERT(f, a, e, ip6_sadr); return;
|
||||
case NET_MPLS: FIB_INSERT(f, a, e, mpls); return;
|
||||
case NET_ASPA: FIB_INSERT(f, a, e, aspa); return;
|
||||
default: bug("invalid type");
|
||||
}
|
||||
}
|
||||
|
@ -249,6 +249,78 @@ net_roa_check(rtable *tab, const net_addr *n, u32 asn)
|
||||
return ROA_UNKNOWN; /* Should not happen */
|
||||
}
|
||||
|
||||
static int
|
||||
net_aspa_pair_check(rtable *tab, u32 customer_asn, u32 provider_asn, u8 afi)
|
||||
{
|
||||
struct net_addr_aspa n = NET_ADDR_ASPA(customer_asn, provider_asn, afi);
|
||||
struct fib_node *fn;
|
||||
int anything = 0;
|
||||
|
||||
for (fn = fib_get_chain(&tab->fib, (net_addr *) &n); fn; fn = fn->next)
|
||||
{
|
||||
net_addr_aspa *aspa = (void *) fn->addr;
|
||||
net *r = fib_node_to_user(&tab->fib, fn);
|
||||
|
||||
if (net_equal_customer_aspa(aspa, &n) && rte_is_valid(r->routes))
|
||||
{
|
||||
anything = 1;
|
||||
if (net_equal_aspa(aspa, &n))
|
||||
return ASPA_VALID;
|
||||
}
|
||||
}
|
||||
|
||||
return anything ? ASPA_INVALID : ASPA_UNKNOWN;
|
||||
}
|
||||
|
||||
int
|
||||
net_aspa_check(linpool *lp, rtable *tab, const struct adata *path, uint dir, u8 afi)
|
||||
{
|
||||
ASSERT(tab->addr_type == NET_ASPA);
|
||||
|
||||
if (as_path_contains_set(path))
|
||||
return ASPA_UNVERIFIED;
|
||||
|
||||
u32 *asns;
|
||||
int asn_count = as_path_get_reverse_unique_asns(lp, path, &asns);
|
||||
|
||||
int invalid_count = 0;
|
||||
int unknown_count = 0;
|
||||
u32 customer_asn;
|
||||
u32 provider_asn;
|
||||
|
||||
if (asn_count < 2)
|
||||
return ASPA_VALID;
|
||||
|
||||
for (int i = 0; i < (asn_count - 1); i++)
|
||||
{
|
||||
if (!invalid_count)
|
||||
{
|
||||
customer_asn = asns[i];
|
||||
provider_asn = asns[i+1];
|
||||
}
|
||||
else
|
||||
{
|
||||
customer_asn = asns[i+1];
|
||||
provider_asn = asns[i];
|
||||
}
|
||||
|
||||
int pair_status = net_aspa_pair_check(tab, customer_asn, provider_asn, afi);
|
||||
|
||||
if (pair_status == ASPA_INVALID)
|
||||
{
|
||||
if ((dir == 1) || invalid_count)
|
||||
return ASPA_INVALID;
|
||||
else
|
||||
invalid_count++;
|
||||
}
|
||||
|
||||
if (pair_status == ASPA_UNKNOWN)
|
||||
unknown_count++;
|
||||
}
|
||||
|
||||
return unknown_count ? ASPA_UNKNOWN : ASPA_VALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* rte_find - find a route
|
||||
* @net: network node
|
||||
|
@ -733,5 +733,10 @@ void bgp_update_next_hop(struct bgp_export_state *s, eattr *a, ea_list **to);
|
||||
#define ORIGIN_EGP 1
|
||||
#define ORIGIN_INCOMPLETE 2
|
||||
|
||||
/* BGP dir */
|
||||
|
||||
#define BGP_DIR_DOWN 0
|
||||
#define BGP_DIR_UP 1
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -32,6 +32,9 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
|
||||
LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL,
|
||||
DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST)
|
||||
|
||||
CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
|
||||
CF_ENUM(T_ENUM_BGP_DIR, BGP_DIR_, DOWN, UP)
|
||||
|
||||
%type <i> bgp_nh
|
||||
%type <i32> bgp_afi
|
||||
|
||||
@ -322,9 +325,6 @@ dynamic_attr: BGP_LARGE_COMMUNITY
|
||||
{ $$ = f_new_dynamic_attr(EAF_TYPE_LC_SET, T_LCLIST, EA_CODE(PROTOCOL_BGP, BA_LARGE_COMMUNITY)); } ;
|
||||
|
||||
|
||||
|
||||
CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
|
||||
|
||||
CF_CODE
|
||||
|
||||
CF_END
|
||||
|
Loading…
Reference in New Issue
Block a user