So here is the validaton logic for ASPA. Now this is currently not
hooked up to anything apart from a larger regress test.
What is missing is code to send the ASPA table to the RDE and the reload
logic for ASPA.

Most of the ASPA validation happens in rde_aspa.c.
aspa_cp_lookup() checks if a customer - provider relation exists
aspa_check_aspath() walks an AS_PATH and calls aspa_cp_lookup() for each
touple. If check_downramp is true then both the upramp (source-as up
towards the Internet) and downramp (from the Internet back to us) are
checked.
aspa_validation() uses aspa_check_aspath() to validate the path. I split
the code up between aspa_check_aspath() and aspa_validation() to make it
possible to unit test aspa_check_aspath().

The code does not implement all the special cases the draft talk about
mainly because these special cases are not required and need to be removed
from the draft.

Diff includes both bgpd and regress test.
-- 
:wq Claudio

Index: usr.sbin/bgpd/Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/Makefile,v
retrieving revision 1.37
diff -u -p -r1.37 Makefile
--- usr.sbin/bgpd/Makefile      16 Feb 2021 08:29:16 -0000      1.37
+++ usr.sbin/bgpd/Makefile      18 Nov 2022 10:40:24 -0000
@@ -4,7 +4,7 @@ PROG=   bgpd
 SRCS=  bgpd.c session.c log.c logmsg.c parse.y config.c \
        rde.c rde_rib.c rde_decide.c rde_prefix.c mrt.c kroute.c control.c \
        pfkey.c rde_update.c rde_attr.c rde_community.c printconf.c \
-       rde_filter.c rde_sets.c rde_trie.c pftable.c name2id.c \
+       rde_filter.c rde_sets.c rde_aspa.c rde_trie.c pftable.c name2id.c \
        util.c carp.c timer.c rde_peer.c rtr.c rtr_proto.c
 CFLAGS+= -Wall -I${.CURDIR}
 CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
Index: usr.sbin/bgpd/bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.456
diff -u -p -r1.456 bgpd.h
--- usr.sbin/bgpd/bgpd.h        4 Jan 2023 14:33:30 -0000       1.456
+++ usr.sbin/bgpd/bgpd.h        4 Jan 2023 14:35:07 -0000
@@ -107,6 +107,11 @@
 #define        ROA_VALID               0x2
 #define        ROA_MASK                0x3
 
+#define        ASPA_UNKNOWN            0x00    /* default */
+#define        ASPA_INVALID            0x01
+#define        ASPA_VALID              0x02
+#define        ASPA_NEVER_KNOWN        0x80    /* unknown, check never needed 
*/
+
 /*
  * Limit the number of messages queued in the session engine.
  * The SE will send an IMSG_XOFF messages to the RDE if the high water mark
Index: usr.sbin/bgpd/rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.582
diff -u -p -r1.582 rde.c
--- usr.sbin/bgpd/rde.c 28 Dec 2022 21:30:16 -0000      1.582
+++ usr.sbin/bgpd/rde.c 3 Jan 2023 09:19:34 -0000
@@ -61,6 +61,8 @@ uint8_t                rde_attr_missing(struct rde_as
 int             rde_get_mp_nexthop(u_char *, uint16_t, uint8_t,
                    struct filterstate *);
 void            rde_as4byte_fixup(struct rde_peer *, struct rde_aspath *);
+uint8_t                 rde_aspa_validation(struct rde_peer *, struct 
rde_aspath *,
+                   uint8_t);
 void            rde_reflector(struct rde_peer *, struct rde_aspath *);
 
 void            rde_dump_ctx_new(struct ctl_show_rib_request *, pid_t,
@@ -107,6 +109,7 @@ static struct imsgbuf               *ibuf_rtr;
 static struct imsgbuf          *ibuf_main;
 static struct bgpd_config      *conf, *nconf;
 static struct rde_prefixset     rde_roa, roa_new;
+static struct rde_aspa         *rde_aspa /* , *aspa_new */;
 
 volatile sig_atomic_t   rde_quit = 0;
 struct filter_head     *out_rules, *out_rules_tmp;
@@ -1456,6 +1459,10 @@ rde_update_dispatch(struct rde_peer *pee
                            NULL, 0);
                        goto done;
                }
