Add plumbing for ASPA support. This implements the parser and part of the
authorclaudio <claudio@openbsd.org>
Fri, 18 Nov 2022 10:17:23 +0000 (10:17 +0000)
committerclaudio <claudio@openbsd.org>
Fri, 18 Nov 2022 10:17:23 +0000 (10:17 +0000)
logic in the rtr process. It does not implement the new RTR messages yet
but it is possible to specify an aspa-set in the config. Also the validation
code in the RDE is missing so this does not do anything.
With this in it will be possible to extend rpki-client to publish an
aspa-set as part of the openbgpd config file.
OK tb@

usr.sbin/bgpd/bgpd.c
usr.sbin/bgpd/bgpd.h
usr.sbin/bgpd/config.c
usr.sbin/bgpd/parse.y
usr.sbin/bgpd/printconf.c
usr.sbin/bgpd/rde.c
usr.sbin/bgpd/rtr.c

index 8632d76..c877884 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: bgpd.c,v 1.254 2022/08/17 15:15:25 claudio Exp $ */
+/*     $OpenBSD: bgpd.c,v 1.255 2022/11/18 10:17:23 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -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,37 @@ 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 (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 70d4738..75a8cc9 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: bgpd.h,v 1.454 2022/09/23 15:50:41 claudio Exp $ */
+/*     $OpenBSD: bgpd.h,v 1.455 2022/11/18 10:17:23 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -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_head *);
 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 d6f2163..28c0e83 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: config.c,v 1.104 2022/08/17 15:15:25 claudio Exp $ */
+/*     $OpenBSD: config.c,v 1.105 2022/11/18 10:17:23 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004, 2005 Henning Brauer <henning@openbsd.org>
@@ -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);
 
@@ -170,6 +171,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)
 {
@@ -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, struct bgpd_config *conf)
        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, struct prefixset_item *b)
 
 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 ce44288..a52ebdf 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.436 2022/09/21 21:12:04 claudio Exp $ */
+/*     $OpenBSD: parse.y,v 1.437 2022/11/18 10:17:23 claudio Exp $ */
 
 /*
  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -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 prefixset_item *, uint32_t, uint8_t,
                    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,8 +231,8 @@ typedef struct {
 %token COMMUNITY EXTCOMMUNITY LARGECOMMUNITY DELETE
 %token MAXCOMMUNITIES MAXEXTCOMMUNITIES MAXLARGECOMMUNITIES
 %token PREFIX PREFIXLEN PREFIXSET
-%token ROASET ORIGINSET OVS EXPIRES
-%token ASSET SOURCEAS TRANSITAS PEERAS MAXASLEN MAXASSEQ
+%token ASPASET ROASET ORIGINSET OVS EXPIRES
+%token ASSET SOURCEAS TRANSITAS PEERAS PROVIDERAS CUSTOMERAS MAXASLEN MAXASSEQ
 %token SET LOCALPREF MED METRIC NEXTHOP REJECT BLACKHOLE NOMODIFY SELF
 %token PREPEND_SELF PREPEND_PEER PFTABLE WEIGHT RTLABEL ORIGIN PRIORITY
 %token ERROR INCLUDE
@@ -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 */
@@ -263,6 +273,7 @@ grammar             : /* empty */
                | grammar as_set '\n'
                | grammar prefixset '\n'
                | grammar roa_set '\n'
+               | grammar aspa_set '\n'
                | grammar origin_set '\n'
                | grammar rtr '\n'
                | grammar rib '\n'
