This diff adds all the plumbing to push the ASPA table from the RTR
process into the RDE. It is still missing important bits but the table
itself should load and `bgpctl show sets` will show what was loaded.

After that the reload logic needs to be added and the filters need to
be extended to check the ASPA validation state.
-- 
:wq Claudio

Index: bgpctl/bgpctl.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v
retrieving revision 1.287
diff -u -p -r1.287 bgpctl.c
--- bgpctl/bgpctl.c     18 Oct 2022 09:30:29 -0000      1.287
+++ bgpctl/bgpctl.c     16 Jan 2023 13:55:09 -0000
@@ -1062,6 +1062,8 @@ const char *
 fmt_set_type(struct ctl_show_set *set)
 {
        switch (set->type) {
+       case ASPA_SET:
+               return "ASPA";
        case ROA_SET:
                return "ROA";
        case PREFIX_SET:
Index: bgpctl/output.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/output.c,v
retrieving revision 1.32
diff -u -p -r1.32 output.c
--- bgpctl/output.c     9 Nov 2022 14:20:11 -0000       1.32
+++ bgpctl/output.c     16 Jan 2023 13:55:57 -0000
@@ -1039,7 +1039,7 @@ show_rib_set(struct ctl_show_set *set)
 {
        char buf[64];
 
-       if (set->type == ASNUM_SET)
+       if (set->type == ASNUM_SET || set->type == ASPA_SET)
                snprintf(buf, sizeof(buf), "%7s %7s %6zu",
                    "-", "-", set->as_cnt);
        else
Index: bgpctl/output_json.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/output_json.c,v
retrieving revision 1.27
diff -u -p -r1.27 output_json.c
--- bgpctl/output_json.c        28 Dec 2022 21:30:15 -0000      1.27
+++ bgpctl/output_json.c        16 Jan 2023 13:56:48 -0000
@@ -981,7 +981,7 @@ json_rib_set(struct ctl_show_set *set)
        json_do_printf("type", "%s", fmt_set_type(set));
        json_do_printf("last_change", "%s", fmt_monotime(set->lastchange));
        json_do_int("last_change_sec", get_monotime(set->lastchange));
-       if (set->type == ASNUM_SET) {
+       if (set->type == ASNUM_SET || set->type == ASPA_SET) {
                json_do_uint("num_ASnum", set->as_cnt);
        } else {
                json_do_uint("num_IPv4", set->v4_cnt);
Index: bgpd/bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.457
diff -u -p -r1.457 bgpd.h
--- bgpd/bgpd.h 11 Jan 2023 13:53:17 -0000      1.457
+++ bgpd/bgpd.h 16 Jan 2023 13:28:53 -0000
@@ -604,6 +604,7 @@ enum imsg_type {
        IMSG_RECONF_ASPA_TAS,
        IMSG_RECONF_ASPA_TAS_AID,
        IMSG_RECONF_ASPA_DONE,
+       IMSG_RECONF_ASPA_PREP,
        IMSG_RECONF_RTR_CONFIG,
        IMSG_RECONF_DRAIN,
        IMSG_RECONF_DONE,
@@ -801,6 +802,7 @@ struct ctl_show_set {
                PREFIX_SET,
                ORIGIN_SET,
                ROA_SET,
+               ASPA_SET,
        }                       type;
 };
 
@@ -1178,6 +1180,11 @@ struct aspa_set {
        uint32_t                         *tas;
        uint8_t                          *tas_aid;
        RB_ENTRY(aspa_set)               entry;
+};
+
+struct aspa_prep {
+       size_t                          datasize;
+       uint32_t                        entries;
 };
 
 struct l3vpn {
Index: bgpd/rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.586
diff -u -p -r1.586 rde.c
--- bgpd/rde.c  16 Jan 2023 10:37:08 -0000      1.586
+++ bgpd/rde.c  17 Jan 2023 10:35:14 -0000
@@ -83,6 +83,7 @@ static void    rde_softreconfig_sync_reeva
 static void     rde_softreconfig_sync_fib(struct rib_entry *, void *);
 static void     rde_softreconfig_sync_done(void *, uint8_t);
 static void     rde_roa_reload(void);
+static void     rde_aspa_reload(void);
 int             rde_update_queue_pending(void);
 void            rde_update_queue_runner(void);
 void            rde_update6_queue_runner(uint8_t);
@@ -109,7 +110,7 @@ static struct imsgbuf               *ibuf_rtr;
 static struct imsgbuf          *ibuf_main;
 static struct bgpd_config      *conf, *nconf;
 static struct rde_prefixset     rde_roa, roa_new;
-static struct rde_aspa         *rde_aspa /* , *aspa_new */;
+static struct rde_aspa         *rde_aspa, *aspa_new;
 
 volatile sig_atomic_t   rde_quit = 0;
 struct filter_head     *out_rules, *out_rules_tmp;
@@ -641,6 +642,15 @@ badnetdel:
                        imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_SET, 0,
                            imsg.hdr.pid, -1, &cset, sizeof(cset));
 
+                       memset(&cset, 0, sizeof(cset));
+                       cset.type = ASPA_SET;
+                       strlcpy(cset.name, "RPKI ASPA", sizeof(cset.name));
+                       aspa_table_stats(rde_aspa, &cset);
+
+                       imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_SET, 0,
+                           imsg.hdr.pid, -1, &cset, sizeof(cset));
+                       
+
                        SIMPLEQ_FOREACH(aset, &conf->as_sets, entry) {
                                memset(&cset, 0, sizeof(cset));
                                cset.type = ASNUM_SET;
@@ -1065,9 +1075,11 @@ rde_dispatch_imsg_parent(struct imsgbuf 
 void
 rde_dispatch_imsg_rtr(struct imsgbuf *ibuf)
 {
-       struct imsg      imsg;
-       struct roa       roa;
-       int              n;
+       static struct aspa_set  *aspa;
+       struct imsg              imsg;
+       struct roa               roa;
+       struct aspa_prep         ap;
+       int                      n;
 
        while (ibuf) {
                if ((n = imsg_get(ibuf, &imsg)) == -1)
@@ -1094,9 +1106,66 @@ rde_dispatch_imsg_rtr(struct imsgbuf *ib
                                    log_addr(&p), roa.prefixlen);
                        }
                        break;
+               case IMSG_RECONF_ASPA_PREP:
+                       if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+                           sizeof(ap))
+                               fatalx("IMSG_RECONF_ASPA_PREP bad len");
+                       if (aspa_new)
+                               fatalx("unexpected IMSG_RECONF_ASPA_PREP");
+                       memcpy(&ap, imsg.data, sizeof(ap));
+                       aspa_new = aspa_table_prep(ap.entries, ap.datasize);
+                       break;
+               case IMSG_RECONF_ASPA:
+                       if (aspa_new == NULL)
+                               fatalx("unexpected IMSG_RECONF_ASPA");
+                       if (aspa != NULL)
+                               fatalx("IMSG_RECONF_ASPA already sent");
+                       if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+                           sizeof(uint32_t) * 2)
+                               fatalx("IMSG_RECONF_ASPA bad len");
+
+                       if ((aspa = calloc(1, sizeof(*aspa))) == NULL)
+                               fatal("IMSG_RECONF_ASPA");
+                       memcpy(&aspa->as, imsg.data, sizeof(aspa->as));
+                       memcpy(&aspa->num, (char *)imsg.data + sizeof(aspa->as),
+                           sizeof(aspa->num));
+                       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(uint32_t))
+                               fatalx("IMSG_RECONF_ASPA_TAS bad len");
+                       aspa->tas = reallocarray(NULL, aspa->num,
+                           sizeof(uint32_t));
+                       if (aspa->tas == NULL)
+                               fatal("IMSG_RECONF_ASPA_TAS");
+                       memcpy(aspa->tas, imsg.data,
+                           aspa->num * sizeof(uint32_t));
+                       break;
+               case IMSG_RECONF_ASPA_TAS_AID:
+                       if (aspa == NULL)
+                               fatalx("unexpected IMSG_RECONF_ASPA_TAS_AID");
+                       if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+                            (aspa->num + 15) / 16)
+                               fatalx("IMSG_RECONF_ASPA_TAS_AID bad len");
+                       aspa->tas_aid = malloc((aspa->num + 15) / 16);
+                       if (aspa->tas_aid == NULL)
+                               fatal("IMSG_RECONF_ASPA_TAS_AID");
+                       memcpy(aspa->tas_aid, imsg.data, (aspa->num + 15) / 16);
+                       break;
+               case IMSG_RECONF_ASPA_DONE:
+                       if (aspa_new == NULL)
+                               fatalx("unexpected IMSG_RECONF_ASPA");
+                       aspa_add_set(aspa_new, aspa->as, aspa->tas,
+                           aspa->num, (void *)aspa->tas_aid);
+                       free_aspa(aspa);
+                       aspa = NULL;
+                       break;
                case IMSG_RECONF_DONE:
                        /* end of update */
                        rde_roa_reload();
+                       rde_aspa_reload();
                        break;
                }
                imsg_free(&imsg);
@@ -3933,6 +4002,7 @@ rde_roa_softreload(struct rib_entry *re,
 }
 
 static int roa_update_pending;
+static int aspa_update_pending;
 
 static void
 rde_roa_softreload_done(void *arg, uint8_t aid)
@@ -3972,6 +4042,32 @@ rde_roa_reload(void)
            rib_byid(RIB_ADJ_IN), rde_roa_softreload,
            rde_roa_softreload_done, NULL) == -1)
                fatal("%s: rib_dump_new", __func__);
