Dear all, To simplify the concept of RPKI ASPAs, the sidrops@ group came to consensus on removing the notion of 'afiLimit'. This means that going forward, ASPA will be AFI-agnostic.
Advantages to operators are that by creating just one ASPA they'll be prepared for a IPv4+IPv6 dual-stack future (no need to update the ASPA when IPv6 BGP sessions are added at a later point in time), and for BGP implementers the cachability of the AS_PATH attribute is restored to its former glory. New ASPA profile definition is pending review here: https://github.com/QratorLabs/ASPA/pull/15/files A test object reachable via RIPE NCC TAL is available here: rsync://chloe.sobornost.net/rpki/RIPE-nljobsnijders/5m80fwYws_3FiFD7JiQjAqZ1RYQ.asa Feedback? Kind regards, Job Index: aspa.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/aspa.c,v retrieving revision 1.18 diff -u -p -r1.18 aspa.c --- aspa.c 7 Jun 2023 10:46:34 -0000 1.18 +++ aspa.c 7 Jun 2023 12:15:08 -0000 @@ -47,32 +47,15 @@ extern ASN1_OBJECT *aspa_oid; */ typedef struct { - ASN1_INTEGER *providerASID; - ASN1_OCTET_STRING *afiLimit; -} ProviderAS; - -DECLARE_STACK_OF(ProviderAS); - -#ifndef DEFINE_STACK_OF -#define sk_ProviderAS_num(sk) SKM_sk_num(ProviderAS, (sk)) -#define sk_ProviderAS_value(sk, i) SKM_sk_value(ProviderAS, (sk), (i)) -#endif - -ASN1_SEQUENCE(ProviderAS) = { - ASN1_SIMPLE(ProviderAS, providerASID, ASN1_INTEGER), - ASN1_OPT(ProviderAS, afiLimit, ASN1_OCTET_STRING), -} ASN1_SEQUENCE_END(ProviderAS); - -typedef struct { ASN1_INTEGER *version; ASN1_INTEGER *customerASID; - STACK_OF(ProviderAS) *providers; + STACK_OF(ASN1_INTEGER) *providers; } ASProviderAttestation; ASN1_SEQUENCE(ASProviderAttestation) = { ASN1_EXP_OPT(ASProviderAttestation, version, ASN1_INTEGER, 0), ASN1_SIMPLE(ASProviderAttestation, customerASID, ASN1_INTEGER), - ASN1_SEQUENCE_OF(ASProviderAttestation, providers, ProviderAS), + ASN1_SEQUENCE_OF(ASProviderAttestation, providers, ASN1_INTEGER), } ASN1_SEQUENCE_END(ASProviderAttestation); DECLARE_ASN1_FUNCTIONS(ASProviderAttestation); @@ -83,13 +66,13 @@ IMPLEMENT_ASN1_FUNCTIONS(ASProviderAttes * Return zero on failure, non-zero on success. */ static int -aspa_parse_providers(struct parse *p, const STACK_OF(ProviderAS) *providers) +aspa_parse_providers(struct parse *p, const STACK_OF(ASN1_INTEGER) *providers) { - ProviderAS *pa; - struct aspa_provider provider; + const ASN1_INTEGER *pa; + uint32_t provider; size_t providersz, i; - if ((providersz = sk_ProviderAS_num(providers)) == 0) { + if ((providersz = sk_ASN1_INTEGER_num(providers)) == 0) { warnx("%s: ASPA: ProviderASSet needs at least one entry", p->fn); return 0; @@ -106,39 +89,33 @@ aspa_parse_providers(struct parse *p, co err(1, NULL); for (i = 0; i < providersz; i++) { - pa = sk_ProviderAS_value(providers, i); + pa = sk_ASN1_INTEGER_value(providers, i); memset(&provider, 0, sizeof(provider)); - if (!as_id_parse(pa->providerASID, &provider.as)) { + if (!as_id_parse(pa, &provider)) { warnx("%s: ASPA: malformed ProviderAS", p->fn); return 0; } - if (p->res->custasid == provider.as) { + if (p->res->custasid == provider) { warnx("%s: ASPA: CustomerASID can't also be Provider", p->fn); return 0; } if (i > 0) { - if (p->res->providers[i - 1].as > provider.as) { + if (p->res->providers[i - 1] > provider) { warnx("%s: ASPA: invalid ProviderASSet order", p->fn); return 0; } - if (p->res->providers[i - 1].as == provider.as) { + if (p->res->providers[i - 1] == provider) { warnx("%s: ASPA: duplicate ProviderAS", p->fn); return 0; } } - if (pa->afiLimit != NULL && !ip_addr_afi_parse(p->fn, - pa->afiLimit, &provider.afi)) { - warnx("%s: ASPA: invalid afiLimit", p->fn); - return 0; - } - p->res->providers[p->res->providersz++] = provider; } @@ -161,7 +138,7 @@ aspa_parse_econtent(const unsigned char goto out; } - if (!valid_econtent_version(p->fn, aspa->version, 0)) + if (!valid_econtent_version(p->fn, aspa->version, 1)) goto out; if (!as_id_parse(aspa->customerASID, &p->res->custasid)) { @@ -314,8 +291,7 @@ aspa_read(struct ibuf *b) io_read_buf(b, &p->expires, sizeof(p->expires)); io_read_buf(b, &p->providersz, sizeof(size_t)); - if ((p->providers = calloc(p->providersz, - sizeof(struct aspa_provider))) == NULL) + if ((p->providers = calloc(p->providersz, sizeof(uint32_t))) == NULL) err(1, NULL); io_read_buf(b, p->providers, p->providersz * sizeof(p->providers[0])); @@ -328,12 +304,12 @@ aspa_read(struct ibuf *b) } /* - * Insert a new aspa_provider at index idx in the struct vap v. + * Insert a new uint32_t at index idx in the struct vap v. * All elements in the provider array from idx are moved up by one * to make space for the new element. */ static void -insert_vap(struct vap *v, uint32_t idx, struct aspa_provider *p) +insert_vap(struct vap *v, uint32_t idx, uint32_t *p) { if (idx < v->providersz) memmove(v->providers + idx + 1, v->providers + idx, @@ -391,21 +367,15 @@ aspa_insert_vaps(struct vap_tree *tree, */ for (i = 0, j = 0; i < aspa->providersz; ) { if (j == v->providersz || - aspa->providers[i].as < v->providers[j].as) { + aspa->providers[i] < v->providers[j]) { /* merge provider from aspa into v */ repo_stat_inc(rp, v->talid, RTYPE_ASPA, - STYPE_BOTH + aspa->providers[i].afi); + STYPE_BOTH + aspa->providers[i]); insert_vap(v, j, &aspa->providers[i]); i++; - } else if (aspa->providers[i].as == v->providers[j].as) { - /* duplicate provider, merge afi */ - if (v->providers[j].afi != aspa->providers[i].afi) { - repo_stat_inc(rp, v->talid, RTYPE_ASPA, - STYPE_BOTH + aspa->providers[i].afi); - v->providers[j].afi = 0; - } + } else if (aspa->providers[i] == v->providers[j]) i++; - } + if (j < v->providersz) j++; } Index: extern.h =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/extern.h,v retrieving revision 1.184 diff -u -p -r1.184 extern.h --- extern.h 7 Jun 2023 10:46:34 -0000 1.184 +++ extern.h 7 Jun 2023 12:15:09 -0000 @@ -351,11 +351,6 @@ struct gbr { int talid; /* TAL the GBR is chained up to */ }; -struct aspa_provider { - uint32_t as; - enum afi afi; -}; - /* * A single ASPA record */ @@ -367,7 +362,7 @@ struct aspa { char *sia; /* SIA signedObject */ char *ski; /* SKI */ uint32_t custasid; /* the customerASID */ - struct aspa_provider *providers; /* the providers */ + uint32_t *providers; /* the providers */ size_t providersz; /* number of providers */ time_t signtime; /* CMS signing-time attribute */ time_t notbefore; /* EE cert's Not Before */ @@ -382,7 +377,7 @@ struct aspa { struct vap { RB_ENTRY(vap) entry; uint32_t custasid; - struct aspa_provider *providers; + uint32_t *providers; size_t providersz; time_t expires; int talid; Index: output-bgpd.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/output-bgpd.c,v retrieving revision 1.27 diff -u -p -r1.27 output-bgpd.c --- output-bgpd.c 19 Apr 2023 19:26:26 -0000 1.27 +++ output-bgpd.c 7 Jun 2023 12:15:09 -0000 @@ -63,18 +63,8 @@ output_bgpd(FILE *out, struct vrp_tree * (long long)vap->expires) < 0) return -1; for (i = 0; i < vap->providersz; i++) { - if (fprintf(out, "%u", vap->providers[i].as) < 0) + if (fprintf(out, "%u", vap->providers[i]) < 0) return -1; - switch (vap->providers[i].afi) { - case AFI_IPV4: - if (fprintf(out, " inet") < 0) - return -1; - break; - case AFI_IPV6: - if (fprintf(out, " inet6") < 0) - return -1; - break; - } if (i + 1 < vap->providersz) if (fprintf(out, ", ") < 0) return -1; Index: output-json.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/output-json.c,v retrieving revision 1.39 diff -u -p -r1.39 output-json.c --- output-json.c 5 Jun 2023 14:19:13 -0000 1.39 +++ output-json.c 7 Jun 2023 12:15:09 -0000 @@ -82,24 +82,18 @@ outputheader_json(struct stats *st) } static void -print_vap(struct vap *v, enum afi afi) +print_vap(struct vap *v) { size_t i; - int found = 0; json_do_object("aspa", 1); json_do_int("customer_asid", v->custasid); json_do_int("expires", v->expires); json_do_array("providers"); - for (i = 0; i < v->providersz; i++) { - if (v->providers[i].afi != 0 && v->providers[i].afi != afi) - continue; - found = 1; - json_do_int("provider", v->providers[i].as); - } - if (!found) - json_do_int("provider", 0); + for (i = 0; i < v->providersz; i++) + json_do_int("provider", v->providers[i]); + json_do_end(); } @@ -108,18 +102,9 @@ output_aspa(struct vap_tree *vaps) { struct vap *v; - json_do_object("provider_authorizations", 0); - - json_do_array("ipv4"); - RB_FOREACH(v, vap_tree, vaps) { - print_vap(v, AFI_IPV4); - } - json_do_end(); - - json_do_array("ipv6"); - RB_FOREACH(v, vap_tree, vaps) { - print_vap(v, AFI_IPV6); - } + json_do_array("provider_authorizations"); + RB_FOREACH(v, vap_tree, vaps) + print_vap(v); json_do_end(); } Index: print.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/print.c,v retrieving revision 1.40 diff -u -p -r1.40 print.c --- print.c 5 Jun 2023 14:19:13 -0000 1.40 +++ print.c 7 Jun 2023 12:15:09 -0000 @@ -613,59 +613,23 @@ rsc_print(const X509 *x, const struct rs } static void -aspa_provider(uint32_t as, enum afi afi) +aspa_provider(uint32_t as) { if (outformats & FORMAT_JSON) { json_do_object("aspa", 1); json_do_uint("asid", as); - if (afi == AFI_IPV4) - json_do_string("afi_limit", "ipv4"); - if (afi == AFI_IPV6) - json_do_string("afi_limit", "ipv6"); json_do_end(); } else { printf("AS: %u", as); - if (afi == AFI_IPV4) - printf(" (IPv4 only)"); - if (afi == AFI_IPV6) - printf(" (IPv6 only)"); printf("\n"); } } -static void -aspa_providers(const struct aspa *a) -{ - size_t i; - int hasv4 = 0, hasv6 = 0; - - for (i = 0; i < a->providersz; i++) { - if ((outformats & FORMAT_JSON) == 0 && i > 0) - printf("%26s", ""); - aspa_provider(a->providers[i].as, a->providers[i].afi); - - switch (a->providers[i].afi) { - case AFI_IPV4: - hasv4 = 1; - break; - case AFI_IPV6: - hasv6 = 1; - break; - default: - hasv4 = hasv6 = 1; - break; - } - } - - if (!hasv4) - aspa_provider(0, AFI_IPV4); - if (!hasv6) - aspa_provider(0, AFI_IPV6); -} - void aspa_print(const X509 *x, const struct aspa *p) { + size_t i; + if (outformats & FORMAT_JSON) { json_do_string("type", "aspa"); json_do_string("ski", pretty_key_id(p->ski)); @@ -697,7 +661,11 @@ aspa_print(const X509 *x, const struct a printf("Provider set: "); } - aspa_providers(p); + for (i = 0; i < p->providersz; i++) { + if ((outformats & FORMAT_JSON) == 0 && i > 0) + printf("%26s", ""); + aspa_provider(p->providers[i]); + } if (outformats & FORMAT_JSON) json_do_end();