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);
>  }
> 

Reply via email to