+}
+
+static void
+rde_aspa_reload(void)
+{
+       struct rde_aspa *aspa_old;
+
+       if (aspa_update_pending) {
+               log_info("ASPA softreload skipped, old still running");
+               return;
+       }
+
+       aspa_old = rde_aspa;
+       rde_aspa = aspa_new;
+       aspa_new = NULL;
+
+       /* check if aspa changed */
+       if (aspa_table_equal(rde_aspa, aspa_old)) {
+               aspa_table_unchanged(rde_aspa, aspa_old);
+               aspa_table_free(aspa_old);      /* old aspa no longer needed */
+               return;
+       }
+       
+       aspa_table_free(aspa_old);      /* old aspa no longer needed */
+       log_debug("ASPA change: reloading Adj-RIB-In");
+       /* XXX MISSING */
 }
 
 /*
Index: bgpd/rde.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.278
diff -u -p -r1.278 rde.h
--- bgpd/rde.h  12 Jan 2023 17:35:51 -0000      1.278
+++ bgpd/rde.h  17 Jan 2023 10:36:02 -0000
@@ -113,6 +113,8 @@ struct rde_peer {
        uint8_t                          flags;
 };
 
+struct rde_aspa;
+
 #define AS_SET                 1
 #define AS_SEQUENCE            2
 #define AS_CONFED_SEQUENCE     3
@@ -731,11 +733,17 @@ int                up_dump_attrnlri(u_char *, int, st
 int             up_dump_mp_reach(u_char *, int, struct rde_peer *, uint8_t);
 
 /* rde_aspa.c */
