On Wed, Nov 16, 2022 at 12:47:46PM +0100, Claudio Jeker wrote:
> 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 }
> }
Adding this to the end of the default bgpd.conf I get
/tmp/bgpd.conf:134: syntax error, expires not allowed
/tmp/bgpd.conf:136: syntax error
Deleting the expire time, it parses fine. I suspect the duplicate
aspa_set rules in parse.y don't work as intended.
>
> 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.
Generally looks good. The bitmask is a bit of a hack, but I guess we
can live with it.
A few comments and questions below.
> --
> :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_set was already defined a few lines up. Is this legal yacc? As
mentioned above, it doesn't seem to work.
> + 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;
Are these numbers guaranteed to be small enough that we don't overflow
the 32-bit num? Also, it's not clear to me that num > 0 is guaranteed
(which seems to be assumed in the for loop below).
> + newtas = recallocarray(aspa->tas, aspa->num, num, sizeof(uint32_t));
> + if (newtas == NULL) {
> + yyerror("out of memory");
> + return -1;
> + }
> + if (aspa->tas_aid) {
I don't really understand the reason for this conditional. Are aspa
sets with all tas->aid == AFI_UNSPEC common enough to warrant such extra
dances? Doing this recallocarray() unconditionally would allow us to
remove the calloc() in the somewhat tricky loop below.
> + newtasaid = recallocarray(aspa->tas_aid, aspa->num, num, 1);
extra space after =
> + 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) {
I'd move the i-- here:
for (i = num - 1; tas; tas = tas->next, i--) {
> + newtas[i] = tas->as;
> + if (tas->aid != AFI_UNSPEC) {
typo: AID_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");
Worth switching to reallocarray(NULL, ...) to avoid zeroing and immediately
overwriting all of it?
> + 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));
ditto
> + 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);
here you use malloc.
> + 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)
typo: aspa_set_entry()
> +{
> + 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;
This is not yet untangled, right? Or did I miss it?
> + 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));
> + }
Phew. looks correct to me.
> + 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];
Again, I'm not quite sure what allowing mergeset->tas_aid == NULL buys us.
We rely on the fact that AFI_UNSPEC == AID_UNSPEC == 0 when not setting
them in parse.y.
> + 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);
> }
>