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