ASPA sets are the 2nd thing that will be generated out of rpki (and
rpki-client). ASPA is used to further prevent leaks by providing customer
provider attestation (and blocking paths which violate these attestations).

I started to implement this and while this code does no checks yet it is
enough plumbing to parse and "merge" the ASPA sets. Similar to the roa-set
an aspa-set in the config will be parsed and passed to the rtr process
where it will be merged with the data from RTR sessions.
What is missing is all the code needed in the RDE to do the lookups.

A aspa-set is defined like this:
aspa-set {
        source-as 1 transit-as { 5 }
        source-as 2 expires 1668181648 transit-as { 3 4 }
        source-as 5 transit-as { 1 2 allow inet 7 allow inet6 }
}

As usual more than one aspa-set can be used and these sets will all be
merged into one table.

Since this is already a lot of code to fiddle with various rb trees and
doing various realloc games I would like to get this reviewed and
committed before adding a lot more code.

Once this is in rpki-client can be adjusted to produce an aspa-set in the
openbgpd output.
-- 
:wq Claudio

Index: bgpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v
retrieving revision 1.254
diff -u -p -r1.254 bgpd.c
--- bgpd.c      17 Aug 2022 15:15:25 -0000      1.254
+++ bgpd.c      27 Oct 2022 12:36:03 -0000
@@ -27,6 +27,7 @@
 #include <poll.h>
 #include <pwd.h>
 #include <signal.h>
+#include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -594,7 +595,8 @@ send_config(struct bgpd_config *conf)
        struct as_set           *aset;
        struct prefixset        *ps;
        struct prefixset_item   *psi, *npsi;
-       struct roa              *roa, *nroa;
+       struct roa              *roa;
+       struct aspa_set         *aspa;
        struct rtr_config       *rtr;
 
        reconfpending = 3;      /* one per child */
@@ -676,24 +678,38 @@ send_config(struct bgpd_config *conf)
                if (imsg_compose(ibuf_rde, IMSG_RECONF_ORIGIN_SET, 0, 0, -1,
                    ps->name, sizeof(ps->name)) == -1)
                        return (-1);
-               RB_FOREACH_SAFE(roa, roa_tree, &ps->roaitems, nroa) {
-                       RB_REMOVE(roa_tree, &ps->roaitems, roa);
+               RB_FOREACH(roa, roa_tree, &ps->roaitems) {
                        if (imsg_compose(ibuf_rde, IMSG_RECONF_ROA_ITEM, 0, 0,
                            -1, roa, sizeof(*roa)) == -1)
                                return (-1);
-                       free(roa);
                }
+               free_roatree(&ps->roaitems);
                free(ps);
        }
 
