]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Add ASPA functionality to BIRD aspa
authorEugene Bogomazov <eb@qrator.net>
Wed, 2 Oct 2019 14:26:05 +0000 (16:26 +0200)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Mon, 4 Nov 2019 13:31:48 +0000 (14:31 +0100)
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)

16 files changed:
bird-gdb.py
conf/confbase.Y
filter/config.Y
filter/data.h
filter/f-inst.c
filter/test.conf
lib/net.c
lib/net.h
nest/a-path.c
nest/attrs.h
nest/config.Y
nest/route.h
nest/rt-fib.c
nest/rt-table.c
proto/bgp/bgp.h
proto/bgp/config.Y

index 3cf65a9cca0168fc9be709dfab49d2b8dfac7e63..adfa58954ee22bf4ac683a01c5440912ff4b3297 100644 (file)
@@ -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",
index 75158927673af1e7e66ad6f67f95ddafc4d8e600..a0200abb1da6b33395e6478add0d815a9310a7e3 100644 (file)
@@ -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_
  ;
 
 
index 340053ba7b11108a22700c2c37a5d78f25696fba..3625cc81c006596cd286e71929313eacf821a921 100644 (file)
@@ -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'); } */
index db6b6e121e02d79b25e13e40989acaebe7f87881..d179a20256c6487754afd80f9a5a6cae97f13acc 100644 (file)
@@ -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 */
index 385d18d054a5cf3de58177744f1b980432b36db6..932f5549afc3340664ec16d10104ebf27767ded3 100644 (file)
 
   }
 
+  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));
index b1342819686dc3f3fd4d84c5d8ba98c31fc67dca..eaa864d1b328c4ca426cfeb64bfe32e1a703094b 100644 (file)
@@ -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
  *      -----------------------
index 976ddbcc7b841c8a82dd69db6d8af96eae736a35..cf7513bd5837e9a6a11fcf36a073d1996d2ffcbb 100644 (file)
--- a/lib/net.c
+++ b/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);
 }
index 0cd5f735d6296fcc5aa7ffa6399dd1b04a3fde1c..7c8c430982a910563e5f4a0e721f9df85daf9de5 100644 (file)
--- a/lib/net.h
+++ b/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);
 
 
index b6a30f548f31d3f1776ad4aaf35efb01ddf68cb4..7712126f202e261b0415db7c2c0d7e9d07351652 100644 (file)
@@ -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)
 {
index 6fb0a8fa32ecf04076e2029de46c40a0fdb4a842..6d9857d7566b5387aebb3e73a39fe70583695e79 100644 (file)
@@ -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);
index 8fe060c522d77e42d1cd0e3929293fda14f90442..1a4215d07d6d3c64ae6d3612653afdf4918a6571 100644 (file)
@@ -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"; }
  ;
index cd4b75db883135ff3a8fc686d61e3a67fbe8a952..f59ce679984ad3b0cbd55a5f98b8b2ace143accf 100644 (file)
@@ -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
index a8800b654dbae94f3138dc2fd8e97c65bd18c08c..a1be54648563d4f3705b70267b0f24086a2bcbb5 100644 (file)
@@ -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");
   }
 }
index df7c1634293035b7a20f4b260dcfbc242a889936..994dfbd94ce8c2aa1504a2b6fd7021f7cef69f38 100644 (file)
@@ -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
index d3e8f2ff9462a63e78d2ba794679fef83e052912..483c589e0126078be91e17b108795fbcb4d5290c 100644 (file)
@@ -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
index 692854cf6a410626d795292e3fa52640cf34b4d8..b98fe6b7663bbae71c5782455881f15397ddfc93 100644 (file)
@@ -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