@@ -520,10 +531,8 @@ prefixset_item     : prefix prefixlenop                    {
 
 roa_set                : ROASET '{' optnl              {
                        curroatree = &conf->roa;
-                       noexpires = 0;
                } roa_set_l optnl '}'                   {
                        curroatree = NULL;
-                       noexpires = 1;
                }
                | ROASET '{' optnl '}'          /* nothing */
                ;
@@ -540,6 +549,7 @@ origin_set  : ORIGINSET STRING '{' optnl            {
                        SIMPLEQ_INSERT_TAIL(&conf->originsets, curoset, entry);
                        curoset = NULL;
                        curroatree = NULL;
+                       noexpires = 0;
                }
                | ORIGINSET STRING '{' optnl '}'                {
                        if ((curoset = new_prefix_set($2, 1)) == NULL) {
@@ -586,6 +596,55 @@ roa_set_l  : prefixset_item SOURCEAS as4number_any expires         {
                }
                ;
 
+aspa_set       : ASPASET '{' optnl aspa_set_l optnl '}'
+               | ASPASET '{' optnl '}'
+               ;
+
+aspa_set_l     : aspa_elm
+               | aspa_set_l comma aspa_elm
+               ;
+
+aspa_elm       : CUSTOMERAS as4number expires PROVIDERAS '{' 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 +668,7 @@ rtr         : RTR address   {
 
 rtropt_l       : rtropt
                | rtropt_l optnl rtropt
+               ;
 
 rtropt         : DESCR STRING          {
                        if (strlcpy(currtr->descr, $2,
@@ -3090,12 +3150,14 @@ lookup(char *s)
                { "as-4byte",           AS4BYTE },
                { "as-override",        ASOVERRIDE},
                { "as-set",             ASSET },
+               { "aspa-set",           ASPASET},
                { "blackhole",          BLACKHOLE},
                { "capabilities",       CAPABILITIES},
                { "community",          COMMUNITY},
                { "compare",            COMPARE},
                { "connect-retry",      CONNECTRETRY},
                { "connected",          CONNECTED},
+               { "customer-as",        CUSTOMERAS},
                { "default-route",      DEFAULTROUTE},
                { "delete",             DELETE},
                { "demote",             DEMOTE},
@@ -3173,6 +3235,7 @@ lookup(char *s)
                { "prepend-neighbor",   PREPEND_PEER},
                { "prepend-self",       PREPEND_SELF},
                { "priority",           PRIORITY},
+               { "provider-as",        PROVIDERAS},
                { "qualify",            QUALIFY},
                { "quick",              QUICK},
                { "rd",                 RD},
@@ -4999,3 +5062,57 @@ insert_rtr(struct rtr_config *new)
 
        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;
+               aspa->expires = expires;
+               RB_INSERT(aspa_tree, &conf->aspa, aspa);
+       }
+
+       if (UINT32_MAX - aspa->num <= tas->num) {
+               yyerror("aspa_set overflow");
+               return -1;
+       }
+       num = aspa->num + tas->num;
+       newtas = recallocarray(aspa->tas, aspa->num, num, sizeof(uint32_t));
+       if (newtas == NULL) {
+               yyerror("out of memory");
+               return -1;
+       }
+       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 */
+       if (num > 0) {
+               for (i = num - 1; tas; tas = tas->next, i--) {
+                       newtas[i] = tas->as;
+                       if (tas->aid != AID_UNSPEC)
+                               newtasaid[i] = tas->aid;
+               }
+       }
+
+       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 605ed00..461a3c4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: printconf.c,v 1.159 2022/09/21 21:12:04 claudio Exp $ */
+/*     $OpenBSD: printconf.c,v 1.160 2022/11/18 10:17:23 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -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 *);
@@ -590,6 +591,33 @@ print_roa(struct roa_tree *r)
        printf("\n}\n\n");
 }
 
+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("customer-as %s", log_as(aspa->as));
+               if (aspa->expires != 0)
+                       printf(" expires %lld", (long long)aspa->expires);
+               printf(" provider-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 ", print_af(aspa->tas_aid[i]));
+               }
+               printf("}");
+       }
+       printf("\n}\n\n");
+}
+
 void
 print_rtrs(struct rtr_config_head *rh)
 {
@@ -1096,6 +1124,7 @@ print_config(struct bgpd_config *conf, struct rib_names *rib_l)
        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 1d9730b..b6fd6d3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rde.c,v 1.579 2022/11/07 22:48:35 mbuhl Exp $ */
+/*     $OpenBSD: rde.c,v 1.580 2022/11/18 10:17:23 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -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 0960f24..cd3756f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rtr.c,v 1.8 2022/10/18 09:30:29 job Exp $ */
+/*     $OpenBSD: rtr.c,v 1.9 2022/11/18 10:17:23 claudio Exp $ */
 
 /*
  * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
@@ -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,48 @@ rtr_dispatch_imsg_parent(struct imsgbuf *ibuf)
                                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 (aspa == NULL)
+                               fatalx("unexpected 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 = reallocarray(NULL, 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 (aspa == NULL)
+                               fatalx("unexpected IMSG_RECONF_ASPA_TAS_ID");
+                       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 (aspa == NULL)
+                               fatalx("unexpected 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 +360,15 @@ rtr_dispatch_imsg_parent(struct imsgbuf *ibuf)
                        /* 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,
@@ -347,6 +417,77 @@ rtr_imsg_compose(int type, uint32_t id, pid_t pid, void *data, size_t datalen)
        imsg_compose(ibuf_main, type, id, pid, -1, data, datalen);
 }
 
+/*
+ * 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
+aspa_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++)
+               aspa_set_entry(aspa, mergeset->tas[i], mergeset->tas_aid[i]);
+}
+
 /*
  * Merge all RPKI ROA trees into one as one big union.
  * Simply try to add all roa entries into a new RB tree.
@@ -356,9 +497,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 +515,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);
 }