+uint8_t                 aspa_validation(struct rde_aspa *, enum role, struct 
aspath *,
+                   uint8_t);
 struct rde_aspa        *aspa_table_prep(uint32_t, size_t);
 void            aspa_add_set(struct rde_aspa *, uint32_t, const uint32_t *,
                    uint32_t, const uint32_t *);
 void            aspa_table_free(struct rde_aspa *);
-uint8_t                 aspa_validation(struct rde_aspa *, enum role, struct 
aspath *,
-                   uint8_t);
+void            aspa_table_stats(const struct rde_aspa *,
+                   struct ctl_show_set *);
+int             aspa_table_equal(const struct rde_aspa *,
+                   const struct rde_aspa *);
+void            aspa_table_unchanged(struct rde_aspa *,
+                   const struct rde_aspa *);
 
 #endif /* __RDE_H__ */
Index: bgpd/rde_aspa.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_aspa.c,v
retrieving revision 1.1
diff -u -p -r1.1 rde_aspa.c
--- bgpd/rde_aspa.c     11 Jan 2023 13:53:17 -0000      1.1
+++ bgpd/rde_aspa.c     17 Jan 2023 11:32:44 -0000
@@ -52,6 +52,7 @@ struct rde_aspa {
        size_t                    maxdata;
        size_t                    curdata;
        uint32_t                  curset;
+       time_t                    lastchange;
 };
 
 struct aspa_state {
@@ -114,14 +115,14 @@ static enum cp_res
 aspa_cp_lookup(struct rde_aspa *ra, uint32_t cas, uint32_t pas, uint8_t aid)
 {
        struct rde_aspa_set *aspa;
-       uint32_t i;
+       uint32_t i, mask;
 
        switch (aid) {
        case AID_INET:
-               aid = 0x1;
+               mask = 0x1;
                break;
        case AID_INET6:
-               aid = 0x2;
+               mask = 0x2;
                break;
        default:
                return UNKNOWN;
@@ -162,7 +163,7 @@ aspa_cp_lookup(struct rde_aspa *ra, uint
 
        if (aspa->pas_aid == NULL)
                return PROVIDER;
-       if (aspa->pas_aid[i / 16] & (aid << ((i % 16) * 2)))
+       if (aspa->pas_aid[i / 16] & (mask << ((i % 16) * 2)))
                return PROVIDER;
        return NOT_PROVIDER;
 }
@@ -336,10 +337,10 @@ aspa_validation(struct rde_aspa *ra, enu
 /*
  * Preallocate all data structures needed for the aspa table.
  * There are entries number of rde_aspa_sets with data_size bytes of
- * extra data.
+ * extra data (used to store SPAS and optional AFI bitmasks).
  */
 struct rde_aspa *
-aspa_table_prep(uint32_t entries, size_t data_size)
+aspa_table_prep(uint32_t entries, size_t datasize)
 {
        struct rde_aspa *ra;
        uint32_t hsize = 1024;
@@ -361,12 +362,13 @@ aspa_table_prep(uint32_t entries, size_t
        if ((ra->sets = calloc(entries, sizeof(ra->sets[0]))) == NULL)
                fatal("aspa table prep");
 
-       if ((ra->data = malloc(data_size)) == NULL)
+       if ((ra->data = malloc(datasize)) == NULL)
                fatal("aspa table prep");
                
        ra->mask = hsize - 1;
        ra->maxset = entries;
-       ra->maxdata = data_size / sizeof(ra->data[0]);
+       ra->maxdata = datasize / sizeof(ra->data[0]);
+       ra->lastchange = getmonotime();
 
        return ra;
 }
@@ -441,4 +443,45 @@ aspa_table_free(struct rde_aspa *ra)
        free(ra->sets);
        free(ra->data);
        free(ra);
+}
+
+void
+aspa_table_stats(const struct rde_aspa *ra, struct ctl_show_set *cset)
+{
+       if (ra == NULL)
+               return;
+       cset->lastchange = ra->lastchange;
+       cset->as_cnt = ra->maxset;
+}
+
+/*
+ * Return true if the two rde_aspa tables are contain the same data.
+ */
+int
+aspa_table_equal(const struct rde_aspa *ra, const struct rde_aspa *rb)
+{
+       uint32_t i;
+
+        /* allow NULL pointers to be passed */
+       if (ra == NULL && rb == NULL)
+               return 1;
+       if (ra == NULL || rb == NULL)
+               return 0;
+
+       if (ra->maxset != rb->maxset ||
+           ra->maxdata != rb->maxdata)
+               return 0;
+       for (i = 0; i < ra->maxset; i++)
+               if (ra->sets[i].as != rb->sets[i].as)
+                       return 0;
+       if (memcmp(ra->data, rb->data, ra->maxdata * sizeof(ra->data[0])) != 0)
+               return 0;
+
+       return 1;
+}
+
+void
+aspa_table_unchanged(struct rde_aspa *ra, const struct rde_aspa *old)
+{
+       ra->lastchange = old->lastchange;
 }
Index: bgpd/rtr.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rtr.c,v
retrieving revision 1.9
diff -u -p -r1.9 rtr.c
--- bgpd/rtr.c  18 Nov 2022 10:17:23 -0000      1.9
+++ bgpd/rtr.c  17 Jan 2023 11:08:01 -0000
@@ -428,25 +428,15 @@ aspa_set_entry(struct aspa_set *aspa, ui
        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");
-       }
+       if (aid != AID_UNSPEC && aid != AID_INET && aid != AID_INET6)
+               fatalx("aspa set with invalid AFI %s", aid2str(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;
+                       if (aspa->tas_aid[i] != aid)
+                               aspa->tas_aid[i] = AID_UNSPEC;
                        return;
                }
        }
@@ -489,6 +479,53 @@ rtr_aspa_merge_set(struct aspa_tree *a, 
 }
 
 /*
+ * Compress aspa_set tas_aid into the bitfield used by the RDE.
+ * Returns the size of tas and tas_aid bitfield required for this aspa_set.
+ * At the same time tas_aid is overwritten with the bitmasks or cleared
+ * if no extra aid masks are needed.
+ */
+static size_t
+rtr_aspa_set_prep(struct aspa_set *aspa)
+{
+       uint32_t i, mask = 0;
+       int needafi = 0;
+       size_t s;
+       
+       s = aspa->num * sizeof(uint32_t);
+       for (i = 0; i < aspa->num; i++) {
+               switch (aspa->tas_aid[i]) {
+               case AID_INET:
+                       needafi = 1;
+                       mask |= 0x1 << ((i % 16) * 2);
+                       break;
+               case AID_INET6:
+                       needafi = 1;
+                       mask |= 0x2 << ((i % 16) * 2);
+                       break;
+               default:
+                       mask |= 0x3 << ((i % 16) * 2);
+                       break;
+               }
+               if (i % 16 == 15) {
+                       memcpy(aspa->tas_aid + (i / 16) * sizeof(mask), &mask,
+                           sizeof(mask));
+                       mask = 0;
+               }
+       }
+
+       if (!needafi) {
+               free(aspa->tas_aid);
+               aspa->tas_aid = NULL;
+       } else {
+               memcpy(aspa->tas_aid + (aspa->num / 16) * sizeof(mask), &mask,
+                   sizeof(mask));
+               s += (aspa->num + 15) / 16;
+       }
+
+       return s;
+}
+
+/*
  * 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.
@@ -500,6 +537,7 @@ rtr_recalc(void)
        struct aspa_tree at;
        struct roa *roa, *nr;
        struct aspa_set *aspa;
+       struct aspa_prep ap = { 0 };
 
        RB_INIT(&rt);
        RB_INIT(&at);
@@ -510,14 +548,37 @@ rtr_recalc(void)
 
        imsg_compose(ibuf_rde, IMSG_RECONF_ROA_SET, 0, 0, -1, NULL, 0);
        RB_FOREACH_SAFE(roa, roa_tree, &rt, nr) {
-               RB_REMOVE(roa_tree, &rt, roa);
                imsg_compose(ibuf_rde, IMSG_RECONF_ROA_ITEM, 0, 0, -1,
                    roa, sizeof(*roa));
-               free(roa);
        }
+       free_roatree(&rt);
 
        RB_FOREACH(aspa, aspa_tree, &conf->aspa)
                rtr_aspa_merge_set(&at, aspa);
+
+       RB_FOREACH(aspa, aspa_tree, &at) {
+               ap.datasize += rtr_aspa_set_prep(aspa);
+               ap.entries++;
+       }
+
+       imsg_compose(ibuf_rde, IMSG_RECONF_ASPA_PREP, 0, 0, -1,
+           &ap, sizeof(ap));
+
+       RB_FOREACH(aspa, aspa_tree, &at) {
+               uint32_t        as[2];
+               as[0] = aspa->as;
+               as[1] = aspa->num;
+
+               imsg_compose(ibuf_rde, IMSG_RECONF_ASPA, 0, 0, -1,
+                   &as, sizeof(as));
+               imsg_compose(ibuf_rde, IMSG_RECONF_ASPA_TAS, 0, 0, -1,
+                   aspa->tas, aspa->num * sizeof(*aspa->tas));
+               if (aspa->tas_aid)
+                       imsg_compose(ibuf_rde, IMSG_RECONF_ASPA_TAS, 0, 0, -1,
+                           aspa->tas_aid, (aspa->num + 15) / 16);
+               imsg_compose(ibuf_rde, IMSG_RECONF_ASPA_DONE, 0, 0, -1,
+                   NULL, 0);
+       }
 
        free_aspatree(&at);
 

Reply via email to