-       /* roa table and rtr config are sent to the RTR engine */
-       RB_FOREACH_SAFE(roa, roa_tree, &conf->roa, nroa) {
-               RB_REMOVE(roa_tree, &conf->roa, roa);
+       /* roa table, aspa table and rtr config are sent to the RTR engine */
+       RB_FOREACH(roa, roa_tree, &conf->roa) {
                if (imsg_compose(ibuf_rtr, IMSG_RECONF_ROA_ITEM, 0, 0,
                    -1, roa, sizeof(*roa)) == -1)
                        return (-1);
-               free(roa);
        }
+       free_roatree(&conf->roa);
+       RB_FOREACH(aspa, aspa_tree, &conf->aspa) {
+               if (imsg_compose(ibuf_rtr, IMSG_RECONF_ASPA, 0, 0,
+                   -1, aspa, offsetof(struct aspa_set, tas)) == -1)
+                       return (-1);
+               if (imsg_compose(ibuf_rtr, IMSG_RECONF_ASPA_TAS, 0, 0,
+                   -1, aspa->tas, sizeof(*aspa->tas) * aspa->num) == -1)
+                       return (-1);
+               if (aspa->tas_aid != NULL)
+                       if (imsg_compose(ibuf_rtr, IMSG_RECONF_ASPA_TAS_AID,
+                           0, 0, -1, aspa->tas_aid, aspa->num) == -1)
+                       return (-1);
+               if (imsg_compose(ibuf_rtr, IMSG_RECONF_ASPA_DONE, 0, 0, -1,
+                   NULL, 0) == -1)
+                       return -1;
+       }
+       free_aspatree(&conf->aspa);
        SIMPLEQ_FOREACH(rtr, &conf->rtrs, entry) {
                if (imsg_compose(ibuf_rtr, IMSG_RECONF_RTR_CONFIG, rtr->id,
                    0, -1, rtr->descr, sizeof(rtr->descr)) == -1)
Index: bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.454
diff -u -p -r1.454 bgpd.h
--- bgpd.h      23 Sep 2022 15:50:41 -0000      1.454
+++ bgpd.h      15 Nov 2022 11:22:41 -0000
@@ -263,6 +263,7 @@ struct roa {
 };
 
 RB_HEAD(roa_tree, roa);
+RB_HEAD(aspa_tree, aspa_set);
 
 struct set_table;
 struct as_set;
@@ -284,6 +285,7 @@ struct bgpd_config {
        struct prefixset_head                    prefixsets;
        struct prefixset_head                    originsets;
        struct roa_tree                          roa;
+       struct aspa_tree                         aspa;
        struct rde_prefixset_head                rde_prefixsets;
        struct rde_prefixset_head                rde_originsets;
        struct as_set_head                       as_sets;
@@ -582,6 +584,10 @@ enum imsg_type {
        IMSG_RECONF_ORIGIN_SET,
        IMSG_RECONF_ROA_SET,
        IMSG_RECONF_ROA_ITEM,
+       IMSG_RECONF_ASPA,
+       IMSG_RECONF_ASPA_TAS,
+       IMSG_RECONF_ASPA_TAS_AID,
+       IMSG_RECONF_ASPA_DONE,
        IMSG_RECONF_RTR_CONFIG,
        IMSG_RECONF_DRAIN,
        IMSG_RECONF_DONE,
@@ -1149,6 +1155,15 @@ struct as_set {
        int                              dirty;
 };
 
+struct aspa_set {
+       time_t                           expires;
+       uint32_t                         as;
+       uint32_t                         num;
+       uint32_t                         *tas;
+       uint8_t                          *tas_aid;
+       RB_ENTRY(aspa_set)               entry;
+};
+
 struct l3vpn {
        SIMPLEQ_ENTRY(l3vpn)            entry;
        char                            descr[PEER_DESCR_LEN];
@@ -1270,14 +1285,16 @@ void            free_prefixsets(struct prefixset_h
 void           free_rde_prefixsets(struct rde_prefixset_head *);
 void           free_prefixtree(struct prefixset_tree *);
 void           free_roatree(struct roa_tree *);
+void           free_aspa(struct aspa_set *);
+void           free_aspatree(struct aspa_tree *);
 void           free_rtrs(struct rtr_config_head *);
 void           filterlist_free(struct filter_head *);
 int            host(const char *, struct bgpd_addr *, uint8_t *);
 uint32_t       get_bgpid(void);
 void           expand_networks(struct bgpd_config *, struct network_head *);
 RB_PROTOTYPE(prefixset_tree, prefixset_item, entry, prefixset_cmp);
-int            roa_cmp(struct roa *, struct roa *);
 RB_PROTOTYPE(roa_tree, roa, entry, roa_cmp);
+RB_PROTOTYPE(aspa_tree, aspa_set, entry, aspa_cmp);
 
 /* kroute.c */
 int             kr_init(int *, uint8_t);
Index: config.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/config.c,v
retrieving revision 1.104
diff -u -p -r1.104 config.c
--- config.c    17 Aug 2022 15:15:25 -0000      1.104
+++ config.c    15 Nov 2022 11:22:18 -0000
@@ -59,6 +59,7 @@ new_config(void)
        SIMPLEQ_INIT(&conf->rde_prefixsets);
        SIMPLEQ_INIT(&conf->rde_originsets);
        RB_INIT(&conf->roa);
+       RB_INIT(&conf->aspa);
        SIMPLEQ_INIT(&conf->as_sets);
        SIMPLEQ_INIT(&conf->rtrs);
 
@@ -171,6 +172,27 @@ free_roatree(struct roa_tree *r)
 }
 
 void
+free_aspa(struct aspa_set *aspa)
+{
+       if (aspa == NULL)
+               return;
+       free(aspa->tas);
+       free(aspa->tas_aid);
+       free(aspa);
+}
+
+void
+free_aspatree(struct aspa_tree *a)
+{
+       struct aspa_set *aspa, *naspa;
+
+       RB_FOREACH_SAFE(aspa, aspa_tree, a, naspa) {
+               RB_REMOVE(aspa_tree, a, aspa);
+               free_aspa(aspa);
+       }
+}
+
+void
 free_rtrs(struct rtr_config_head *rh)
 {
        struct rtr_config       *r;
@@ -198,6 +220,7 @@ free_config(struct bgpd_config *conf)
        free_rde_prefixsets(&conf->rde_originsets);
        as_sets_free(&conf->as_sets);
        free_roatree(&conf->roa);
+       free_aspatree(&conf->aspa);
        free_rtrs(&conf->rtrs);
 
        while ((la = TAILQ_FIRST(conf->listen_addrs)) != NULL) {
@@ -267,6 +290,12 @@ merge_config(struct bgpd_config *xconf, 
        RB_ROOT(&xconf->roa) = RB_ROOT(&conf->roa);
        RB_ROOT(&conf->roa) = NULL;
 
+       /* switch the aspa, first remove the old one */
+       free_aspatree(&xconf->aspa);
+       /* then move the RB tree root */
+       RB_ROOT(&xconf->aspa) = RB_ROOT(&conf->aspa);
+       RB_ROOT(&conf->aspa) = NULL;
+
        /* switch the rtr_configs, first remove the old ones */
        free_rtrs(&xconf->rtrs);
        SIMPLEQ_CONCAT(&xconf->rtrs, &conf->rtrs);
@@ -582,7 +611,7 @@ prefixset_cmp(struct prefixset_item *a, 
 
 RB_GENERATE(prefixset_tree, prefixset_item, entry, prefixset_cmp);
 
-int
+static inline int
 roa_cmp(struct roa *a, struct roa *b)
 {
        int i;
@@ -627,3 +656,15 @@ roa_cmp(struct roa *a, struct roa *b)
 }
 
 RB_GENERATE(roa_tree, roa, entry, roa_cmp);
+
+static inline int
+aspa_cmp(struct aspa_set *a, struct aspa_set *b)
+{
+       if (a->as < b->as)
+               return (-1);
+       if (a->as > b->as)
+               return (1);
+       return (0);
+}
+
+RB_GENERATE(aspa_tree, aspa_set, entry, aspa_cmp);
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
retrieving revision 1.436
diff -u -p -r1.436 parse.y
--- parse.y     21 Sep 2022 21:12:04 -0000      1.436
+++ parse.y     3 Nov 2022 17:22:52 -0000
@@ -140,6 +140,13 @@ struct filter_match_l {
        struct filter_prefixset *prefixset;
 } fmopts;
 
+struct aspa_tas_l {
+       struct aspa_tas_l       *next;
+       uint32_t                 as;
+       uint32_t                 num;
+       uint8_t                  aid;
+};
+
 struct peer    *alloc_peer(void);
 struct peer    *new_peer(void);
 struct peer    *new_group(void);
@@ -171,6 +178,7 @@ static void  add_roa_set(struct prefixse
                    time_t);
 static struct rtr_config       *get_rtr(struct bgpd_addr *);
 static int      insert_rtr(struct rtr_config *);
+static int      merge_aspa_set(uint32_t, struct aspa_tas_l *, time_t);
 
 typedef struct {
        union {
@@ -186,6 +194,7 @@ typedef struct {
                struct filter_as_l      *filter_as;
                struct filter_set       *filter_set;
                struct filter_set_head  *filter_set_head;
+               struct aspa_tas_l       *aspa_elm;
                struct {
                        struct bgpd_addr        prefix;
                        uint8_t                 len;
@@ -222,7 +231,7 @@ typedef struct {
 %token COMMUNITY EXTCOMMUNITY LARGECOMMUNITY DELETE
 %token MAXCOMMUNITIES MAXEXTCOMMUNITIES MAXLARGECOMMUNITIES
 %token PREFIX PREFIXLEN PREFIXSET
-%token ROASET ORIGINSET OVS EXPIRES
+%token ASPASET ROASET ORIGINSET OVS EXPIRES
 %token ASSET SOURCEAS TRANSITAS PEERAS MAXASLEN MAXASSEQ
 %token SET LOCALPREF MED METRIC NEXTHOP REJECT BLACKHOLE NOMODIFY SELF
 %token PREPEND_SELF PREPEND_PEER PFTABLE WEIGHT RTLABEL ORIGIN PRIORITY
@@ -254,6 +263,7 @@ typedef struct {
 %type  <v.filter_prefix>       filter_prefix_m
 %type  <v.u8>                  unaryop equalityop binaryop filter_as_type
 %type  <v.encspec>             encspec
+%type  <v.aspa_elm>            aspa_tas aspa_tas_l
 %%
 
 grammar                : /* empty */
@@ -262,6 +272,7 @@ grammar             : /* empty */
                | grammar include '\n'
                | grammar as_set '\n'
                | grammar prefixset '\n'
+               | grammar aspa_set '\n'
                | grammar roa_set '\n'
                | grammar origin_set '\n'
                | grammar rtr '\n'
@@ -586,6 +597,55 @@ roa_set_l  : prefixset_item SOURCEAS as4n
                }
                ;
 
+aspa_set       : ASPASET '{' optnl aspa_set_l optnl '}'
+               | ASPASET '{' optnl '}'
+               ;
+
+aspa_set_l     : aspa_set
+               | aspa_set_l comma aspa_set
+               ;
+
+aspa_set       : SOURCEAS as4number expires TRANSITAS '{' optnl
+                   aspa_tas_l optnl '}' {
+                       int rv;
+                       struct aspa_tas_l *a, *n;
+
+                       rv = merge_aspa_set($2, $7, $3);
+
+                       for (a = $7; a != NULL; a = n) {
+                               n = a->next;
+                               free(a);
+                       }
+
+                       if (rv == -1)
+                               YYERROR;
+               }
+               ;
+
+aspa_tas_l     : aspa_tas                      { $$ = $1; }
+               | aspa_tas_l comma aspa_tas     {
+                       $3->next = $1;
+                       $3->num = $1->num + 1;
+                       $$ = $3;
+               }
+               ;
+
+aspa_tas       : as4number_any {
+                       if (($$ = calloc(1, sizeof(*$$))) == NULL)
+                               fatal(NULL);
+                       $$->as = $1;
+                       $$->aid = AID_UNSPEC;
+                       $$->num = 1;
+               }
+               | as4number_any ALLOW family {
+                       if (($$ = calloc(1, sizeof(*$$))) == NULL)
+                               fatal(NULL);
+                       $$->as = $1;
+                       $$->aid = $3;
+                       $$->num = 1;
+               }
+               ;
+
 rtr            : RTR address   {
                        currtr = get_rtr(&$2);
                        currtr->remote_port = RTR_PORT;
@@ -609,6 +669,7 @@ rtr         : RTR address   {
 
 rtropt_l       : rtropt
                | rtropt_l optnl rtropt
+               ;
 
 rtropt         : DESCR STRING          {
                        if (strlcpy(currtr->descr, $2,
@@ -3090,6 +3151,7 @@ lookup(char *s)
                { "as-4byte",           AS4BYTE },
                { "as-override",        ASOVERRIDE},
                { "as-set",             ASSET },
+               { "aspa-set",           ASPASET},
                { "blackhole",          BLACKHOLE},
                { "capabilities",       CAPABILITIES},
                { "community",          COMMUNITY},
@@ -4996,6 +5058,64 @@ insert_rtr(struct rtr_config *new)
                new->id = ++id;
 
        SIMPLEQ_INSERT_TAIL(&conf->rtrs, currtr, entry);
+
+       return 0;
+}
+
+static int
+merge_aspa_set(uint32_t as, struct aspa_tas_l *tas, time_t expires)
+{
+       struct aspa_set *aspa, needle = { .as = as };
+       uint32_t i, num, *newtas;
+       uint8_t *newtasaid = NULL;
+
+       aspa = RB_FIND(aspa_tree, &conf->aspa, &needle);
+       if (aspa == NULL) {
+               if ((aspa = calloc(1, sizeof(*aspa))) == NULL) {
+                       yyerror("out of memory");
+                       return -1;
+               }
+               aspa->as = as;
+               RB_INSERT(aspa_tree, &conf->aspa, aspa);
+       }
+
+       num = aspa->num + tas->num;
+       newtas = recallocarray(aspa->tas, aspa->num, num, sizeof(uint32_t));
+       if (newtas == NULL) {
+               yyerror("out of memory");
+               return -1;
+       }
+       if (aspa->tas_aid) {
+               newtasaid =  recallocarray(aspa->tas_aid, aspa->num, num, 1);
+               if (newtasaid == NULL) {
+                       free(newtas);
+                       yyerror("out of memory");
+                       return -1;
+               }
+       }
+
+       /* fill starting at the end since the tas list is reversed */
+       for (i = num - 1; tas; tas = tas->next) {
+               newtas[i] = tas->as;
+               if (tas->aid != AFI_UNSPEC) {
+                       if (newtasaid == NULL) {
+                               if ((newtasaid = calloc(num, 1)) == NULL) {
+                                       free(newtas);
+                                       yyerror("out of memory");
+                                       return -1;
+                               }
+                       }
+                       newtasaid[i] = tas->aid;
+               }
+               i--;
+       }
+
+       aspa->num = num;
+       aspa->tas = newtas;
+       aspa->tas_aid = newtasaid;
+       /* take the longest expiry time, same logic as for ROA entries */
+       if (aspa->expires != 0 && expires != 0 && expires > aspa->expires)
+               aspa->expires = expires;
 
        return 0;
 }
Index: printconf.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v
retrieving revision 1.159
diff -u -p -r1.159 printconf.c
--- printconf.c 21 Sep 2022 21:12:04 -0000      1.159
+++ printconf.c 27 Oct 2022 11:11:40 -0000
@@ -42,6 +42,7 @@ void           print_as_sets(struct as_set_head 
 void            print_prefixsets(struct prefixset_head *);
 void            print_originsets(struct prefixset_head *);
 void            print_roa(struct roa_tree *);
+void            print_aspa(struct aspa_tree *);
 void            print_rtrs(struct rtr_config_head *);
 void            print_peer(struct peer_config *, struct bgpd_config *,
                    const char *);
@@ -591,6 +592,33 @@ print_roa(struct roa_tree *r)
 }
 
 void
+print_aspa(struct aspa_tree *a)
+{
+       struct aspa_set *aspa;
+       uint32_t i;
+
+       if (RB_EMPTY(a))
+               return;
+
+       printf("aspa-set {");
+       RB_FOREACH(aspa, aspa_tree, a) {
+               printf("\n\t");
+               printf("source-as %s", log_as(aspa->as));
+               if (aspa->expires != 0)
+                       printf(" expires %lld", (long long)aspa->expires);
+               printf(" transit-as { ");
+               for (i = 0; i < aspa->num; i++) {
+                       printf("%s ", log_as(aspa->tas[i]));
+                       if (aspa->tas_aid != NULL &&
+                           aspa->tas_aid[i] != AID_UNSPEC)
+                               printf("allow %s ", aid2str(aspa->tas_aid[i]));
+               }
+               printf("}");
+       }
+       printf("\n}\n\n");
+}
+
+void
 print_rtrs(struct rtr_config_head *rh)
 {
        struct rtr_config *r;
@@ -1096,6 +1124,7 @@ print_config(struct bgpd_config *conf, s
        print_mainconf(conf);
        print_rtrs(&conf->rtrs);
        print_roa(&conf->roa);
+       print_aspa(&conf->aspa);
        print_as_sets(&conf->as_sets);
        print_prefixsets(&conf->prefixsets);
        print_originsets(&conf->originsets);
Index: rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.579
diff -u -p -r1.579 rde.c
--- rde.c       7 Nov 2022 22:48:35 -0000       1.579
+++ rde.c       9 Nov 2022 14:18:16 -0000
@@ -323,6 +323,11 @@ rde_main(int debug, int verbose)
                close(ibuf_se_ctl->fd);
                free(ibuf_se_ctl);
        }
+       if (ibuf_rtr) {
+               msgbuf_clear(&ibuf_rtr->w);
+               close(ibuf_rtr->fd);
+               free(ibuf_rtr);
+       }
        msgbuf_clear(&ibuf_main->w);
        close(ibuf_main->fd);
        free(ibuf_main);
Index: rtr.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rtr.c,v
retrieving revision 1.8
diff -u -p -r1.8 rtr.c
--- rtr.c       18 Oct 2022 09:30:29 -0000      1.8
+++ rtr.c       16 Nov 2022 11:19:59 -0000
@@ -20,6 +20,7 @@
 #include <poll.h>
 #include <pwd.h>
 #include <signal.h>
+#include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -80,6 +81,24 @@ rtr_expire_roas(time_t now)
        return recalc;
 }
 
+static unsigned int
+rtr_expire_aspa(time_t now)
+{
+       struct aspa_set *aspa, *na;
+       unsigned int recalc = 0;
+
+       RB_FOREACH_SAFE(aspa, aspa_tree, &conf->aspa, na) {
+               if (aspa->expires != 0 && aspa->expires <= now) {
+                       recalc++;
+                       RB_REMOVE(aspa_tree, &conf->aspa, aspa);
+                       free_aspa(aspa);
+               }
+       }
+       if (recalc != 0)
+               log_info("%u aspa-set entries expired", recalc);
+       return recalc;
+}
+
 void
 roa_insert(struct roa_tree *rt, struct roa *in)
 {
@@ -193,6 +212,8 @@ rtr_main(int debug, int verbose)
                            EXPIRE_TIMEOUT);
                        if (rtr_expire_roas(time(NULL)) != 0)
                                rtr_recalc();
+                       if (rtr_expire_aspa(time(NULL)) != 0)
+                               rtr_recalc();
                }
        }
 
@@ -218,10 +239,11 @@ rtr_main(int debug, int verbose)
 static void
 rtr_dispatch_imsg_parent(struct imsgbuf *ibuf)
 {
-       struct imsg     imsg;
-       struct roa      *roa;
-       struct rtr_session *rs;
-       int             n, fd;
+       static struct aspa_set  *aspa;
+       struct imsg              imsg;
+       struct roa              *roa;
+       struct rtr_session      *rs;
+       int                      n, fd;
 
        while (ibuf) {
                if ((n = imsg_get(ibuf, &imsg)) == -1)
@@ -274,6 +296,41 @@ rtr_dispatch_imsg_parent(struct imsgbuf 
                                fatalx("IMSG_RECONF_ROA_ITEM bad len");
                        roa_insert(&nconf->roa, imsg.data);
                        break;
+               case IMSG_RECONF_ASPA:
+                       if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+                           offsetof(struct aspa_set, tas))
+                               fatalx("IMSG_RECONF_ASPA bad len");
+                       if (aspa != NULL)
+                               fatalx("unexpected IMSG_RECONF_ASPA");
+                       if ((aspa = calloc(1, sizeof(*aspa))) == NULL)
+                               fatal("aspa alloc");
+                       memcpy(aspa, imsg.data, offsetof(struct aspa_set, tas));
+                       break;
+               case IMSG_RECONF_ASPA_TAS:
+                       if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+                           aspa->num * sizeof(*aspa->tas))
+                               fatalx("IMSG_RECONF_ASPA_TAS bad len");
+                       aspa->tas = calloc(aspa->num, sizeof(*aspa->tas));
+                       if (aspa->tas == NULL)
+                               fatal("aspa tas alloc");
+                       memcpy(aspa->tas, imsg.data,
+                           aspa->num * sizeof(*aspa->tas));
+                       break;
+               case IMSG_RECONF_ASPA_TAS_AID:
+                       if (imsg.hdr.len - IMSG_HEADER_SIZE != aspa->num)
+                               fatalx("IMSG_RECONF_ASPA_TAS_AID bad len");
+                       aspa->tas_aid = malloc(aspa->num);
+                       if (aspa->tas_aid == NULL)
+                               fatal("aspa tas aid alloc");
+                       memcpy(aspa->tas_aid, imsg.data, aspa->num);
+                       break;
+               case IMSG_RECONF_ASPA_DONE:
+                       if (RB_INSERT(aspa_tree, &nconf->aspa, aspa) != NULL) {
+                               log_warnx("duplicate ASPA set received");
+                               free_aspa(aspa);
+                       }
+                       aspa = NULL;
+                       break;
                case IMSG_RECONF_RTR_CONFIG:
                        if (imsg.hdr.len - IMSG_HEADER_SIZE != PEER_DESCR_LEN)
                                fatalx("IMSG_RECONF_RTR_CONFIG bad len");
@@ -296,9 +353,15 @@ rtr_dispatch_imsg_parent(struct imsgbuf 
                        /* then move the RB tree root */
                        RB_ROOT(&conf->roa) = RB_ROOT(&nconf->roa);
                        RB_ROOT(&nconf->roa) = NULL;
+                       /* switch the aspa tree, first remove the old one */
+                       free_aspatree(&conf->aspa);
+                       /* then move the RB tree root */
+                       RB_ROOT(&conf->aspa) = RB_ROOT(&nconf->aspa);
+                       RB_ROOT(&nconf->aspa) = NULL;
                        /* finally merge the rtr session */
                        rtr_config_merge();
                        rtr_expire_roas(time(NULL));
+                       rtr_expire_aspa(time(NULL));
                        rtr_recalc();
                        log_info("RTR engine reconfigured");
                        imsg_compose(ibuf_main, IMSG_RECONF_DONE, 0, 0,
@@ -348,6 +411,81 @@ rtr_imsg_compose(int type, uint32_t id, 
 }
 
 /*
+ * Add an asnum to the aspa_set. The aspa_set is sorted by asnum.
+ * The aid is altered into a bitmask to simplify the merge of entries
+ * that just use a different aid.
+ */
+static void
+asap_set_entry(struct aspa_set *aspa, uint32_t asnum, uint8_t aid)
+{
+       uint32_t i, num, *newtas;
+       uint8_t *newtasaid;
+
+       switch (aid) {
+       case AID_INET:
+               aid = 0x1;
+               break;
+       case AID_INET6:
+               aid = 0x2;
+               break;
+       case AID_UNSPEC:
+               aid = 0x3;
+               break;
+       default:
+               fatalx("aspa_set bad AID");
+       }
+
+       for (i = 0; i < aspa->num; i++) {
+               if (asnum < aspa->tas[i] || aspa->tas[i] == 0)
+                       break;
+               if (asnum == aspa->tas[i]) {
+                       aspa->tas_aid[i] |= aid;
+                       return;
+               }
+       }
+
+       num = aspa->num + 1;
+       newtas = recallocarray(aspa->tas, aspa->num, num, sizeof(uint32_t));
+       newtasaid = recallocarray(aspa->tas_aid, aspa->num, num, 1);
+       if (newtas == NULL || newtasaid == NULL)
+               fatal("aspa_set merge");
+
+       if (i < aspa->num) {
+               memmove(newtas + i + 1, newtas + i,
+                   (aspa->num - i) * sizeof(uint32_t));
+               memmove(newtasaid + i + 1, newtasaid + i, (aspa->num - i));
+       }
+       newtas[i] = asnum;
+       newtasaid[i] = aid;
+
+       aspa->num = num;
+       aspa->tas = newtas;
+       aspa->tas_aid = newtasaid;
+}
+
+static void
+rtr_aspa_merge_set(struct aspa_tree *a, struct aspa_set *mergeset)
+{
+       struct aspa_set *aspa, needle = { .as = mergeset->as };
+       uint32_t i;
+
+       aspa = RB_FIND(aspa_tree, a, &needle);
+       if (aspa == NULL) {
+               if ((aspa = calloc(1, sizeof(*aspa))) == NULL)
+                       fatal("aspa insert");
+               aspa->as = mergeset->as;
+               RB_INSERT(aspa_tree, a, aspa);
+       }
+
+       for (i = 0; i < mergeset->num; i++) {
+               uint8_t aid = AID_UNSPEC;
+               if (mergeset->tas_aid != NULL)
+                       aid = mergeset->tas_aid[i];
+               asap_set_entry(aspa, mergeset->tas[i], aid);
+       }
+}
+
+/*
  * Merge all RPKI ROA trees into one as one big union.
  * Simply try to add all roa entries into a new RB tree.
  * This could be made a fair bit faster but for now this is good enough.
@@ -356,9 +494,12 @@ void
 rtr_recalc(void)
 {
        struct roa_tree rt;
+       struct aspa_tree at;
        struct roa *roa, *nr;
+       struct aspa_set *aspa;
 
        RB_INIT(&rt);
+       RB_INIT(&at);
 
        RB_FOREACH(roa, roa_tree, &conf->roa)
                roa_insert(&rt, roa);
@@ -371,5 +512,11 @@ rtr_recalc(void)
                    roa, sizeof(*roa));
                free(roa);
        }
+
+       RB_FOREACH(aspa, aspa_tree, &conf->aspa)
+               rtr_aspa_merge_set(&at, aspa);
+
+       free_aspatree(&at);
+
        imsg_compose(ibuf_rde, IMSG_RECONF_DONE, 0, 0, -1, NULL, 0);
 }

Reply via email to