+#if NOTYET
+               state.aspath.aspa_state = rde_aspa_validation(peer,
+                   &state.aspath, AID_INET);
+#endif
        }
        while (nlri_len > 0) {
                if (peer_has_add_path(peer, AID_INET, CAPA_AP_RECV)) {
@@ -1528,6 +1535,10 @@ rde_update_dispatch(struct rde_peer *pee
                mpp += pos;
                mplen -= pos;
 
+#if NOTYET
+               state.aspath.aspa_state = rde_aspa_validation(peer,
+                   &state.aspath, aid);
+#endif
                while (mplen > 0) {
                        if (peer_has_add_path(peer, aid, CAPA_AP_RECV)) {
                                if (mplen <= sizeof(pathid)) {
@@ -2395,6 +2406,36 @@ rde_as4byte_fixup(struct rde_peer *peer,
                aspath_merge(a, nasp);
 }
 
+
+uint8_t
+rde_aspa_validation(struct rde_peer *peer, struct rde_aspath *asp, uint8_t aid)
+{
+       if (!peer->conf.ebgp)   /* ASPA is only performed on ebgp sessions */
+               return ASPA_NEVER_KNOWN;
+       if (aid != AID_INET && aid != AID_INET6) /* skip uncovered aids */
+               return ASPA_NEVER_KNOWN;
+
+#ifdef MAYBE
+       /*
+        * By default enforce neighbor-as is set for all ebgp sessions.
+        * So if a admin disables this check should we really "reenable"
+        * it here in such a dubious way?
+        * This just fails the ASPA validation for these paths so maybe
+        * this can be helpful. But it is not transparent to the admin.
+        */
+
+       /* skip neighbor-as check for transparent RS sessions */
+       if (peer->conf.role != ROLE_RS_CLIENT) {
+               uint32_t fas;
+
+               fas = aspath_neighbor(asp->aspath);
+               if (peer->conf.remote_as != fas)
+                       return ASPA_INVALID;
+       }
+#endif
+
+       return aspa_validation(rde_aspa, peer->conf.role, asp->aspath, aid);
+}
 
 /*
  * route reflector helper function
Index: usr.sbin/bgpd/rde.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.275
diff -u -p -r1.275 rde.h
--- usr.sbin/bgpd/rde.h 28 Dec 2022 21:30:16 -0000      1.275
+++ usr.sbin/bgpd/rde.h 3 Jan 2023 09:19:34 -0000
@@ -225,6 +225,7 @@ struct rde_aspath {
        uint16_t                         pftableid;     /* pf table id */
        uint8_t                          origin;
        uint8_t                          others_len;
+       uint8_t                          aspa_state;
 };
 
 enum nexthop_state {
@@ -471,7 +472,6 @@ aspath_origin(struct aspath *aspath)
        return (aspath->source_as);
 }
 
-
 /* rde_community.c */
 int    community_match(struct rde_community *, struct community *,
            struct rde_peer *);
@@ -727,5 +727,13 @@ int                 up_dump_withdraws(u_char *, int, s
 int             up_dump_mp_unreach(u_char *, int, struct rde_peer *, uint8_t);
 int             up_dump_attrnlri(u_char *, int, struct rde_peer *);
 int             up_dump_mp_reach(u_char *, int, struct rde_peer *, uint8_t);
+
+/* rde_aspa.c */
+struct rde_aspa        *aspa_table_prep(uint32_t, size_t);
+void            aspa_add_set(struct rde_aspa *, uint32_t, const uint32_t *,
+                   uint32_t, const uint32_t *);
+void            aspa_table_free(struct rde_aspa *);
+uint8_t                 aspa_validation(struct rde_aspa *, enum role, struct 
aspath *,
+                   uint8_t);
 
 #endif /* __RDE_H__ */
Index: usr.sbin/bgpd/rde_aspa.c
===================================================================
RCS file: usr.sbin/bgpd/rde_aspa.c
diff -N usr.sbin/bgpd/rde_aspa.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ usr.sbin/bgpd/rde_aspa.c    22 Dec 2022 13:49:10 -0000
@@ -0,0 +1,442 @@
+/*     $OpenBSD$ */
+
+/*
+ * Copyright (c) 2022 Claudio Jeker <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bgpd.h"
+#include "rde.h"
+
+enum cp_res {
+       UNKNOWN = -1,
+       NOT_PROVIDER = 0,
+       PROVIDER = 1,
+};
+
+struct rde_aspa_set {
+       uint32_t                 as;
+       uint32_t                 num;
+       uint32_t                *pas;
+       uint32_t                *pas_aid;
+       int                      next;
+};
+
+/*
+ * Power of 2 hash table
+ * The nodes are stored in the sets array.
+ * Additonal data for the rde_aspa_set are stored in data.
+ * For lookups only table and mask need to be accessed.
+ */
+struct rde_aspa {
+       struct rde_aspa_set     **table;
+       uint32_t                  mask;
+       uint32_t                  maxset;
+       struct rde_aspa_set      *sets;
+       uint32_t                 *data;
+       size_t                    maxdata;
+       size_t                    curdata;
+       uint32_t                  curset;
+};
+
+struct aspa_state {
+       int     nhops;
+       int     nup_p;
+       int     nup_u;
+       int     nup_np;
+       int     ndown_p;
+       int     ndown_u;
+       int     ndown_np;
+};
+
+/*
+ * Use the final step of MurmurHash3 as a 32bit -> 32bit hash function.
+ */
+static inline uint32_t
+hash(uint32_t h)
+{
+       h ^= h >> 16;
+       h *= 0x85ebca6b;
+       h ^= h >> 13;
+       h *= 0xc2b2ae35;
+       h ^= h >> 16;
+       return h;
+}
+
+/*
+ * Lookup an asnum in the aspa hash table.
+ */
+static struct rde_aspa_set *
+aspa_lookup(struct rde_aspa *ra, uint32_t asnum)
+{
+       struct rde_aspa_set *aspa;
+       uint32_t h;
+
+       h = hash(asnum) & ra->mask;
+       aspa = ra->table[h];
+       if (aspa == NULL)
+               return NULL;
+
+       while (aspa->as < asnum) {
+               if (!aspa->next)
+                       break;
+               aspa++;
+       }
+
+       if (aspa->as == asnum)
+               return aspa;
+       return NULL;
+}
+
+/*
+ * Lookup is there is a customer - provider realtion between cas and pas.
+ * Returns UNKNOWN if cas is not in the ra table or the aid is out of range.
+ * Returns PROVIDER if pas is registered for cas for the specified aid.
+ * Retruns NOT_PROVIDER otherwise.
+ * This function is called very frequently and needs to be fast.
+ */
+static enum cp_res
+aspa_cp_lookup(struct rde_aspa *ra, uint32_t cas, uint32_t pas, uint8_t aid)
+{
+       struct rde_aspa_set *aspa;
+       uint32_t i;
+
+       switch (aid) {
+       case AID_INET:
+               aid = 0x1;
+               break;
+       case AID_INET6:
+               aid = 0x2;
+               break;
+       default:
+               return UNKNOWN;
+       }
+
+       aspa = aspa_lookup(ra, cas);
+       if (aspa == NULL)
+               return UNKNOWN;
+
+       if (aspa->num < 16) {
+               for (i = 0; i < aspa->num; i++) {
+                       if (aspa->pas[i] == pas)
+                               break;
+                       if (aspa->pas[i] > pas)
+                               return NOT_PROVIDER;
+               }
+               if (i == aspa->num)
+                       return NOT_PROVIDER;
+       } else {
+               uint32_t lim, x;
+               for (i = 0, lim = aspa->num; lim != 0; lim /= 2) {
+                       x = lim / 2;
+                       i += x; 
+                       if (aspa->pas[i] == pas) {
+                               break;
+                       } else if (aspa->pas[i] < pas) {
+                               /* move right */
+                               i++;
+                               lim--;
+                       } else {
+                               /* move left */
+                               i -= x;
+                       }
+               }
+               if (lim == 0)
+                       return NOT_PROVIDER;
+       }
+
+       if (aspa->pas_aid == NULL)
+               return PROVIDER;
+       if (aspa->pas_aid[i / 16] & (aid << ((i % 16) * 2)))
+               return PROVIDER;
+       return NOT_PROVIDER;
+}
+
+/*
+ * Calculate the various indexes of an aspath.
+ * The up-ramp starts at the source-as which is the right-most / last element
+ * in the aspath. The down-ramp starts at index 1.
+ * nhops: number of unique hops in the path
+ * nup_p: smallest index after which all aspath hops are provider valid
+ * nup_u: The biggest index of an unknown aspath hop.
+ * nup_np: The biggest index of a not-provider aspath hop.
+ * ndown_p: biggest index before which all aspath hops are provider valid
+ * ndown_u: The smallest index of an unknown aspath hop.
+ * ndown_np: The smallest index of a not-provider aspath hop.
+ * Returns 0 on success and -1 if a AS_SET is encountered.
+ */
+static int
+aspa_check_aspath(struct rde_aspa *ra, struct aspath *a, int check_downramp,
+    uint8_t aid, struct aspa_state *s)
+{
+       uint8_t         *seg;
+       uint32_t         as, prevas = 0;
+       uint16_t         len, seg_size;
+       uint8_t          i, seg_type, seg_len;
+       enum cp_res      r;
+
+       memset(s, 0, sizeof(*s));
+       /* the neighbor-as itself is by definition valid */ 
+       s->ndown_p = 1;
+
+       /*
+        * Walk aspath and validate if necessary both up- and down-ramp.
+        * If and AS_SET is found the result is immediatly ASPA_INVALID.
+        */
+       seg = aspath_dump(a);
+       len = aspath_length(a);
+       for (; len > 0; len -= seg_size, seg += seg_size) {
+               seg_type = seg[0];
+               seg_len = seg[1];
+               seg_size = 2 + sizeof(uint32_t) * seg_len;
+
+               if (seg_type == AS_SET)
+                       return -1;
+
+               for (i = 0; i < seg_len; i++) {
+                       as = aspath_extract(seg, i);
+
+                       if (as == prevas)
+                               continue; /* skip prepends */
+
+                       s->nhops++;
+                       if (prevas != 0) {
+                               if (check_downramp) {
+                                       /*
+                                        * down-ramp check, remember the
+                                        * left-most unknown or not-provider
+                                        * node and the right-most provider node
+                                        * for which all nodes before are valid.
+                                        */
+                                       r = aspa_cp_lookup(ra, prevas, as, aid);
+                                       switch (r) {
+                                       case UNKNOWN:
+                                               if (s->ndown_u == 0)
+                                                       s->ndown_u = s->nhops;
+                                               break;
+                                       case PROVIDER:
+                                               if (s->ndown_p + 1 == s->nhops)
+                                                       s->ndown_p = s->nhops;
+                                               break;
+                                       case NOT_PROVIDER:
+                                               if (s->ndown_np == 0)
+                                                       s->ndown_np = s->nhops;
+                                               break;
+                                       }
+                               }
+                               /*
+                                * up-ramp check, remember the right-most
+                                * unkown and not-provider node and the
+                                * left-most provider node for which all nodes
+                                * after are valid.
+                                * We recorde the nhops value of prevas,
+                                * that's why the use of nhops - 1.
+                                */
+                               r = aspa_cp_lookup(ra, as, prevas, aid);
+                               switch (r) {
+                               case UNKNOWN:
+                                       s->nup_p = 0;
+                                       s->nup_u = s->nhops - 1;
+                                       break;
+                               case PROVIDER:
+                                       if (s->nup_p == 0)
+                                               s->nup_p = s->nhops - 1;
+                                       break;
+                               case NOT_PROVIDER:
+                                       s->nup_p = 0;
+                                       s->nup_np = s->nhops - 1;
+                                       break;
+                               }
+                       }
+                       prevas = as;
+               }
+       }
+
+       /* the source-as itself is by definition valid */ 
+       if (s->nup_p == 0)
+               s->nup_p = s->nhops;
+       return 0;
+}
+
+/*
+ * Validate an aspath against the aspa_set *ra.
+ * Returns ASPA_VALID if the aspath is valid, ASPA_UNKNOWN if the
+ * aspath contains hops with unknown relation and invalid for
+ * empty aspaths, aspath with AS_SET and aspaths that fail validation.
+ */
+uint8_t
+aspa_validation(struct rde_aspa *ra, enum role role, struct aspath *a,
+    uint8_t aid)
+{
+       struct aspa_state       state;
+
+       /* no aspa table, evrything is unknown */
+       if (ra == NULL)
+               return ASPA_UNKNOWN;
+
+       /* empty ASPATHs are always invalid */
+       if (aspath_length(a) == 0)
+               return ASPA_INVALID;
+
+       /* if no role is set, the outcome is unknown */
+       if (role == ROLE_NONE)
+               return ASPA_UNKNOWN;
+               
+       if (aspa_check_aspath(ra, a, role == ROLE_CUSTOMER, aid, &state) == -1)
+               return ASPA_INVALID;
+
+       if (role != ROLE_CUSTOMER) {
+               /*
+                * Just an up-ramp:
+                * if a check returned NOT_PROVIDER then the result is invalid.
+                * if a check returned UNKNOWN then the result is unknown.
+                * else path is valid.
+                */
+               if (state.nup_np != 0)
+                       return ASPA_INVALID;
+               if (state.nup_u != 0)
+                       return ASPA_UNKNOWN;
+               return ASPA_VALID;
+       } else {
+               /*
+                * Both up-ramp and down-ramp:
+                * if nhops <= 2 the result is valid.
+                * if there is less than one AS hop between up-ramp and
+                *   down-ramp then the result is valid.
+                * if not-provider nodes for both ramps exist and they
+                *   do not overlap the path is invalid.
+                * else the path is unknown.
+                */
+               if (state.nhops <= 2)
+                       return ASPA_VALID;
+               if (state.nup_p - state.ndown_p <= 1)
+                       return ASPA_VALID;
+               if (state.nup_np != 0 && state.ndown_np != 0 &&
+                   state.nup_np - state.ndown_np >= 0)
+                       return ASPA_INVALID;
+               return ASPA_UNKNOWN;
+       }
+}
+
+/*
+ * Preallocate all data structures needed for the aspa table.
+ * There are entries number of rde_aspa_sets with data_size bytes of
+ * extra data.
+ */
+struct rde_aspa *
+aspa_table_prep(uint32_t entries, size_t data_size)
+{
+       struct rde_aspa *ra;
+       uint32_t hsize = 1024;
+
+       if (entries == 0)
+               return NULL;
+
+       while (hsize < entries)
+               hsize *= 2;
+
+       if ((ra = calloc(1, sizeof(*ra))) == NULL)
+               fatal("aspa table prep");
+
+       if ((ra->table = calloc(hsize, sizeof(ra->table[0]))) == NULL)
+               fatal("aspa table prep");
+
+       if ((ra->sets = calloc(entries, sizeof(ra->sets[0]))) == NULL)
+               fatal("aspa table prep");
+
+       if ((ra->data = malloc(data_size)) == NULL)
+               fatal("aspa table prep");
+               
+       ra->mask = hsize - 1;
+       ra->maxset = entries;
+       ra->maxdata = data_size / sizeof(ra->data[0]);
+
+       return ra;
+}
+
+/*
+ * Insert a aspa customer/provider set into the hash table.
+ * For hash conflict resulution insertion must happen in reverse order (biggest
+ * customer asnum first). On conflict obejct in the sets array are moved
+ * around so that conflicting elements are direct neighbors.
+ * The per AID information is (if required) stored as 2bits per provider.
+ */
+void
+aspa_add_set(struct rde_aspa *ra, uint32_t cas, const uint32_t *pas,
+    uint32_t pascnt, const uint32_t *pas_aid)
+{
+       struct rde_aspa_set *aspa;
+       uint32_t h, i;
+
+       if (ra->curset >= ra->maxset)
+               fatalx("aspa set overflow");
+
+       h = hash(cas) & ra->mask;
+       aspa = ra->table[h];
+       if (aspa == NULL) {
+               aspa = &ra->sets[ra->curset++];
+       } else {
+               if (aspa->as <= cas)
+                       fatalx("%s: bad order of adds", __func__);
+
+               /* insert before aspa */
+               memmove(aspa + 1, aspa,
+                   (ra->sets + ra->curset - aspa) * sizeof(*aspa));
+               ra->curset++;
+               memset(aspa, 0, sizeof(*aspa));
+               aspa->next = 1;
+
+               /* adjust hashtable after shift of elements */
+               for (i = 0; i <= ra->mask; i++) {
+                       if (ra->table[i] > aspa)
+                               ra->table[i]++;
+               }
+       }
+       aspa->as = cas;
+       ra->table[h] = aspa;
+
+       if (ra->maxdata - ra->curdata < pascnt)
+               fatalx("aspa set data overflow");
+
+       aspa->num = pascnt;
+       aspa->pas = ra->data + ra->curdata;
+       for (i = 0; i < pascnt; i++)
+               ra->data[ra->curdata++] = pas[i];
+
+       /* nobody in there right mind has per afi specific data */
+       if (pas_aid != NULL) {
+               /* 2 bits per entry rounded to next uint32_t */
+               if (ra->maxdata - ra->curdata < (pascnt * 2 + 31) / 32)
+                       fatalx("aspa set data overflow");
+
+               aspa->pas_aid = ra->data + ra->curdata;
+               for (i = 0; i < (pascnt * 2 + 31) / 32; i++)
+                       ra->data[ra->curdata++] = pas_aid[i];
+       }
+}
+
+void
+aspa_table_free(struct rde_aspa *ra)
+{
+       if (ra == NULL)
+               return;
+       free(ra->table);
+       free(ra->sets);
+       free(ra->data);
+       free(ra);
+}
Index: regress/usr.sbin/bgpd/unittests/Makefile
===================================================================
RCS file: /cvs/src/regress/usr.sbin/bgpd/unittests/Makefile,v
retrieving revision 1.9
diff -u -p -r1.9 Makefile
--- regress/usr.sbin/bgpd/unittests/Makefile    7 Sep 2021 11:10:28 -0000       
1.9
+++ regress/usr.sbin/bgpd/unittests/Makefile    12 Dec 2022 10:46:29 -0000
@@ -6,6 +6,7 @@ PROGS += rde_sets_test
 PROGS += rde_trie_test
 PROGS += rde_community_test
 PROGS += rde_decide_test
+PROGS += rde_aspa_test
 
 .for p in ${PROGS}
 REGRESS_TARGETS += run-regress-$p
Index: regress/usr.sbin/bgpd/unittests/rde_aspa_test.c
===================================================================
RCS file: regress/usr.sbin/bgpd/unittests/rde_aspa_test.c
diff -N regress/usr.sbin/bgpd/unittests/rde_aspa_test.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ regress/usr.sbin/bgpd/unittests/rde_aspa_test.c     27 Dec 2022 13:36:04 
-0000
@@ -0,0 +1,692 @@
+/*     $OpenBSD$ */
+
+/*
+ * Copyright (c) 2022 Claudio Jeker <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <err.h>
+#include <stdio.h>
+
+#include "rde_aspa.c"
+
+static struct aspath   *build_aspath(const uint32_t *, uint32_t, int);
+static const char      *print_aspath(const uint32_t *, uint32_t);
+
+static void     reverse_state(struct aspa_state *, struct aspa_state *);
+static void     print_state(struct aspa_state *, struct aspa_state *);
+
+struct aspa_test_set {
+       uint32_t        customeras;
+       const uint32_t  *providers;
+       uint32_t        pascnt;
+       const uint32_t  *afimasks;
+};
+
+struct cp_test {
+       uint32_t        customeras;
+       uint32_t        provideras;
+       uint8_t         aid;
+       enum cp_res     expected_result;
+};
+
+struct aspath_test {
+       const uint32_t          *aspath;
+       uint32_t                 aspathcnt;
+       struct aspa_state        state;
+};
+
+struct aspa_test {
+       const uint32_t  *aspath;
+       uint32_t         aspathcnt;
+       enum role        role;
+       uint8_t          aid;
+       uint8_t          expected_result;
+};
+
+struct aspa_test_set testset[] = {
+       /* test vectors from  github.com/benmaddison/aspa-fuzz */
+       { 1, (const uint32_t []){ 4, 5, 6 }, 3, NULL },
+       { 2, (const uint32_t []){ 10, 11 }, 2, NULL },
+       { 3, (const uint32_t []){ 1, 13, 14 }, 3, NULL },
+       { 4, (const uint32_t []){ 16, 24 }, 2, NULL },
+       { 5, (const uint32_t []){ 1, 17, 25 }, 3, NULL },
+       { 8, (const uint32_t []){ 0 }, 1, NULL },
+       { 9, (const uint32_t []){ 2 }, 1, NULL },
+       { 10, (const uint32_t []){ 0 }, 1, NULL },
+       { 11, (const uint32_t []){ 2 }, 1, NULL },
+       { 12, (const uint32_t []){ 3 }, 1, NULL },
+       { 13, (const uint32_t []){ 0 }, 1, NULL },
+       { 14, (const uint32_t []){ 3, 25 }, 2, NULL },
+       { 15, (const uint32_t []){ 4 }, 1, NULL },
+       { 16, (const uint32_t []){ 4 }, 1, NULL },
+       { 17, (const uint32_t []){ 5 }, 1, NULL },
+       { 18, (const uint32_t []){ 6 }, 1, NULL },
+       { 20, (const uint32_t []){ 19 }, 1, NULL },
+       { 21, (const uint32_t []){ 0 }, 1, NULL },
+       { 23, (const uint32_t []){ 22 }, 1, NULL },
+       { 24, (const uint32_t []){ 0 }, 1, NULL },
+       { 25, (const uint32_t []){ 0 }, 1, NULL },
+       { 26, (const uint32_t []){ 5 }, 1, NULL },
+       { 27, (const uint32_t []){ 14 }, 1, NULL },
+       /* tests to simulate slides-110-sidrops-sriram-aspa-alg-accuracy-01 */
+       { 101, (const uint32_t []){ 102 }, 1, NULL },
+       { 102, (const uint32_t []){ 103, 104, 105 }, 3, NULL },
+       { 103, (const uint32_t []){ 111, 112, 203 }, 3, NULL },
+       /* 104 no ASPA */
+       { 105, (const uint32_t []){ 0 }, 1, NULL },
+
+       /* 111 no ASPA */
+       { 112, (const uint32_t []){ 0 }, 1, NULL },
+       { 113, (const uint32_t []){ 104, 105, 204, 205 }, 4, NULL },
+
+       { 121, (const uint32_t []){ 131, 132, 133 }, 3, NULL },
+       { 123, (const uint32_t []){ 0 }, 1, NULL },
+       { 131, (const uint32_t []){ 121, 122, 123 }, 3, NULL },
+       { 133, (const uint32_t []){ 0 }, 1, NULL },
+       
+
+       { 201, (const uint32_t []){ 202 }, 1, NULL },
+       { 202, (const uint32_t []){ 203, 204, 205 }, 3, NULL },
+       { 203, (const uint32_t []){ 103, 111, 112 }, 3, NULL },
+       /* 204 no ASPA */
+       { 205, (const uint32_t []){ 0 }, 1, NULL },
+
+       /* extra test for big table test */
+       { 65000, (const uint32_t []){
+           3, 5, 10, 15, 20, 21, 22, 23, 24, 25, 
+           30, 35, 40, 45, 50, 51, 52, 53, 54, 55, 
+           60, 65, 70, 75, 80, 81, 82, 83, 87, 90 }, 30, NULL },
+       /* extra test for AFI check */
+       { 196618, (const uint32_t []){ 1, 2, 3, 4 }, 4,
+           (const uint32_t []){ 0x39 }},
+};
+
+struct cp_test cp_testset[] = {
+       { 1, 2, AID_VPN_IPv4, UNKNOWN },
+       { 1, 4, AID_VPN_IPv6, UNKNOWN },
+
+       { 6, 1, AID_INET, UNKNOWN },
+       { 42, 1, AID_INET, UNKNOWN },
+
+       { 1, 2, AID_INET, NOT_PROVIDER },
+       { 1, 3, AID_INET, NOT_PROVIDER },
+       { 1, 7, AID_INET, NOT_PROVIDER },
+       { 5, 2, AID_INET, NOT_PROVIDER },
+       { 5, 16, AID_INET, NOT_PROVIDER },
+       { 5, 18, AID_INET, NOT_PROVIDER },
+       { 5, 24, AID_INET, NOT_PROVIDER },
+       { 5, 26, AID_INET, NOT_PROVIDER },
+       { 8, 2, AID_INET, NOT_PROVIDER },
+       { 9, 5, AID_INET, NOT_PROVIDER },
+       { 27, 13, AID_INET, NOT_PROVIDER },
+       { 27, 15, AID_INET, NOT_PROVIDER },
+
+       { 1, 4, AID_INET, PROVIDER },
+       { 1, 5, AID_INET, PROVIDER },
+       { 1, 6, AID_INET, PROVIDER },
+       { 2, 10, AID_INET, PROVIDER },
+       { 2, 11, AID_INET, PROVIDER },
+       { 9, 2, AID_INET, PROVIDER },
+       { 27, 14, AID_INET, PROVIDER },
+
+       /* per AID tests */
+       { 196618, 1, AID_INET, PROVIDER },
+       { 196618, 1, AID_INET6, NOT_PROVIDER },
+       { 196618, 2, AID_INET, NOT_PROVIDER },
+       { 196618, 2, AID_INET6, PROVIDER },
+       { 196618, 3, AID_INET, PROVIDER },
+       { 196618, 3, AID_INET6, PROVIDER },
+       { 196618, 4, AID_INET, NOT_PROVIDER },
+       { 196618, 4, AID_INET6, NOT_PROVIDER },
+       { 196618, 5, AID_INET, NOT_PROVIDER },
+       { 196618, 5, AID_INET6, NOT_PROVIDER },
+
+       /* big provider set test */
+       { 65000, 1, AID_INET, NOT_PROVIDER },
+       { 65000, 2, AID_INET, NOT_PROVIDER },
+       { 65000, 3, AID_INET, PROVIDER },
+       { 65000, 4, AID_INET, NOT_PROVIDER },
+       { 65000, 5, AID_INET, PROVIDER },
+       { 65000, 15, AID_INET, PROVIDER },
+       { 65000, 19, AID_INET, NOT_PROVIDER },
+       { 65000, 20, AID_INET, PROVIDER },
+       { 65000, 21, AID_INET, PROVIDER },
+       { 65000, 22, AID_INET, PROVIDER },
+       { 65000, 23, AID_INET, PROVIDER },
+       { 65000, 24, AID_INET, PROVIDER },
+       { 65000, 25, AID_INET, PROVIDER },
+       { 65000, 26, AID_INET, NOT_PROVIDER },
+       { 65000, 85, AID_INET, NOT_PROVIDER },
+       { 65000, 86, AID_INET, NOT_PROVIDER },
+       { 65000, 87, AID_INET, PROVIDER },
+       { 65000, 88, AID_INET, NOT_PROVIDER },
+       { 65000, 89, AID_INET, NOT_PROVIDER },
+       { 65000, 90, AID_INET, PROVIDER },
+       { 65000, 91, AID_INET, NOT_PROVIDER },
+       { 65000, 92, AID_INET, NOT_PROVIDER },
+       { 65000, 6666, AID_INET, NOT_PROVIDER },
+};
+
+struct aspath_test     aspath_testset[] = {
+       { (const uint32_t []) { 1 }, 1, { 1, 1, 0, 0, 1, 0, 0 } },
+       { (const uint32_t []) { 7 }, 1, { 1, 1, 0, 0, 1, 0, 0 } },
+       { (const uint32_t []) { 8 }, 1, { 1, 1, 0, 0, 1, 0, 0 } },
+
+       { (const uint32_t []) { 1, 1 }, 2, { 1, 1, 0, 0, 1, 0, 0 } },
+       { (const uint32_t []) { 7, 7 }, 2, { 1, 1, 0, 0, 1, 0, 0 } },
+       { (const uint32_t []) { 8, 8 }, 2, { 1, 1, 0, 0, 1, 0, 0 } },
+
+       { (const uint32_t []) { 1, 1, 1 }, 3, { 1, 1, 0, 0, 1, 0, 0 } },
+       { (const uint32_t []) { 7, 7, 7 }, 3, { 1, 1, 0, 0, 1, 0, 0 } },
+       { (const uint32_t []) { 8, 8, 8 }, 3, { 1, 1, 0, 0, 1, 0, 0 } },
+
+       { (const uint32_t []) { 1, 5 }, 2, { 2, 1, 0, 0, 2, 0, 0 } },
+       { (const uint32_t []) { 1, 1, 5, 5 }, 4, { 2, 1, 0, 0, 2, 0, 0 } },
+       { (const uint32_t []) { 1, 5, 17 }, 3, { 3, 1, 0, 0, 3, 0, 0 } },
+
+       { (const uint32_t []) { 1, 4 }, 2, { 2, 2, 0, 1, 2, 0, 0 } },
+       { (const uint32_t []) { 1, 6 }, 2, { 2, 2, 1, 0, 2, 0, 0 } },
+       { (const uint32_t []) { 1, 17 }, 2, { 2, 2, 0, 1, 1, 0, 2 } },
+
+       { (const uint32_t []) { 42, 43, 44 }, 3, { 3, 3, 2, 0, 1, 2, 0 } },
+
+       { (const uint32_t []) { 42, 1, 5, 17, 44 }, 5,
+            { 5, 5, 4, 1, 1, 2, 5 } },
+
+       /* 1 ?> 6 -? 11 -- 12 -- 13 ?- 19 <? 20 */
+       { (const uint32_t []) { 1, 6, 11, 12, 13, 19, 20 }, 7,
+            { 7, 6, 5, 4, 2, 3, 4 } },
+};
+
+/*
+ * For simplicity the relation between is described as 123 LR 124 where:
+ * R: ? if ASPA(123) is empty
+ *    > if 124 is a provider of 123
+ *    - otherwise (124 is not part of the provider list)
+ * L: ? if ASPA(124) is empty
+ *    > if 123 is a provider of of 124
+ *    - otherwise (123 is not part of the provider list)
+ *
+ * e.g. 1 -> 2 (2 is provider of 1 but 1 is not for 2)
+ *      1 ?> 2 (2 is provider of 1 but 2 has no ASPA set defined)
+ */
+struct aspa_test       aspa_testset[] = {
+       /* empty ASPATH are invalid by default */
+       { (const uint32_t []) { }, 0, ROLE_CUSTOMER, AID_INET, ASPA_INVALID },
+       { (const uint32_t []) { }, 0, ROLE_PROVIDER, AID_INET, ASPA_INVALID },
+       { (const uint32_t []) { }, 0, ROLE_RS, AID_INET, ASPA_INVALID },
+       { (const uint32_t []) { }, 0, ROLE_RS_CLIENT, AID_INET, ASPA_INVALID },
+       { (const uint32_t []) { }, 0, ROLE_PEER, AID_INET, ASPA_INVALID },
+
+       { (const uint32_t []) { 2 }, 1, ROLE_RS_CLIENT, AID_INET, ASPA_VALID },
+       { (const uint32_t []) { 2 }, 1, ROLE_PEER, AID_INET, ASPA_VALID },
+
+       { (const uint32_t []) { 3 }, 1, ROLE_PROVIDER, AID_INET, ASPA_VALID },
+       { (const uint32_t []) { 4 }, 1, ROLE_CUSTOMER, AID_INET, ASPA_VALID },
+       { (const uint32_t []) { 5 }, 1, ROLE_CUSTOMER, AID_INET, ASPA_VALID },
+       { (const uint32_t []) { 6 }, 1, ROLE_CUSTOMER, AID_INET, ASPA_VALID },
+
+       { (const uint32_t []) { 7 }, 1, ROLE_PROVIDER, AID_INET, ASPA_VALID },
+       { (const uint32_t []) { 7 }, 1, ROLE_PEER, AID_INET, ASPA_VALID },
+       { (const uint32_t []) { 7 }, 1, ROLE_RS_CLIENT, AID_INET, ASPA_VALID },
+
+       { (const uint32_t []) { 2, 8 }, 2, ROLE_PEER, AID_INET, ASPA_INVALID },
+       { (const uint32_t []) { 2, 8 }, 2, ROLE_RS_CLIENT, AID_INET,
+           ASPA_INVALID },
+
+       { (const uint32_t []) { 2, 9 }, 2, ROLE_PEER, AID_INET, ASPA_VALID },
+       { (const uint32_t []) { 2, 9 }, 2, ROLE_RS_CLIENT, AID_INET,
+           ASPA_VALID },
+
+       { (const uint32_t []) { 2, 10 }, 2, ROLE_PEER, AID_INET, ASPA_INVALID },
+       { (const uint32_t []) { 2, 10 }, 2, ROLE_RS_CLIENT, AID_INET,
+           ASPA_INVALID },
+
+       { (const uint32_t []) { 2, 11 }, 2, ROLE_PEER, AID_INET, ASPA_VALID },
+       { (const uint32_t []) { 2, 11 }, 2, ROLE_RS_CLIENT, AID_INET,
+           ASPA_VALID },
+
+       { (const uint32_t []) { 3, 8 }, 2, ROLE_PROVIDER, AID_INET,
+           ASPA_INVALID },
+       { (const uint32_t []) { 3, 12 }, 2, ROLE_PROVIDER, AID_INET,
+           ASPA_VALID },
+       { (const uint32_t []) { 3, 13 }, 2, ROLE_PROVIDER, AID_INET,
+           ASPA_INVALID },
+       { (const uint32_t []) { 3, 14 }, 2, ROLE_PROVIDER, AID_INET,
+           ASPA_VALID },
+
+       { (const uint32_t []) { 4, 8 }, 2, ROLE_CUSTOMER, AID_INET,
+           ASPA_VALID },
+       { (const uint32_t []) { 4, 15 }, 2, ROLE_CUSTOMER, AID_INET,
+           ASPA_VALID },
+       { (const uint32_t []) { 4, 16 }, 2, ROLE_CUSTOMER, AID_INET,
+           ASPA_VALID },
+       { (const uint32_t []) { 4, 24 }, 2, ROLE_CUSTOMER, AID_INET,
+           ASPA_VALID },
+
+       { (const uint32_t []) { 5, 8 }, 2, ROLE_CUSTOMER, AID_INET,
+           ASPA_VALID },
+       { (const uint32_t []) { 5, 17 }, 2, ROLE_CUSTOMER, AID_INET,
+           ASPA_VALID },
+       { (const uint32_t []) { 5, 25 }, 2, ROLE_CUSTOMER, AID_INET,
+           ASPA_VALID },
+       { (const uint32_t []) { 5, 26 }, 2, ROLE_CUSTOMER, AID_INET,
+           ASPA_VALID },
+
+       { (const uint32_t []) { 6, 18 }, 2, ROLE_CUSTOMER, AID_INET,
+           ASPA_VALID },
+       { (const uint32_t []) { 6, 19 }, 2, ROLE_CUSTOMER, AID_INET,
+           ASPA_VALID },
+
+       { (const uint32_t []) { 7, 19 }, 2, ROLE_PROVIDER, AID_INET,
+           ASPA_UNKNOWN },
+       { (const uint32_t []) { 7, 19 }, 2, ROLE_PEER, AID_INET,
+           ASPA_UNKNOWN },
+       { (const uint32_t []) { 7, 19 }, 2, ROLE_RS_CLIENT, AID_INET,
+           ASPA_UNKNOWN },
+       { (const uint32_t []) { 7, 21 }, 2, ROLE_PROVIDER, AID_INET,
+           ASPA_INVALID },
+       { (const uint32_t []) { 7, 21 }, 2, ROLE_PEER, AID_INET,
+           ASPA_INVALID },
+       { (const uint32_t []) { 7, 21 }, 2, ROLE_RS_CLIENT, AID_INET,
+           ASPA_INVALID },
+
+       { (const uint32_t []) { 6, 19, 20 }, 3, ROLE_CUSTOMER, AID_INET,
+           ASPA_VALID },
+       { (const uint32_t []) { 20, 19, 6 }, 3, ROLE_CUSTOMER, AID_INET,
+           ASPA_VALID },
+
+       { (const uint32_t []) { 3, 14, 25 }, 3, ROLE_PROVIDER, AID_INET,
+           ASPA_INVALID },
+       { (const uint32_t []) { 3, 14, 19 }, 3, ROLE_PROVIDER, AID_INET,
+           ASPA_UNKNOWN },
+       { (const uint32_t []) { 3, 14, 19 }, 3, ROLE_PEER, AID_INET,
+           ASPA_UNKNOWN },
+       { (const uint32_t []) { 3, 14, 19 }, 3, ROLE_RS_CLIENT, AID_INET,
+           ASPA_UNKNOWN },
+       { (const uint32_t []) { 3, 14, 21 }, 3, ROLE_PROVIDER, AID_INET,
+           ASPA_INVALID },
+       { (const uint32_t []) { 3, 14, 21 }, 3, ROLE_PEER, AID_INET,
+           ASPA_INVALID },
+       { (const uint32_t []) { 3, 14, 21 }, 3, ROLE_RS_CLIENT, AID_INET,
+           ASPA_INVALID },
+       { (const uint32_t []) { 3, 14, 27 }, 3, ROLE_PROVIDER, AID_INET,
+           ASPA_VALID },
+       { (const uint32_t []) { 3, 14, 27 }, 3, ROLE_PEER, AID_INET,
+           ASPA_VALID },
+       { (const uint32_t []) { 3, 14, 27 }, 3, ROLE_RS_CLIENT, AID_INET,
+           ASPA_VALID },
+       
+       { (const uint32_t []) { 7, 19, 22, 21 }, 4, ROLE_PROVIDER, AID_INET,
+           ASPA_INVALID },
+       { (const uint32_t []) { 7, 19, 22, 21 }, 4, ROLE_PEER, AID_INET,
+           ASPA_INVALID },
+       { (const uint32_t []) { 7, 19, 22, 21 }, 4, ROLE_RS_CLIENT, AID_INET,
+           ASPA_INVALID },
+
+       { (const uint32_t []) { 6, 19, 22, 23 }, 4, ROLE_CUSTOMER, AID_INET,
+           ASPA_UNKNOWN },
+
+       { (const uint32_t []) { 1, 5, 17, 13, 3, 14, 27 }, 7, ROLE_CUSTOMER,
+           AID_INET, ASPA_VALID },
+       { (const uint32_t []) { 27, 14, 3, 13, 17, 5, 1 }, 7, ROLE_CUSTOMER,
+           AID_INET, ASPA_VALID },
+
+       { (const uint32_t []) { 27, 14, 3, 6, 7, 19, 17, 5, 1 }, 9,
+           ROLE_CUSTOMER, AID_INET, ASPA_INVALID },
+       { (const uint32_t []) { 27, 14, 3, 7, 19, 6, 1, 5, 17 }, 9,
+           ROLE_CUSTOMER, AID_INET, ASPA_UNKNOWN },
+
+       /* check L < K (ramps overlap) */
+       { (const uint32_t []) { 201, 202, 203, 103, 102, 101 }, 6,
+           ROLE_CUSTOMER, AID_INET, ASPA_VALID },
+       { (const uint32_t []) { 101, 102, 103, 203, 202, 201 }, 6,
+           ROLE_CUSTOMER, AID_INET, ASPA_VALID },
+
+       /* check L == K (ramps touch) 203 ?> 111 <? 103 */
+       { (const uint32_t []) { 201, 202, 203, 111, 103, 102, 101 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_VALID },
+       { (const uint32_t []) { 101, 102, 103, 111, 203, 202, 201 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_VALID },
+       /* check L == K (ramps touch) 203 -> 111 <- 103 */
+       { (const uint32_t []) { 201, 202, 203, 112, 103, 102, 101 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_VALID },
+       { (const uint32_t []) { 101, 102, 103, 112, 203, 202, 201 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_VALID },
+
+       /* check L - K == 1 (204 ?? 104) */
+       { (const uint32_t []) { 201, 202, 204, 104, 102, 101 }, 6,
+           ROLE_CUSTOMER, AID_INET, ASPA_VALID },
+       /* check L - K == 1 (204 -? 105) */
+       { (const uint32_t []) { 201, 202, 204, 105, 102, 101 }, 6,
+           ROLE_CUSTOMER, AID_INET, ASPA_VALID },
+       /* check L - K == 1 (205 ?- 104) */
+       { (const uint32_t []) { 201, 202, 205, 104, 102, 101 }, 6,
+           ROLE_CUSTOMER, AID_INET, ASPA_VALID },
+       /* check L - K == 1 (205 -- 105) */
+       { (const uint32_t []) { 201, 202, 205, 105, 102, 101 }, 6,
+           ROLE_CUSTOMER, AID_INET, ASPA_VALID },
+
+       /* check L - K == 2 invalid cases (205 ?- 111 -? 105) */
+       { (const uint32_t []) { 201, 202, 205, 111, 105, 102, 101 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_INVALID },
+       /* check L - K == 2 invalid cases (205 -- 112 -- 105) */
+       { (const uint32_t []) { 201, 202, 205, 112, 105, 102, 101 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_INVALID },
+       /* check L - K == 2 invalid cases (205 <- 113 -> 105) */
+       { (const uint32_t []) { 201, 202, 205, 113, 105, 102, 101 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_INVALID },
+
+       /* check L - K == 2 unknown cases (205 ?- 111 ?? 104) */
+       { (const uint32_t []) { 201, 202, 205, 111, 104, 102, 101 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_UNKNOWN },
+       /* check L - K == 2 unknown cases (204 ?? 111 -? 105) */
+       { (const uint32_t []) { 201, 202, 204, 111, 105, 102, 101 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_UNKNOWN },
+       /* check L - K == 2 unknown cases (204 ?? 111 ?? 104) */
+       { (const uint32_t []) { 201, 202, 204, 111, 104, 102, 101 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_UNKNOWN },
+       /* check L - K == 2 unknown cases (205 -- 112 ?- 104) */
+       { (const uint32_t []) { 201, 202, 205, 112, 104, 102, 101 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_UNKNOWN },
+       /* check L - K == 2 unknown cases (204 -? 112 -- 105) */
+       { (const uint32_t []) { 201, 202, 204, 112, 105, 102, 101 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_UNKNOWN },
+       /* check L - K == 2 unknown cases (204 -? 112 ?- 104) */
+       { (const uint32_t []) { 201, 202, 204, 112, 104, 102, 101 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_UNKNOWN },
+       /* check L - K == 2 unknown cases (205 <- 113 ?> 104) */
+       { (const uint32_t []) { 201, 202, 205, 113, 104, 102, 101 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_UNKNOWN },
+       /* check L - K == 2 unknown cases (204 <? 113 -> 105) */
+       { (const uint32_t []) { 201, 202, 204, 113, 105, 102, 101 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_UNKNOWN },
+       /* check L - K == 2 unknown cases (204 <? 113 ?> 104) */
+       { (const uint32_t []) { 201, 202, 204, 113, 104, 102, 101 }, 7,
+           ROLE_CUSTOMER, AID_INET, ASPA_UNKNOWN },
+
+#if 0
+       /* check L - K == 3 with all nP cases */
+       { (const uint32_t []) { 201, 202, 204, X, Y, 104, 102, 101 }, 8,
+           ROLE_CUSTOMER, AID_INET, ASPA_UNKNOWN },
+#endif
+};
+
+static struct rde_aspa *
+load_test_set(struct aspa_test_set *testv, uint32_t numentries)
+{
+       struct rde_aspa *aspa;
+       size_t data_size = 0;
+       uint32_t i;
+
+       for (i = 0; i < numentries; i++) {
+               data_size += testv[i].pascnt * sizeof(uint32_t);
+               if (testv[i].afimasks)
+                       data_size += (testv[i].pascnt * 2 + 31) / 8;
+       }
+
+       aspa = aspa_table_prep(numentries, data_size);
+
+       for (i = numentries; i > 0; i--) {
+               aspa_add_set(aspa, testv[i - 1].customeras,
+                   testv[i - 1].providers, testv[i - 1].pascnt,
+                   testv[i - 1].afimasks);
+       }
+
+       return aspa;
+}
+
+
+int
+main(int argc, char **argv)
+{
+       struct rde_aspa *aspa;
+       size_t num_cp = sizeof(cp_testset) / sizeof(cp_testset[0]);
+       size_t num_aspath = sizeof(aspath_testset) / sizeof(aspath_testset[0]);
+       size_t num_aspa = sizeof(aspa_testset) / sizeof(aspa_testset[0]);
+       size_t i;
+       int cp_failed = 0, aspath_failed = 0, aspa_failed = 0;
+
+       /* first test, loading empty aspa table works. */
+       aspa = load_test_set(NULL, 0);
+       assert(aspa == NULL);
+       aspa_table_free(aspa);
+
+       aspa = load_test_set(testset, sizeof(testset) / sizeof(testset[0]));
+       assert(aspa != NULL);
+
+       printf("testing aspa_cp_lookup: ");
+       for (i = 0; i < num_cp; i++) {
+               enum cp_res r;
+               r = aspa_cp_lookup(aspa, cp_testset[i].customeras,
+                   cp_testset[i].provideras, cp_testset[i].aid);
+
+               if (cp_testset[i].expected_result != r) {
+                       printf("failed: cp_testset[%zu]: "
+                           "cas %u pas %u -> %d got %d\n", i, 
+                           cp_testset[i].customeras,
+                           cp_testset[i].provideras,
+                           cp_testset[i].expected_result,
+                           r);
+                       cp_failed = 1;
+               }
+       }
+       if (!cp_failed)
+               printf("OK\n");
+
+       printf("testing aspa_check_aspath: ");
+       for (i = 0; i < num_aspath; i++) {
+               struct aspa_state st, revst;
+               struct aspath *a;
+
+               a = build_aspath(aspath_testset[i].aspath,
+                   aspath_testset[i].aspathcnt, 0);
+               if (aspa_check_aspath(aspa, a, 1, AID_INET, &st) == -1) {
+                       printf("failed: aspath_testset[%zu]: "
+                           "aspath %s got -1\n", i,
+                           print_aspath(aspath_testset[i].aspath,
+                           aspath_testset[i].aspathcnt));
+                       aspath_failed = 1;
+               }
+
+               if (memcmp(&aspath_testset[i].state, &st, sizeof(st))) {
+                       printf("failed: aspath_testset[%zu]: aspath %s "
+                           "bad state", i,
+                           print_aspath(aspath_testset[i].aspath,
+                           aspath_testset[i].aspathcnt));
+                       print_state(&aspath_testset[i].state, &st);
+                       printf("\n");
+                       aspath_failed = 1;
+               }
+               free(a);
+
+               a = build_aspath(aspath_testset[i].aspath,
+                   aspath_testset[i].aspathcnt, 1);
+               if (aspa_check_aspath(aspa, a, 1, AID_INET, &st) == -1) {
+                       printf("failed: reverse aspath_testset[%zu]: "
+                           "aspath %s got -1\n", i,
+                           print_aspath(aspath_testset[i].aspath,
+                           aspath_testset[i].aspathcnt));
+                       aspath_failed = 1;
+               }
+
+               reverse_state(&aspath_testset[i].state, &revst);
+               if (memcmp(&revst, &st, sizeof(st))) {
+                       printf("failed: reverse aspath_testset[%zu]: aspath %s "
+                           "bad state", i,
+                           print_aspath(aspath_testset[i].aspath,
+                           aspath_testset[i].aspathcnt));
+                       print_state(&revst, &st);
+                       printf("\n");
+                       aspath_failed = 1;
+               }
+               free(a);
+       }
+       if (!aspath_failed)
+               printf("OK\n");
+
+       printf("testing aspa_validation: ");
+       for (i = 0; i < num_aspa; i++) {
+               struct aspath *a;
+               uint8_t rv;
+
+               a = build_aspath(aspa_testset[i].aspath,
+                   aspa_testset[i].aspathcnt, 0);
+               rv = aspa_validation(aspa, aspa_testset[i].role, a,
+                   aspa_testset[i].aid);
+
+               if (aspa_testset[i].expected_result != rv) {
+                       printf("failed: aspa_testset[%zu]: aspath %s role %d "
+                           "want %d got %d", i,
+                           print_aspath(aspa_testset[i].aspath,
+                           aspa_testset[i].aspathcnt),
+                           aspa_testset[i].role,
+                           aspa_testset[i].expected_result,
+                           rv);
+                       aspa_failed = 1;
+               }
+
+               free(a);
+       }
+       if (!aspa_failed)
+               printf("OK\n");
+
+       aspa_table_free(aspa);
+
+       return cp_failed | aspath_failed | aspa_failed;
+}
+
+__dead void
+fatalx(const char *emsg, ...)
+{
+       va_list ap;
+       va_start(ap, emsg);
+       verrx(2, emsg, ap);
+}
+
+__dead void
+fatal(const char *emsg, ...)
+{
+       va_list ap;
+       va_start(ap, emsg);
+       verr(2, emsg, ap);
+}
+
+uint32_t
+aspath_extract(const void *seg, int pos)
+{
+       const u_char    *ptr = seg;
+       uint32_t         as;
+
+       /* minimal pos check, return 0 since that is an invalid ASN */
+       if (pos < 0 || pos >= ptr[1])
+               return (0);
+       ptr += 2 + sizeof(uint32_t) * pos;
+       memcpy(&as, ptr, sizeof(uint32_t));
+       return (ntohl(as));
+}
+
+static struct aspath *
+build_aspath(const uint32_t *asns, uint32_t asncnt, int rev)
+{
+       struct aspath *aspath;
+       uint32_t i, idx, as;
+       uint16_t len;
+
+       /* don't mess around with multi segment ASPATHs */
+       if (asncnt >= 255)
+               errx(1, "asncnt too big");
+
+       if (asncnt == 0)
+               len = 0;
+       else
+               len = 2 + sizeof(uint32_t) * asncnt;
+       aspath = malloc(ASPATH_HEADER_SIZE + len);
+        if (aspath == NULL)
+               err(1, NULL);
+
+       aspath->len = len;
+       aspath->ascnt = asncnt; /* lie but nothing cares */
+       aspath->source_as = 0;
+       if (len != 0)
+               aspath->source_as = asns[0];
+       aspath->data[0] = AS_SEQUENCE;
+       aspath->data[1] = asncnt;
+       for (i = 0; i < asncnt; i++) {
+               if (rev)
+                       idx = asncnt - 1 - i;
+               else
+                       idx = i;
+               as = htonl(asns[idx]);
+               memcpy(aspath->data + 2 + sizeof(as) * i, &as,
+                  sizeof(uint32_t));
+       }
+
+       return aspath;
+}
+
+static const char *
+print_aspath(const uint32_t *asns, uint32_t asncnt)
+{
+       static char buf[1024];
+       char b[16];
+       uint32_t i;
+
+       strlcpy(buf, "", sizeof(buf));
+       for (i = 0; i < asncnt; i++) {
+               snprintf(b, sizeof(b), "%d", asns[i]);
+               if (i > 0)
+                       strlcat(buf, " ", sizeof(buf));
+               strlcat(buf, b, sizeof(buf));
+       }
+       return buf;
+}
+
+static void
+reverse_state(struct aspa_state *in, struct aspa_state *rev)
+{
+       memset(rev, 0, sizeof(*rev));
+       rev->nhops = in->nhops;
+       rev->nup_p = in->nhops + 1 - in->ndown_p;
+       if (in->ndown_u != 0)
+               rev->nup_u = in->nhops + 1 - in->ndown_u;
+       if (in->ndown_np != 0)
+               rev->nup_np = in->nhops + 1 - in->ndown_np;
+       rev->ndown_p = in->nhops + 1 - in->nup_p;
+       if (in->nup_u != 0)
+               rev->ndown_u = in->nhops + 1 - in->nup_u;
+       if (in->nup_np != 0)
+               rev->ndown_np = in->nhops + 1 - in->nup_np;
+}
+
+static void
+print_state(struct aspa_state *a, struct aspa_state *b)
+{
+       if (a->nhops != b->nhops)
+               printf(" nhops %d != %d", a->nhops, b->nhops);
+       if (a->nup_p != b->nup_p)
+               printf(" nup_p %d != %d", a->nup_p, b->nup_p);
+       if (a->nup_u != b->nup_u)
+               printf(" nup_u %d != %d", a->nup_u, b->nup_u);
+       if (a->nup_np != b->nup_np)
+               printf(" nup_np %d != %d", a->nup_np, b->nup_np);
+       if (a->ndown_p != b->ndown_p)
+               printf(" ndown_p %d != %d", a->ndown_p, b->ndown_p);
+       if (a->ndown_u != b->ndown_u)
+               printf(" ndown_u %d != %d", a->ndown_u, b->ndown_u);
+       if (a->ndown_np != b->ndown_np)
+               printf(" ndown_np %d != %d", a->ndown_np, b->ndown_np);
+}


Reply via email to