From: Alberto Leiva Popper Date: Fri, 23 Jan 2026 21:49:18 +0000 (-0600) Subject: Implement ASPA X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fheads%2Faspa;p=thirdparty%2FFORT-validator.git Implement ASPA --- diff --git a/src/Makefile.am b/src/Makefile.am index 215e29ca..54c08952 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -42,6 +42,7 @@ fort_SOURCES += asn1/oid.h asn1/oid.c fort_SOURCES += asn1/signed_data.h asn1/signed_data.c fort_SOURCES += types/address.h types/address.c +fort_SOURCES += types/aspa.h types/aspa.c fort_SOURCES += types/bio_seq.c types/bio_seq.h fort_SOURCES += types/delta.c types/delta.h fort_SOURCES += types/router_key.c types/router_key.h @@ -77,6 +78,7 @@ fort_SOURCES += http/http.h http/http.c fort_SOURCES += incidence/incidence.h incidence/incidence.c +fort_SOURCES += object/aspa.h object/aspa.c fort_SOURCES += object/bgpsec.h object/bgpsec.c fort_SOURCES += object/certificate.h object/certificate.c fort_SOURCES += object/crl.h object/crl.c diff --git a/src/asn1/asn1c/ASProviderAttestation.c b/src/asn1/asn1c/ASProviderAttestation.c new file mode 100644 index 00000000..ce435fd5 --- /dev/null +++ b/src/asn1/asn1c/ASProviderAttestation.c @@ -0,0 +1,94 @@ +/* + * Generated by asn1c-0.9.29 (http://lionet.info/asn1c) + * From ASN.1 module "RPKI-ASPA-2023" + * found in "aspa.asn1" + * `asn1c -Werror -fcompound-names -fwide-types -D asn1/asn1c -no-gen-PER -no-gen-example` + */ + +#include "asn1/asn1c/ASProviderAttestation.h" + +static int asn_DFL_2_cmp_0(const void *sptr) { + const INTEGER_t *st = sptr; + + if(!st) { + return -1; /* No value is not a default value */ + } + + /* Test default value 0 */ + long value; + if(asn_INTEGER2long(st, &value)) + return -1; + return (value != 0); +} +static int asn_DFL_2_set_0(void **sptr) { + INTEGER_t *st = *sptr; + + if(!st) { + st = (*sptr = CALLOC(1, sizeof(*st))); + if(!st) return -1; + } + + /* Install default value 0 */ + return asn_long2INTEGER(st, 0); +} +static asn_TYPE_member_t asn_MBR_ASProviderAttestation_1[] = { + { ATF_POINTER, 1, offsetof(struct ASProviderAttestation, version), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + +1, /* EXPLICIT tag at current level */ + &asn_DEF_INTEGER, + 0, + { 0, 0, 0 }, + &asn_DFL_2_cmp_0, /* Compare DEFAULT 0 */ + &asn_DFL_2_set_0, /* Set DEFAULT 0 */ + "version" + }, + { ATF_NOFLAGS, 0, offsetof(struct ASProviderAttestation, customerASID), + (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)), + 0, + &asn_DEF_ASId, + 0, + { 0, 0, 0 }, + 0, 0, /* No default value */ + "customerASID" + }, + { ATF_NOFLAGS, 0, offsetof(struct ASProviderAttestation, providers), + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)), + 0, + &asn_DEF_ProviderASSet, + 0, + { 0, 0, 0 }, + 0, 0, /* No default value */ + "providers" + }, +}; +static const ber_tlv_tag_t asn_DEF_ASProviderAttestation_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_ASProviderAttestation_tag2el_1[] = { + { (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)), 1, 0, 0 }, /* customerASID */ + { (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)), 2, 0, 0 }, /* providers */ + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 } /* version */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_ASProviderAttestation_specs_1 = { + sizeof(struct ASProviderAttestation), + offsetof(struct ASProviderAttestation, _asn_ctx), + asn_MAP_ASProviderAttestation_tag2el_1, + 3, /* Count of tags in the map */ + -1, /* First extension addition */ +}; +asn_TYPE_descriptor_t asn_DEF_ASProviderAttestation = { + "ASProviderAttestation", + "ASProviderAttestation", + &asn_OP_SEQUENCE, + asn_DEF_ASProviderAttestation_tags_1, + sizeof(asn_DEF_ASProviderAttestation_tags_1) + /sizeof(asn_DEF_ASProviderAttestation_tags_1[0]), /* 1 */ + asn_DEF_ASProviderAttestation_tags_1, /* Same as above */ + sizeof(asn_DEF_ASProviderAttestation_tags_1) + /sizeof(asn_DEF_ASProviderAttestation_tags_1[0]), /* 1 */ + { 0, 0, SEQUENCE_constraint }, + asn_MBR_ASProviderAttestation_1, + 3, /* Elements count */ + &asn_SPC_ASProviderAttestation_specs_1 /* Additional specs */ +}; + diff --git a/src/asn1/asn1c/ASProviderAttestation.h b/src/asn1/asn1c/ASProviderAttestation.h new file mode 100644 index 00000000..2bcae026 --- /dev/null +++ b/src/asn1/asn1c/ASProviderAttestation.h @@ -0,0 +1,39 @@ +/* + * Generated by asn1c-0.9.29 (http://lionet.info/asn1c) + * From ASN.1 module "RPKI-ASPA-2023" + * found in "aspa.asn1" + * `asn1c -Werror -fcompound-names -fwide-types -D asn1/asn1c -no-gen-PER -no-gen-example` + */ + +#ifndef _ASProviderAttestation_H_ +#define _ASProviderAttestation_H_ + +/* Including external dependencies */ +#include "asn1/asn1c/INTEGER.h" +#include "asn1/asn1c/ASId.h" +#include "asn1/asn1c/ProviderASSet.h" +#include "asn1/asn1c/constr_SEQUENCE.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ASProviderAttestation */ +typedef struct ASProviderAttestation { + INTEGER_t *version /* DEFAULT 0 */; + ASId_t customerASID; + ProviderASSet_t providers; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} ASProviderAttestation_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_ASProviderAttestation; + +#ifdef __cplusplus +} +#endif + +#endif /* _ASProviderAttestation_H_ */ +#include "asn1/asn1c/asn_internal.h" diff --git a/src/asn1/asn1c/Makefile.include b/src/asn1/asn1c/Makefile.include index 9b602c27..e15cc619 100644 --- a/src/asn1/asn1c/Makefile.include +++ b/src/asn1/asn1c/Makefile.include @@ -8,6 +8,8 @@ ASN_MODULE_SRCS= \ asn1/asn1c/ASIdentifiers.c \ asn1/asn1c/ASIdentifierChoice.c \ asn1/asn1c/ASIdOrRange.c \ + asn1/asn1c/ASProviderAttestation.c \ + asn1/asn1c/ProviderASSet.c \ asn1/asn1c/ASRange.c \ asn1/asn1c/ASId.c \ asn1/asn1c/AttributeType.c \ @@ -73,6 +75,8 @@ ASN_MODULE_HDRS= \ asn1/asn1c/ASIdentifiers.h \ asn1/asn1c/ASIdentifierChoice.h \ asn1/asn1c/ASIdOrRange.h \ + asn1/asn1c/ASProviderAttestation.h \ + asn1/asn1c/ProviderASSet.h \ asn1/asn1c/ASRange.h \ asn1/asn1c/ASId.h \ asn1/asn1c/AttributeType.h \ diff --git a/src/asn1/asn1c/ProviderASSet.c b/src/asn1/asn1c/ProviderASSet.c new file mode 100644 index 00000000..5074d24a --- /dev/null +++ b/src/asn1/asn1c/ProviderASSet.c @@ -0,0 +1,44 @@ +/* + * Generated by asn1c-0.9.29 (http://lionet.info/asn1c) + * From ASN.1 module "RPKI-ASPA-2023" + * found in "aspa.asn1" + * `asn1c -Werror -fcompound-names -fwide-types -D asn1/asn1c -no-gen-PER -no-gen-example` + */ + +#include "asn1/asn1c/ProviderASSet.h" + +asn_TYPE_member_t asn_MBR_ProviderASSet_1[] = { + { ATF_POINTER, 0, 0, + (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)), + 0, + &asn_DEF_ASId, + 0, + { 0, 0, 0 }, + 0, 0, /* No default value */ + "" + }, +}; +static const ber_tlv_tag_t asn_DEF_ProviderASSet_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +asn_SET_OF_specifics_t asn_SPC_ProviderASSet_specs_1 = { + sizeof(struct ProviderASSet), + offsetof(struct ProviderASSet, _asn_ctx), + 0, /* XER encoding is XMLDelimitedItemList */ +}; +asn_TYPE_descriptor_t asn_DEF_ProviderASSet = { + "ProviderASSet", + "ProviderASSet", + &asn_OP_SEQUENCE_OF, + asn_DEF_ProviderASSet_tags_1, + sizeof(asn_DEF_ProviderASSet_tags_1) + /sizeof(asn_DEF_ProviderASSet_tags_1[0]), /* 1 */ + asn_DEF_ProviderASSet_tags_1, /* Same as above */ + sizeof(asn_DEF_ProviderASSet_tags_1) + /sizeof(asn_DEF_ProviderASSet_tags_1[0]), /* 1 */ + { NULL, NULL, SEQUENCE_OF_constraint }, + asn_MBR_ProviderASSet_1, + 1, /* Single element */ + &asn_SPC_ProviderASSet_specs_1 /* Additional specs */ +}; + diff --git a/src/asn1/asn1c/ProviderASSet.h b/src/asn1/asn1c/ProviderASSet.h new file mode 100644 index 00000000..85219337 --- /dev/null +++ b/src/asn1/asn1c/ProviderASSet.h @@ -0,0 +1,38 @@ +/* + * Generated by asn1c-0.9.29 (http://lionet.info/asn1c) + * From ASN.1 module "RPKI-ASPA-2023" + * found in "aspa.asn1" + * `asn1c -Werror -fcompound-names -fwide-types -D asn1/asn1c -no-gen-PER -no-gen-example` + */ + +#ifndef _ProviderASSet_H_ +#define _ProviderASSet_H_ + +/* Including external dependencies */ +#include "asn1/asn1c/ASId.h" +#include "asn1/asn1c/asn_SEQUENCE_OF.h" +#include "asn1/asn1c/constr_SEQUENCE_OF.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ProviderASSet */ +typedef struct ProviderASSet { + A_SEQUENCE_OF(ASId_t) list; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} ProviderASSet_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_ProviderASSet; +extern asn_SET_OF_specifics_t asn_SPC_ProviderASSet_specs_1; +extern asn_TYPE_member_t asn_MBR_ProviderASSet_1[1]; + +#ifdef __cplusplus +} +#endif + +#endif /* _ProviderASSet_H_ */ +#include "asn1/asn1c/asn_internal.h" diff --git a/src/asn1/oid.h b/src/asn1/oid.h index 46420512..b57e60fa 100644 --- a/src/asn1/oid.h +++ b/src/asn1/oid.h @@ -35,6 +35,7 @@ typedef asn_oid_arc_t OID[]; #define OID_ROA { 1, 2, 840, 113549, 1, 9, 16, 1, 24 } #define OID_MANIFEST { 1, 2, 840, 113549, 1, 9, 16, 1, 26 } #define OID_GHOSTBUSTERS { 1, 2, 840, 113549, 1, 9, 16, 1, 35 } +#define OID_ASPA { 1, 2, 840, 113549, 1, 9, 16, 1, 49 } int oid2arcs(OBJECT_IDENTIFIER_t *, struct oid_arcs *); diff --git a/src/asn1/signed_data.c b/src/asn1/signed_data.c index 40f7e6f4..1b59ce4d 100644 --- a/src/asn1/signed_data.c +++ b/src/asn1/signed_data.c @@ -16,8 +16,10 @@ static const OID oid_mda = OID_MESSAGE_DIGEST_ATTR; static const OID oid_sta = OID_SIGNING_TIME_ATTR; void -eecert_init(struct ee_cert *ee, STACK_OF(X509_CRL) *crls, bool force_inherit) +eecert_init(struct ee_cert *ee, enum ee_type type, STACK_OF(X509_CRL) *crls, + bool force_inherit) { + ee->type = type; ee->res = resources_create(RPKI_POLICY_RFC6484, force_inherit); ee->crls = crls; memset(&ee->refs, 0, sizeof(ee->refs)); @@ -101,7 +103,7 @@ handle_sdata_certificate(ANY_t *cert_encoded, struct ee_cert *ee, goto end2; resources_set_policy(ee->res, policy); - error = certificate_get_resources(cert, ee->res, CERTYPE_EE); + error = certificate_get_resources(cert, ee->res, CERTYPE_EE, ee->type); if (error) goto end2; diff --git a/src/asn1/signed_data.h b/src/asn1/signed_data.h index 81b5f105..889b7fe0 100644 --- a/src/asn1/signed_data.h +++ b/src/asn1/signed_data.h @@ -7,7 +7,16 @@ #include "certificate_refs.h" #include "resource.h" +enum ee_type { + EET_ROA = 1, + EET_ASPA, + EET_MFT, + EET_GBR, +}; + struct ee_cert { + enum ee_type type; + /** CRL that might or might not revoke the EE certificate. */ STACK_OF(X509_CRL) *crls; /** A copy of the resources carried by the EE certificate. */ @@ -19,7 +28,7 @@ struct ee_cert { struct certificate_refs refs; }; -void eecert_init(struct ee_cert *, STACK_OF(X509_CRL) *, bool); +void eecert_init(struct ee_cert *, enum ee_type, STACK_OF(X509_CRL) *, bool); void eecert_cleanup(struct ee_cert *); int signed_data_decode(ANY_t *, struct SignedData **); diff --git a/src/cert_stack.c b/src/cert_stack.c index 8f21a070..936270cc 100644 --- a/src/cert_stack.c +++ b/src/cert_stack.c @@ -248,7 +248,7 @@ init_resources(X509 *x509, enum rpki_policy policy, enum cert_type type, result = resources_create(policy, false); - error = certificate_get_resources(x509, result, type); + error = certificate_get_resources(x509, result, type, 0); if (error) goto fail; diff --git a/src/config.c b/src/config.c index 25a5c767..06aaaf6e 100644 --- a/src/config.c +++ b/src/config.c @@ -168,6 +168,10 @@ struct rpki_config { uint32_t facility; } validation_log; + struct { + unsigned int max_providers; /* per customer */ + } aspa; + struct { /** File where the validated ROAs will be stored */ char *roa; @@ -715,6 +719,17 @@ static const struct option_field options[] = { .doc = "Print ANSI color codes", }, + /* ASPA */ + { + .id = 15000, + .name = "aspa.max-providers", + .type = >_uint, + .offset = offsetof(struct rpki_config, aspa.max_providers), + .doc = "Maximum allowed providers per customer", + .min = 0, + .max = 16380u, + }, + /* Incidences */ { .id = 7001, @@ -948,9 +963,9 @@ set_default_values(void) "--contimeout=20", "--max-size=20MB", "--timeout=15", - "--include=*/", "--include=*.cer", "--include=*.crl", - "--include=*.gbr", "--include=*.mft", "--include=*.roa", - "--exclude=*", + "--include=*/", "--include=*.cer", "--include=*.roa", + "--include=*.asa", "--include=*.mft", "--include=*.crl", + "--include=*.gbr", "--exclude=*", "$REMOTE", "$LOCAL", }; @@ -1570,3 +1585,9 @@ free_rpki_config(void) option->type->free(get_rpki_config_field(option)); free(rpki_config.payload); } + +unsigned int +config_get_max_aspa_providers(void) +{ + return rpki_config.aspa.max_providers; +} diff --git a/src/config.h b/src/config.h index ee21f0d5..9110b1d6 100644 --- a/src/config.h +++ b/src/config.h @@ -61,6 +61,7 @@ unsigned int config_get_asn1_decode_max_stack(void); unsigned int config_get_thread_pool_server_max(void); enum file_type config_get_file_type(void); char const *config_get_payload(void); +unsigned int config_get_max_aspa_providers(void); /* Logging getters */ bool config_get_op_log_enabled(void); diff --git a/src/object/aspa.c b/src/object/aspa.c new file mode 100644 index 00000000..104889d0 --- /dev/null +++ b/src/object/aspa.c @@ -0,0 +1,192 @@ +#include "object/aspa.h" + +#include "asn1/asn1c/ASProviderAttestation.h" +#include "asn1/decode.h" +#include "asn1/oid.h" +#include "asn1/signed_data.h" +#include "config.h" +#include "log.h" +#include "object/signed_object.h" +#include "thread_var.h" + +#define ASID_MAX UINT32_MAX + +static int +decode_aspa(struct signed_object *sobj, struct ASProviderAttestation **result) +{ + return asn1_decode_octet_string( + sobj->sdata->encapContentInfo.eContent, + &asn_DEF_ASProviderAttestation, + (void **) result, + true + ); +} + +static int +validate_version(INTEGER_t *version) +{ + long primitive; + + if (version == NULL) + return pr_val_err("Version number is NULL."); + if (asn_INTEGER2long(version, &primitive) < 0) + return pr_val_err("Version number %s", strerror(errno)); + if (primitive != 1) + return pr_val_err("Version number is not 1: %ld", primitive); + + return 0; +} + +static int +parse_asid(char const *what, ASId_t *asid, uint32_t *result) +{ + unsigned long primitive; + + if (asid == NULL) + return pr_val_err("%s is NULL.", what); + if (asn_INTEGER2ulong(asid, &primitive) < 0) + return pr_val_err("%s %s", what, strerror(errno)); + if (primitive > ASID_MAX) + return pr_val_err("%s out of range. (0-%u)", what, ASID_MAX); + + *result = primitive; + return 0; +} + +static int +parse_customer(ASId_t *asid, struct resources *parent, uint32_t *result) +{ + int error; + + error = parse_asid("customerASID", asid, result); + if (error) + return error; + + if (!resources_matches_asn(parent, *result)) + return pr_val_err( + "EE certificate's ASN extension does not exactly match customerASID %u.", + *result); + + return 0; +} + +static int +parse_providers(ProviderASSet_t *set, struct aspa *aspa) +{ + uint32_t *providers; + unsigned int limit; + int i; + int error; + + aspa->providers.asids = NULL; + aspa->providers.count = 0; + + if (set == NULL) + return pr_val_err("Providers set is NULL."); + + limit = config_get_max_aspa_providers(); + if (set->list.count > limit) + return pr_val_err("Too many providers: %d > %u", + set->list.count, limit); + + providers = pcalloc(set->list.count, sizeof(uint32_t)); + for (i = 0; i < set->list.count; i++) { + error = parse_asid("Provider", set->list.array[i], &providers[i]); + if (error) + goto cancel; + + if (providers[i] == aspa->customer) { + error = pr_val_err("The Providers list contains the customer's ASID (%u).", + aspa->customer); + goto cancel; + } + if (i != 0 && providers[i - 1] >= providers[i]) { + error = pr_val_err("The Provider ASIDs are not listed in ascending order."); + goto cancel; + } + } + + aspa->providers.asids = providers; + aspa->providers.count = set->list.count; + return 0; + +cancel: free(providers); + return error; +} + +static int +__handle_aspa(struct ASProviderAttestation *asn1, struct resources *parent) +{ + struct aspa *aspa; + int error; + + error = validate_version(asn1->version); + if (error) + return error; + + aspa = pzalloc(sizeof(struct aspa)); + aspa->refs = 1; + + error = parse_customer(&asn1->customerASID, parent, &aspa->customer); + if (error) + goto end; + + error = parse_providers(&asn1->providers, aspa); + if (error) + goto end; + + error = vhandler_handle_aspa(aspa); + +end: aspa_refput(aspa); + return error; +} + +int +aspa_traverse(struct rpki_uri *uri, struct rpp *pp) +{ + static OID oid = OID_ASPA; + struct oid_arcs arcs = OID2ARCS("aspa", oid); + struct signed_object sobj; + struct ee_cert ee; + struct ASProviderAttestation *aspa; + STACK_OF(X509_CRL) *crl; + int error; + + /* Prepare */ + pr_val_debug("ASPA '%s' {", uri_val_get_printable(uri)); + fnstack_push_uri(uri); + + /* Decode */ + error = signed_object_decode(&sobj, uri); + if (error) + goto revert_log; + error = decode_aspa(&sobj, &aspa); + if (error) + goto revert_sobj; + + /* Prepare validation arguments */ + error = rpp_crl(pp, &crl); + if (error) + goto revert_roa; + eecert_init(&ee, EET_ASPA, crl, false); + + /* Validate and handle everything */ + error = signed_object_validate(&sobj, &arcs, &ee); + if (error) + goto revert_args; + error = __handle_aspa(aspa, ee.res); + if (error) + goto revert_args; + error = refs_validate_ee(&ee.refs, pp, uri); + +revert_args: + eecert_cleanup(&ee); +revert_roa: + ASN_STRUCT_FREE(asn_DEF_ASProviderAttestation, aspa); +revert_sobj: + signed_object_cleanup(&sobj); +revert_log: + fnstack_pop(); + pr_val_debug("}"); + return error; +} diff --git a/src/object/aspa.h b/src/object/aspa.h new file mode 100644 index 00000000..6e613cc6 --- /dev/null +++ b/src/object/aspa.h @@ -0,0 +1,8 @@ +#ifndef SRC_OBJECT_ASPA_H_ +#define SRC_OBJECT_ASPA_H_ + +#include "rpp.h" + +int aspa_traverse(struct rpki_uri *, struct rpp *); + +#endif /* SRC_OBJECT_ASPA_H_ */ diff --git a/src/object/bgpsec.c b/src/object/bgpsec.c index 8f5a45d6..2af60bd3 100644 --- a/src/object/bgpsec.c +++ b/src/object/bgpsec.c @@ -44,7 +44,7 @@ handle_bgpsec(X509 *cert, struct resources *parent_resources, struct rpp *pp) resources = resources_create(policy, false); if (resources == NULL) goto revert_ski; - error = certificate_get_resources(cert, resources, CERTYPE_BGPSEC); + error = certificate_get_resources(cert, resources, CERTYPE_BGPSEC, 0); if (error) goto revert_resources; diff --git a/src/object/certificate.c b/src/object/certificate.c index ac5108ee..931e7917 100644 --- a/src/object/certificate.c +++ b/src/object/certificate.c @@ -1139,6 +1139,9 @@ handle_ip_extension(X509_EXTENSION *ext, struct resources *resources) int i; int error; + if (!X509_EXTENSION_get_critical(ext)) + return pr_val_err("IP extension is not marked critical."); + string = X509_EXTENSION_get_data(ext); error = asn1_decode(string->data, string->length, &asn_DEF_IPAddrBlocks, (void **) &blocks, true); @@ -1199,95 +1202,92 @@ handle_asn_extension(X509_EXTENSION *ext, struct resources *resources, return error; } -static int -__certificate_get_resources(X509 *cert, struct resources *resources, - int addr_nid, int asn_nid, int bad_addr_nid, int bad_asn_nid, - char const *policy_rfc, char const *bad_ext_rfc, bool allow_asn_inherit) +/** + * Copies the resources from @cert to @resources. + */ +int +certificate_get_resources(X509 *cert, struct resources *resources, + enum cert_type type, enum ee_type eet) { + int allowed_ip_nid = NID_undef; + int allowed_as_nid = NID_undef; + + int nid_ip1 = NID_sbgp_ipAddrBlock; + int nid_ip2 = nid_ipAddrBlocksv2(); + int nid_as1 = NID_sbgp_autonomousSysNum; + int nid_as2 = nid_autonomousSysIdsv2(); + X509_EXTENSION *ext; - int nid; - int i; + int extnid; + int e; int error; - bool ip_ext_found = false; - bool asn_ext_found = false; - /* Reference: X509_get_ext_d2i */ - /* rfc6487#section-2 */ + switch (type) { + case CERTYPE_TA: + case CERTYPE_CA: + case CERTYPE_BGPSEC: + switch (resources_get_policy(resources)) { + case RPKI_POLICY_RFC6484: + allowed_ip_nid = nid_ip1; + allowed_as_nid = nid_as1; + break; + case RPKI_POLICY_RFC8360: + allowed_ip_nid = nid_ip2; + allowed_as_nid = nid_as2; + } + break; + case CERTYPE_EE: + switch (eet) { + case EET_ROA: + switch (resources_get_policy(resources)) { + case RPKI_POLICY_RFC6484: + allowed_ip_nid = nid_ip1; break; + case RPKI_POLICY_RFC8360: + allowed_ip_nid = nid_ip2; + } + break; + case EET_ASPA: + switch (resources_get_policy(resources)) { + case RPKI_POLICY_RFC6484: + allowed_as_nid = nid_as1; break; + case RPKI_POLICY_RFC8360: + allowed_as_nid = nid_as2; + } + break; + case EET_MFT: + case EET_GBR: + break; + } + break; + } - for (i = 0; i < X509_get_ext_count(cert); i++) { - ext = X509_get_ext(cert, i); - nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext)); + for (e = 0; e < X509_get_ext_count(cert); e++) { + ext = X509_get_ext(cert, e); + extnid = OBJ_obj2nid(X509_EXTENSION_get_object(ext)); - if (nid == addr_nid) { - if (ip_ext_found) - return pr_val_err("Multiple IP extensions found."); - if (!X509_EXTENSION_get_critical(ext)) - return pr_val_err("The IP extension is not marked as critical."); + if (extnid == nid_ip1 || extnid == nid_ip2) { + if (extnid != allowed_ip_nid) + return pr_val_err("Found an unexpected IP Resources extension."); - pr_val_debug("IP {"); error = handle_ip_extension(ext, resources); - pr_val_debug("}"); - ip_ext_found = true; - if (error) return error; + allowed_ip_nid = NID_undef; - } else if (nid == asn_nid) { - if (asn_ext_found) - return pr_val_err("Multiple AS extensions found."); - if (!X509_EXTENSION_get_critical(ext)) - return pr_val_err("The AS extension is not marked as critical."); - - pr_val_debug("ASN {"); - error = handle_asn_extension(ext, resources, - allow_asn_inherit); - pr_val_debug("}"); - asn_ext_found = true; + } else if (extnid == nid_as1 || extnid == nid_as2) { + if (extnid != allowed_as_nid) + return pr_val_err("Found an unexpected AS Resources extension."); + error = handle_asn_extension(ext, resources, false); if (error) return error; - - } else if (nid == bad_addr_nid) { - return pr_val_err("Certificate has an RFC%s policy, but contains an RFC%s IP extension.", - policy_rfc, bad_ext_rfc); - } else if (nid == bad_asn_nid) { - return pr_val_err("Certificate has an RFC%s policy, but contains an RFC%s ASN extension.", - policy_rfc, bad_ext_rfc); + allowed_as_nid = NID_undef; } } - if (!ip_ext_found && !asn_ext_found) - return pr_val_err("Certificate lacks both IP and AS extension."); - return 0; } -/** - * Copies the resources from @cert to @resources. - */ -int -certificate_get_resources(X509 *cert, struct resources *resources, - enum cert_type type) -{ - enum rpki_policy policy; - - policy = resources_get_policy(resources); - switch (policy) { - case RPKI_POLICY_RFC6484: - return __certificate_get_resources(cert, resources, - NID_sbgp_ipAddrBlock, NID_sbgp_autonomousSysNum, - nid_ipAddrBlocksv2(), nid_autonomousSysIdsv2(), - "6484", "8360", type != CERTYPE_BGPSEC); - case RPKI_POLICY_RFC8360: - return __certificate_get_resources(cert, resources, - nid_ipAddrBlocksv2(), nid_autonomousSysIdsv2(), - NID_sbgp_ipAddrBlock, NID_sbgp_autonomousSysNum, - "8360", "6484", type != CERTYPE_BGPSEC); - } - - pr_crit("Unknown policy: %u", policy); -} - static bool is_rsync(ASN1_IA5STRING *uri) { diff --git a/src/object/certificate.h b/src/object/certificate.h index 37413f52..4aa2ace0 100644 --- a/src/object/certificate.h +++ b/src/object/certificate.h @@ -3,6 +3,7 @@ #include "asn1/asn1c/ANY.h" #include "asn1/asn1c/SignatureValue.h" +#include "asn1/signed_data.h" #include "certificate_refs.h" #include "resource.h" @@ -36,7 +37,8 @@ int certificate_validate_signature(X509 *, ANY_t *coded, SignatureValue_t *); * not care about order. I don't know if you'll find other reasons if you choose * to migrate it. */ -int certificate_get_resources(X509 *, struct resources *, enum cert_type); +int certificate_get_resources(X509 *, struct resources *, + enum cert_type, enum ee_type); /** * Validates the certificate extensions, End-Entity style. diff --git a/src/object/ghostbusters.c b/src/object/ghostbusters.c index 2aae9912..5b27d7e8 100644 --- a/src/object/ghostbusters.c +++ b/src/object/ghostbusters.c @@ -36,7 +36,7 @@ ghostbusters_traverse(struct rpki_uri *uri, struct rpp *pp) error = rpp_crl(pp, &crl); if (error) goto revert_sobj; - eecert_init(&ee, crl, true); + eecert_init(&ee, EET_GBR, crl, true); /* Validate everything */ error = signed_object_validate(&sobj, &arcs, &ee); diff --git a/src/object/manifest.c b/src/object/manifest.c index 8e404aa7..fa172d9a 100644 --- a/src/object/manifest.c +++ b/src/object/manifest.c @@ -253,6 +253,8 @@ build_rpp(struct Manifest *mft, struct rpki_uri *notif, rpp_add = rpp_add_cer; else if (strncmp(ext, ".roa", 4) == 0) rpp_add = rpp_add_roa; + else if (strncmp(ext, ".asa", 4) == 0) + rpp_add = rpp_add_asa; else if (strncmp(ext, ".crl", 4) == 0) rpp_add = rpp_add_crl; else if (strncmp(ext, ".gbr", 4) == 0) @@ -348,7 +350,7 @@ handle_manifest(struct rpki_uri *uri, struct rpki_uri *notif, struct rpp **pp) error = rpp_crl(*pp, &crl); if (error) goto revert_rpp; - eecert_init(&ee, crl, false); + eecert_init(&ee, EET_MFT, crl, false); /* Validate everything */ error = signed_object_validate(&sobj, &arcs, &ee); diff --git a/src/object/roa.c b/src/object/roa.c index 0b4067ab..a8f5d1bc 100644 --- a/src/object/roa.c +++ b/src/object/roa.c @@ -271,7 +271,7 @@ roa_traverse(struct rpki_uri *uri, struct rpp *pp) error = rpp_crl(pp, &crl); if (error) goto revert_roa; - eecert_init(&ee, crl, false); + eecert_init(&ee, EET_ROA, crl, false); /* Validate and handle everything */ error = signed_object_validate(&sobj, &arcs, &ee); diff --git a/src/object/tal.c b/src/object/tal.c index 33515748..635d813f 100644 --- a/src/object/tal.c +++ b/src/object/tal.c @@ -351,6 +351,7 @@ handle_tal_uri(struct tal *tal, struct rpki_uri *uri, struct db_table *db) validation_handler.handle_roa_v4 = handle_roa_v4; validation_handler.handle_roa_v6 = handle_roa_v6; validation_handler.handle_router_key = handle_router_key; + validation_handler.handle_aspa = handle_aspa; validation_handler.arg = db; error = validation_prepare(&state, tal, &validation_handler); diff --git a/src/resource.c b/src/resource.c index 793acfc7..e9a4523c 100644 --- a/src/resource.c +++ b/src/resource.c @@ -556,6 +556,12 @@ resources_contains_asns(struct resources *res, struct asn_range const *range) return rasn_contains(res->asns, range); } +bool +resources_matches_asn(struct resources *res, uint32_t asid) +{ + return rasn_matches(res->asns, asid); +} + bool resources_contains_ipv4(struct resources *res, struct ipv4_prefix const *prefix) { diff --git a/src/resource.h b/src/resource.h index 661e3b7b..7600798c 100644 --- a/src/resource.h +++ b/src/resource.h @@ -31,6 +31,7 @@ int resources_add_asn(struct resources *, struct ASIdentifiers *, bool); bool resources_empty(struct resources *); bool resources_contains_asns(struct resources *, struct asn_range const *); +bool resources_matches_asn(struct resources *, uint32_t); bool resources_contains_ipv4(struct resources *, struct ipv4_prefix const *); bool resources_contains_ipv6(struct resources *, struct ipv6_prefix const *); diff --git a/src/resource/asn.c b/src/resource/asn.c index d9bebdc0..958856cd 100644 --- a/src/resource/asn.c +++ b/src/resource/asn.c @@ -70,6 +70,21 @@ rasn_contains(struct resources_asn *asns, struct asn_range const *range) return sarray_contains((struct sorted_array *) asns, range); } +bool +rasn_matches(struct resources_asn *_asns, uint32_t asn) +{ + struct sorted_array *asns = (struct sorted_array *)_asns; + struct asn_range range; + + if (asns == NULL) + return false; + if (sarray_count(asns) != 1u) + return false; + + range.min = range.max = asn; + return sarray_contains(asns, &range); +} + static int asn_range_cb(void *node, void *arg) { diff --git a/src/resource/asn.h b/src/resource/asn.h index 2d16c28e..1baf029e 100644 --- a/src/resource/asn.h +++ b/src/resource/asn.h @@ -24,6 +24,7 @@ void rasn_put(struct resources_asn *); int rasn_add(struct resources_asn *, struct asn_range const *); bool rasn_empty(struct resources_asn *); bool rasn_contains(struct resources_asn *, struct asn_range const *); +bool rasn_matches(struct resources_asn *, uint32_t); typedef int (*foreach_asn_cb)(struct asn_range const *, void *); int rasn_foreach(struct resources_asn *, foreach_asn_cb, void *); diff --git a/src/rpp.c b/src/rpp.c index d62dcae0..dbd7fc6d 100644 --- a/src/rpp.c +++ b/src/rpp.c @@ -1,6 +1,7 @@ #include "rpp.h" #include "log.h" +#include "object/aspa.h" #include "object/crl.h" #include "object/ghostbusters.h" #include "object/roa.h" @@ -34,6 +35,8 @@ struct rpp { struct uri_list roas; /* Route Origin Attestations */ + struct uri_list aspas; + struct uri_list ghostbusters; /* @@ -99,6 +102,13 @@ rpp_add_roa(struct rpp *pp, struct rpki_uri *uri) return 0; } +int +rpp_add_asa(struct rpp *pp, struct rpki_uri *uri) +{ + uris_add(&pp->aspas, uri); + return 0; +} + /** Steals ownership of @uri. */ int rpp_add_gbr(struct rpp *pp, struct rpki_uri *uri) @@ -250,6 +260,9 @@ rpp_traverse(struct rpp *pp) ARRAYLIST_FOREACH(&pp->roas, uri) roa_traverse(*uri, pp); + ARRAYLIST_FOREACH(&pp->aspas, uri) + aspa_traverse(*uri, pp); + /* * We don't do much with the ghostbusters right now. * Just validate them. diff --git a/src/rpp.h b/src/rpp.h index 874068f5..afcdd835 100644 --- a/src/rpp.h +++ b/src/rpp.h @@ -15,6 +15,7 @@ void rpp_refput(struct rpp *pp); int rpp_add_cer(struct rpp *, struct rpki_uri *); int rpp_add_crl(struct rpp *, struct rpki_uri *); int rpp_add_roa(struct rpp *, struct rpki_uri *); +int rpp_add_asa(struct rpp *pp, struct rpki_uri *); int rpp_add_gbr(struct rpp *, struct rpki_uri *); struct rpki_uri *rpp_get_crl(struct rpp const *); diff --git a/src/rrdp.c b/src/rrdp.c index 4d5355b6..f830db0e 100644 --- a/src/rrdp.c +++ b/src/rrdp.c @@ -538,6 +538,7 @@ is_known_extension(struct rpki_uri *uri) ext = uri_get_global(uri) + uri_get_global_len(uri) - 4; return ((strcmp(ext, ".cer") == 0) || (strcmp(ext, ".roa") == 0) + || (strcmp(ext, ".asa") == 0) || (strcmp(ext, ".mft") == 0) || (strcmp(ext, ".crl") == 0) || (strcmp(ext, ".gbr") == 0)); diff --git a/src/rtr/db/db_table.c b/src/rtr/db/db_table.c index 2038381d..4e26c871 100644 --- a/src/rtr/db/db_table.c +++ b/src/rtr/db/db_table.c @@ -3,8 +3,10 @@ #include #include "alloc.h" +#include "config.h" #include "data_structure/uthash.h" #include "log.h" +#include "types/aspa.h" struct hashable_roa { struct vrp data; @@ -16,9 +18,15 @@ struct hashable_key { UT_hash_handle hh; }; +struct hashable_aspa { + struct aspa *v; + UT_hash_handle hh; +}; + struct db_table { struct hashable_roa *roas; struct hashable_key *router_keys; + struct hashable_aspa *aspas; unsigned int total_roas_v4; unsigned int total_roas_v6; @@ -100,6 +108,75 @@ add_router_key(struct db_table *table, struct hashable_key *new) return 0; } +/* Assumes the lists are already sorted. Places the result in @new. */ +static struct aspa_providers +merge_providers(struct aspa_providers *old, struct aspa_providers *new) +{ + struct aspa_providers result; + uint32_t *merge; + size_t o, n, m; + + m = old->count + new->count; + if (!old->asids || m > config_get_max_aspa_providers()) { + result.asids = NULL; + result.count = 0; + return result; + } + + merge = pcalloc(m, sizeof(uint32_t)); + + for (o = 0, n = 0, m = 0; o < old->count && n < new->count;) { + if (old->asids[o] < new->asids[n]) { + merge[m] = old->asids[o]; + o++; + m++; + } else if (old->asids[o] > new->asids[n]) { + merge[m] = new->asids[n]; + n++; + m++; + } else + o++; + } + + for (; o < old->count; o++, m++) + merge[m] = old->asids[o]; + for (; n < new->count; n++, m++) + merge[m] = new->asids[n]; + + result.asids = merge; + result.count = m; + return result; +} + +static int +add_aspa(struct db_table *table, struct hashable_aspa *new) +{ + struct hashable_aspa *old; + struct aspa_providers merge; + int error; + + errno = 0; + HASH_REPLACE(hh, table->aspas, v->customer, sizeof(new->v->customer), + new, old); + error = errno; + if (error) { + pr_val_err("Cannot store ASPA: %s", strerror(error)); + return -error; + } + + if (old != NULL) { + merge = merge_providers(&old->v->providers, &new->v->providers); + + free(new->v->providers.asids); + new->v->providers = merge; + + free(old->v->providers.asids); + free(old); + } + + return 0; +} + /* Moves the content from @src into @dst. */ int db_table_join(struct db_table *dst, struct db_table *src) @@ -156,6 +233,22 @@ db_table_foreach_router_key(struct db_table const *table, return 0; } +int +db_table_foreach_aspa(struct db_table const *table, aspa_foreach_cb cb, + void *arg) +{ + struct hashable_aspa *node, *tmp; + int error; + + HASH_ITER(hh, table->aspas, node, tmp) { + error = cb(node->v, arg); + if (error) + return error; + } + + return 0; +} + unsigned int db_table_roa_count(struct db_table *table) { @@ -253,6 +346,24 @@ rtrhandler_handle_router_key(struct db_table *table, unsigned char const *ski, return error; } +int +rtrhandler_handle_aspa(struct db_table *table, struct aspa *v) +{ + struct hashable_aspa *aspa; + int error; + + aspa = pzalloc(sizeof(struct hashable_aspa)); + aspa->v = v; + + error = add_aspa(table, aspa); + if (error) + free(aspa); + else + aspa_refget(v); + + return error; +} + /* * Copies `@roas1 - roas2` into @deltas. * @@ -282,12 +393,6 @@ add_roa_deltas(struct hashable_roa *roas1, struct hashable_roa *roas2, return 0; } -static void -add_router_key_delta(struct deltas *deltas, struct hashable_key *key, int op) -{ - deltas_add_router_key(deltas, &key->data, op); -} - /* * Copies `@keys1 - keys2` into @deltas. * @@ -303,7 +408,33 @@ add_router_key_deltas(struct hashable_key *keys1, struct hashable_key *keys2, for (n1 = keys1; n1 != NULL; n1 = n1->hh.next) { HASH_FIND(hh, keys2, &n1->data, sizeof(n1->data), n2); if (n2 == NULL) - add_router_key_delta(deltas, n1, op); + deltas_add_router_key(deltas, &n1->data, op); + } +} + +static void +add_aspa_announcements(struct hashable_aspa *old, struct hashable_aspa *new, + struct deltas *deltas) +{ + struct hashable_aspa *o, *n, *tmp; + + HASH_ITER(hh, new, n, tmp) { + HASH_FIND(hh, old, &n->v->customer, sizeof(n->v->customer), o); + if (o == NULL || !providers_equal(&o->v->providers, &n->v->providers)) + deltas_add_aspa(deltas, o->v, FLAG_ANNOUNCEMENT); + } +} + +static void +add_aspa_withdraws(struct hashable_aspa *old, struct hashable_aspa *new, + struct deltas *deltas) +{ + struct hashable_aspa *o, *n, *tmp; + + HASH_ITER(hh, old, o, tmp) { + HASH_FIND(hh, new, &o->v->customer, sizeof(o->v->customer), n); + if (n == NULL) + deltas_add_aspa(deltas, o->v, FLAG_WITHDRAWAL); } } @@ -318,6 +449,8 @@ compute_deltas(struct db_table *old, struct db_table *new) FLAG_ANNOUNCEMENT); add_router_key_deltas(old->router_keys, new->router_keys, deltas, FLAG_WITHDRAWAL); + add_aspa_announcements(old->aspas, new->aspas, deltas); + add_aspa_withdraws(old->aspas, new->aspas, deltas); if (deltas_is_empty(deltas)) { deltas_refput(deltas); diff --git a/src/rtr/db/db_table.h b/src/rtr/db/db_table.h index 814d3af0..894187dc 100644 --- a/src/rtr/db/db_table.h +++ b/src/rtr/db/db_table.h @@ -3,6 +3,7 @@ #include "rtr/db/delta.h" #include "types/address.h" +#include "types/aspa.h" struct db_table; @@ -23,12 +24,15 @@ int db_table_foreach_router_key(struct db_table const *, router_key_foreach_cb, void *); void db_table_remove_router_key(struct db_table *, struct router_key const *); +int db_table_foreach_aspa(struct db_table const *, aspa_foreach_cb, void *); + int rtrhandler_handle_roa_v4(struct db_table *, uint32_t, struct ipv4_prefix const *, uint8_t); int rtrhandler_handle_roa_v6(struct db_table *, uint32_t, struct ipv6_prefix const *, uint8_t); int rtrhandler_handle_router_key(struct db_table *, unsigned char const *, uint32_t, unsigned char const *); +int rtrhandler_handle_aspa(struct db_table *, struct aspa *); struct deltas *compute_deltas(struct db_table *, struct db_table *); #endif /* SRC_RTR_DB_DB_TABLE_H_ */ diff --git a/src/rtr/db/delta.c b/src/rtr/db/delta.c index bd625748..c5f96047 100644 --- a/src/rtr/db/delta.c +++ b/src/rtr/db/delta.c @@ -25,9 +25,14 @@ struct delta_rk { unsigned char spk[RK_SPKI_LEN]; }; +struct _delta_aspa { + struct aspa *aspa; +}; + STATIC_ARRAY_LIST(deltas_v6, struct delta_v6) STATIC_ARRAY_LIST(deltas_v4, struct delta_v4) STATIC_ARRAY_LIST(deltas_rk, struct delta_rk) +STATIC_ARRAY_LIST(deltas_aspa, struct _delta_aspa) struct deltas { struct { @@ -42,6 +47,10 @@ struct deltas { struct deltas_rk adds; struct deltas_rk removes; } rk; + struct { + struct deltas_aspa adds; + struct deltas_aspa removes; + } aspa; atomic_uint references; }; @@ -59,6 +68,8 @@ deltas_create(void) deltas_v6_init(&result->v6.removes); deltas_rk_init(&result->rk.adds); deltas_rk_init(&result->rk.removes); + deltas_aspa_init(&result->aspa.adds); + deltas_aspa_init(&result->aspa.removes); atomic_init(&result->references, 1); return result; @@ -84,6 +95,8 @@ deltas_refput(struct deltas *deltas) deltas_v6_cleanup(&deltas->v6.removes, NULL); deltas_rk_cleanup(&deltas->rk.adds, NULL); deltas_rk_cleanup(&deltas->rk.removes, NULL); + deltas_aspa_cleanup(&deltas->aspa.adds, NULL); + deltas_aspa_cleanup(&deltas->aspa.removes, NULL); free(deltas); } } @@ -177,6 +190,26 @@ deltas_add_router_key(struct deltas *deltas, struct router_key const *key, pr_crit("Unknown delta operation: %d", op); } +void +deltas_add_aspa(struct deltas *deltas, struct aspa *aspa, int op) +{ + struct _delta_aspa delta; + + delta.aspa = aspa; + aspa_refget(aspa); + + switch (op) { + case FLAG_ANNOUNCEMENT: + deltas_aspa_add(&deltas->aspa.adds, &delta); + return; + case FLAG_WITHDRAWAL: + deltas_aspa_add(&deltas->aspa.removes, &delta); + return; + } + + pr_crit("Unknown delta operation: %d", op); +} + bool deltas_is_empty(struct deltas *deltas) { @@ -185,7 +218,9 @@ deltas_is_empty(struct deltas *deltas) && (deltas->v6.adds.len == 0) && (deltas->v6.removes.len == 0) && (deltas->rk.adds.len == 0) - && (deltas->rk.removes.len == 0); + && (deltas->rk.removes.len == 0) + && (deltas->aspa.adds.len == 0) + && (deltas->aspa.removes.len == 0); } static int @@ -258,37 +293,59 @@ __foreach_rk(struct deltas_rk *array, delta_router_key_foreach_cb cb, return 0; } +static int +__foreach_aspa(struct deltas_aspa *array, delta_aspa_foreach_cb cb, void *arg, + uint8_t flags) +{ + struct delta_aspa delta; + struct _delta_aspa *d; + int error; + + delta.flags = flags; + + ARRAYLIST_FOREACH(array, d) { + delta.aspa = d->aspa; + error = cb(&delta, arg); + if (error) + return error; + } + + return 0; +} + int deltas_foreach(struct deltas *deltas, delta_vrp_foreach_cb cb_vrp, - delta_router_key_foreach_cb cb_rk, void *arg) + delta_router_key_foreach_cb cb_rk, delta_aspa_foreach_cb cb_aspa, + void *arg) { int error; error = __foreach_v4(&deltas->v4.adds, cb_vrp, arg, FLAG_ANNOUNCEMENT); if (error) return error; - error = __foreach_v4(&deltas->v4.removes, cb_vrp, arg, FLAG_WITHDRAWAL); if (error) return error; - error = __foreach_v6(&deltas->v6.adds, cb_vrp, arg, FLAG_ANNOUNCEMENT); if (error) return error; - error = __foreach_v6(&deltas->v6.removes, cb_vrp, arg, FLAG_WITHDRAWAL); if (error) return error; - error = __foreach_rk(&deltas->rk.adds, cb_rk, arg, FLAG_ANNOUNCEMENT); if (error) return error; - - return __foreach_rk(&deltas->rk.removes, cb_rk, arg, FLAG_WITHDRAWAL); + error = __foreach_rk(&deltas->rk.removes, cb_rk, arg, FLAG_WITHDRAWAL); + if (error) + return error; + error = __foreach_aspa(&deltas->aspa.adds, cb_aspa, arg, FLAG_ANNOUNCEMENT); + if (error) + return error; + return __foreach_aspa(&deltas->aspa.removes, cb_aspa, arg, FLAG_WITHDRAWAL); } void deltas_print(struct deltas *deltas) { - deltas_foreach(deltas, delta_vrp_print, delta_rk_print, NULL); + deltas_foreach(deltas, delta_vrp_print, delta_rk_print, delta_aspa_print, NULL); } diff --git a/src/rtr/db/delta.h b/src/rtr/db/delta.h index cdaa9bcb..b7d943ce 100644 --- a/src/rtr/db/delta.h +++ b/src/rtr/db/delta.h @@ -1,6 +1,7 @@ #ifndef SRC_DELTA_H_ #define SRC_DELTA_H_ +#include "types/aspa.h" #include "types/delta.h" struct deltas; @@ -12,10 +13,11 @@ void deltas_refput(struct deltas *); void deltas_add_roa(struct deltas *, struct vrp const *, int, char, unsigned int, unsigned int); void deltas_add_router_key(struct deltas *, struct router_key const *, int); +void deltas_add_aspa(struct deltas *, struct aspa *, int); bool deltas_is_empty(struct deltas *); int deltas_foreach(struct deltas *, delta_vrp_foreach_cb, - delta_router_key_foreach_cb, void *); + delta_router_key_foreach_cb, delta_aspa_foreach_cb, void *); void deltas_print(struct deltas *); #endif /* SRC_DELTA_H_ */ diff --git a/src/rtr/db/vrps.c b/src/rtr/db/vrps.c index f23107d6..7778ce08 100644 --- a/src/rtr/db/vrps.c +++ b/src/rtr/db/vrps.c @@ -22,13 +22,20 @@ struct rk_node { SLIST_ENTRY(rk_node) next; }; +struct aspa_node { + struct delta_aspa delta; + SLIST_ENTRY(aspa_node) next; +}; + /** Sorted list to filter deltas */ SLIST_HEAD(vrp_slist, vrp_node); SLIST_HEAD(rk_slist, rk_node); +SLIST_HEAD(aspa_slist, aspa_node); struct sorted_lists { struct vrp_slist prefixes; struct rk_slist router_keys; + struct aspa_slist aspas; }; struct state { @@ -168,6 +175,12 @@ handle_router_key(unsigned char const *ski, struct asn_range const *asns, return 0; } +int +handle_aspa(struct aspa *aspa, void *arg) +{ + return rtrhandler_handle_aspa(arg, aspa); +} + /* * High level validator function. * @@ -289,7 +302,8 @@ vrps_update(bool *changed) * 2. -EAGAIN: No data available; database still under construction. */ int -vrps_foreach_base(vrp_foreach_cb cb_roa, router_key_foreach_cb cb_rk, void *arg) +vrps_foreach_base(vrp_foreach_cb cb_roa, router_key_foreach_cb cb_rk, + aspa_foreach_cb cb_aspa, void *arg) { int error; @@ -302,6 +316,9 @@ vrps_foreach_base(vrp_foreach_cb cb_roa, router_key_foreach_cb cb_rk, void *arg) if (error) goto end; error = db_table_foreach_router_key(state.base, cb_rk, arg); + if (error) + goto end; + error = db_table_foreach_aspa(state.base, cb_aspa, arg); } else error = -EAGAIN; @@ -355,6 +372,7 @@ router_key_ovrd_remove(struct delta_router_key const *delta, void *arg) && memcmp(key->spk, ptr->delta.router_key.spk, RK_SPKI_LEN) == 0 && delta->flags != ptr->delta.flags) { + /* TODO (rk) Shouldn't it be replaced? */ SLIST_REMOVE(filtered_keys, ptr, rk_node, next); free(ptr); return 0; @@ -368,11 +386,34 @@ router_key_ovrd_remove(struct delta_router_key const *delta, void *arg) return 0; } +static int +aspa_ovrd_remove(struct delta_aspa const *delta, void *arg) +{ + struct sorted_lists *lists = arg; + struct aspa_node *ptr; + struct aspa_slist *filtered_aspas; + + filtered_aspas = &lists->aspas; + SLIST_FOREACH(ptr, filtered_aspas, next) { + if (delta->aspa->customer == ptr->delta.aspa->customer) { + SLIST_REMOVE(filtered_aspas, ptr, aspa_node, next); + ptr->delta = *delta; + SLIST_INSERT_HEAD(filtered_aspas, ptr, next); + return 0; + } + } + + ptr = pmalloc(sizeof(struct aspa_node)); + ptr->delta = *delta; + SLIST_INSERT_HEAD(filtered_aspas, ptr, next); + return 0; +} + static int __deltas_foreach(struct deltas *deltas, void *arg) { return deltas_foreach(deltas, vrp_ovrd_remove, router_key_ovrd_remove, - arg); + aspa_ovrd_remove, arg); } /** @@ -388,11 +429,12 @@ __deltas_foreach(struct deltas *deltas, void *arg) int vrps_foreach_delta_since(serial_t from, serial_t *to, delta_vrp_foreach_cb vrp_cb, delta_router_key_foreach_cb rk_cb, - void *arg) + delta_aspa_foreach_cb aspa_cb, void *arg) { struct sorted_lists filtered_lists; struct vrp_node *vnode; struct rk_node *rnode; + struct aspa_node *anode; int error; error = rwlock_read_lock(&state_lock); @@ -434,6 +476,7 @@ vrps_foreach_delta_since(serial_t from, serial_t *to, */ SLIST_INIT(&filtered_lists.prefixes); SLIST_INIT(&filtered_lists.router_keys); + SLIST_INIT(&filtered_lists.aspas); error = darray_foreach_since(state.deltas, state.serial - from, __deltas_foreach, &filtered_lists); @@ -451,6 +494,11 @@ vrps_foreach_delta_since(serial_t from, serial_t *to, if (error) break; } + SLIST_FOREACH(anode, &filtered_lists.aspas, next) { + error = aspa_cb(&anode->delta, arg); + if (error) + break; + } release_list: while (!SLIST_EMPTY(&filtered_lists.prefixes)) { @@ -463,6 +511,11 @@ release_list: SLIST_REMOVE_HEAD(&filtered_lists.router_keys, next); free(rnode); } + while (!SLIST_EMPTY(&filtered_lists.aspas)) { + anode = filtered_lists.aspas.slh_first; + SLIST_REMOVE_HEAD(&filtered_lists.aspas, next); + free(anode); + } *to = state.serial; rwlock_unlock(&state_lock); @@ -507,5 +560,5 @@ get_current_session_id(uint8_t rtr_version) void vrps_print_base(void) { - vrps_foreach_base(vrp_print, router_key_print, NULL); + vrps_foreach_base(vrp_print, router_key_print, aspa_print, NULL); } diff --git a/src/rtr/db/vrps.h b/src/rtr/db/vrps.h index f3a65211..4a9bd2cd 100644 --- a/src/rtr/db/vrps.h +++ b/src/rtr/db/vrps.h @@ -9,6 +9,7 @@ #include "as_number.h" #include "types/address.h" +#include "types/aspa.h" #include "types/delta.h" #include "types/serial.h" @@ -23,15 +24,17 @@ int vrps_update(bool *); * Handle gracefully. */ -int vrps_foreach_base(vrp_foreach_cb, router_key_foreach_cb, void *); +int vrps_foreach_base(vrp_foreach_cb, router_key_foreach_cb, aspa_foreach_cb, + void *); int vrps_foreach_delta_since(serial_t, serial_t *, delta_vrp_foreach_cb, - delta_router_key_foreach_cb, void *); + delta_router_key_foreach_cb, delta_aspa_foreach_cb, void *); int get_last_serial_number(serial_t *); int handle_roa_v4(uint32_t, struct ipv4_prefix const *, uint8_t, void *); int handle_roa_v6(uint32_t, struct ipv6_prefix const *, uint8_t, void *); int handle_router_key(unsigned char const *, struct asn_range const *, unsigned char const *, void *); +int handle_aspa(struct aspa *, void *); uint16_t get_current_session_id(uint8_t); diff --git a/src/rtr/pdu.c b/src/rtr/pdu.c index 86fcd141..07829579 100644 --- a/src/rtr/pdu.c +++ b/src/rtr/pdu.c @@ -24,6 +24,8 @@ pdutype2str(enum pdu_type type) return "Router Key PDU"; case PDU_TYPE_ERROR_REPORT: return "Error Report PDU"; + case PDU_TYPE_ASPA: + return "ASPA PDU"; } return "unknown PDU"; diff --git a/src/rtr/pdu.h b/src/rtr/pdu.h index 35ded498..60372a2b 100644 --- a/src/rtr/pdu.h +++ b/src/rtr/pdu.h @@ -10,6 +10,7 @@ enum rtr_version { RTR_V0 = 0, RTR_V1 = 1, + RTR_V2 = 2, }; struct rtr_buffer { @@ -28,6 +29,7 @@ enum pdu_type { PDU_TYPE_CACHE_RESET = 8, PDU_TYPE_ROUTER_KEY = 9, PDU_TYPE_ERROR_REPORT = 10, + PDU_TYPE_ASPA = 11, }; char const *pdutype2str(enum pdu_type); diff --git a/src/rtr/pdu_handler.c b/src/rtr/pdu_handler.c index 5a419140..39ea6b24 100644 --- a/src/rtr/pdu_handler.c +++ b/src/rtr/pdu_handler.c @@ -56,6 +56,20 @@ send_delta_rk(struct delta_router_key const *delta, void *arg) &delta->router_key, delta->flags); } +static int +send_delta_aspa(struct delta_aspa const *delta, void *arg) +{ + struct send_delta_args *args = arg; + int error; + + error = send_cache_response_maybe(args); + if (error) + return error; + + return send_aspa_pdu(args->fd, args->rtr_version, + delta->aspa, delta->flags); +} + int handle_serial_query_pdu(struct rtr_request *request) { @@ -93,7 +107,8 @@ handle_serial_query_pdu(struct rtr_request *request) */ error = vrps_foreach_delta_since(request->pdu.obj.sq.serial_number, - &final_serial, send_delta_vrp, send_delta_rk, &args); + &final_serial, send_delta_vrp, send_delta_rk, send_delta_aspa, + &args); switch (error) { case 0: /* @@ -167,6 +182,22 @@ send_base_router_key(struct router_key const *key, void *arg) FLAG_ANNOUNCEMENT); } +static int +send_base_aspa(struct aspa const *aspa, void *arg) +{ + struct base_roa_args *args = arg; + int error; + + if (!args->started) { + error = send_cache_response_pdu(args->fd, args->version); + if (error) + return error; + args->started = true; + } + + return send_aspa_pdu(args->fd, args->version, aspa, FLAG_ANNOUNCEMENT); +} + int handle_reset_query_pdu(struct rtr_request *request) { @@ -196,7 +227,8 @@ handle_reset_query_pdu(struct rtr_request *request) * queries than reset queries. */ - error = vrps_foreach_base(send_base_roa, send_base_router_key, &args); + error = vrps_foreach_base(send_base_roa, send_base_router_key, + send_base_aspa, &args); /* See handle_serial_query_pdu() for some comments. */ switch (error) { diff --git a/src/rtr/pdu_sender.c b/src/rtr/pdu_sender.c index 2e50c23d..064a331f 100644 --- a/src/rtr/pdu_sender.c +++ b/src/rtr/pdu_sender.c @@ -6,6 +6,7 @@ #include "alloc.h" #include "config.h" +#include "data_structure/common.h" #include "log.h" #include "rtr/db/vrps.h" #include "rtr/primitive_writer.h" @@ -184,7 +185,7 @@ send_router_key_pdu(int fd, uint8_t version, unsigned char data[RTRPDU_ROUTER_KEY_LEN]; unsigned char *buf; - if (version == RTR_V0) + if (version < RTR_V1) return 0; buf = serialize_hdr(data, version, type, flags << 8, len); @@ -197,6 +198,52 @@ send_router_key_pdu(int fd, uint8_t version, return send_response(fd, type, data, len); } +int +send_aspa_pdu(int fd, uint8_t version, struct aspa const *aspa, uint8_t flags) +{ + static const uint8_t type = PDU_TYPE_ASPA; + unsigned char data[1024]; + array_index i; + unsigned char *buf; + int error; + + if (version < RTR_V2) + return 0; + + if (flags & FLAG_ANNOUNCEMENT) { + buf = serialize_hdr(data, version, type, flags << 8, + 12 + 4 * aspa->providers.count); + buf = write_uint32(buf, aspa->customer); + + for (i = 0; i < aspa->providers.count; i++) { + buf = write_uint32(buf, aspa->providers.asids[i]); + + if (buf >= data + 1024) { + error = send_response(fd, type, data, buf - data); + if (error) + return error; + buf = data; + } + } + + if (buf > data) { + error = send_response(fd, type, data, buf - data); + if (error) + return error; + } + + } else { + buf = serialize_hdr(data, version, type, flags << 8, 12); + write_uint32(buf, aspa->customer); + error = send_response(fd, type, data, 12); + if (error) + return error; + } + + + return 0; +} + #define MAX(a, b) ((a > b) ? a : b) int diff --git a/src/rtr/pdu_sender.h b/src/rtr/pdu_sender.h index ed1b709e..ea05dc1a 100644 --- a/src/rtr/pdu_sender.h +++ b/src/rtr/pdu_sender.h @@ -2,6 +2,7 @@ #define SRC_RTR_PDU_SENDER_H_ #include "rtr/pdu.h" +#include "types/aspa.h" #include "types/router_key.h" #include "types/serial.h" #include "types/vrp.h" @@ -11,6 +12,7 @@ int send_cache_reset_pdu(int, uint8_t); int send_cache_response_pdu(int, uint8_t); int send_prefix_pdu(int, uint8_t, struct vrp const *, uint8_t); int send_router_key_pdu(int, uint8_t, struct router_key const *, uint8_t); +int send_aspa_pdu(int, uint8_t, struct aspa const *, uint8_t); int send_end_of_data_pdu(int, uint8_t, serial_t); int send_error_report_pdu(int, uint8_t, uint16_t, struct rtr_buffer const *, char *); diff --git a/src/sorted_array.c b/src/sorted_array.c index e6d10bf1..b18a5b0d 100644 --- a/src/sorted_array.c +++ b/src/sorted_array.c @@ -165,6 +165,12 @@ sarray_contains(struct sorted_array const *sarray, void const *elem) return false; } +unsigned int +sarray_count(struct sorted_array const *sarray) +{ + return sarray ? sarray->count : 0; +} + int sarray_foreach(struct sorted_array *sarray, sarray_foreach_cb cb, void *arg) { diff --git a/src/sorted_array.h b/src/sorted_array.h index b06a685d..166f6a76 100644 --- a/src/sorted_array.h +++ b/src/sorted_array.h @@ -41,6 +41,7 @@ void sarray_put(struct sorted_array *); int sarray_add(struct sorted_array *, void const *); bool sarray_empty(struct sorted_array const *); bool sarray_contains(struct sorted_array const *, void const *); +unsigned int sarray_count(struct sorted_array const *); typedef int (*sarray_foreach_cb)(void *, void *); int sarray_foreach(struct sorted_array *, sarray_foreach_cb, void *); diff --git a/src/types/aspa.c b/src/types/aspa.c new file mode 100644 index 00000000..406b7611 --- /dev/null +++ b/src/types/aspa.c @@ -0,0 +1,48 @@ +#include "types/aspa.h" + +#include +#include +#include "data_structure/common.h" + +void +aspa_refget(struct aspa *aspa) +{ + aspa->refs++; +} + +void +aspa_refput(struct aspa *aspa) +{ + if ((aspa->refs--) <= 1) { + free(aspa->providers.asids); + free(aspa); + } +} + +int +aspa_print(struct aspa const *aspa, void *arg) +{ + array_index i; + printf("- [ASPA customerASID:%u\n", aspa->customer); + for (i = 0; i < aspa->providers.count; i++) + printf(" [Provider:%u]\n", aspa->providers.asids[i]); + printf(" ]\n"); + return 0; +} + +bool +providers_equal(struct aspa_providers *a, struct aspa_providers *b) +{ + array_index i; + + if (a == b) + return true; + if (!a || !b || (a->count != b->count)) + return false; + + for (i = 0; i < a->count; i++) + if (a->asids[i] != b->asids[i]) + return false; + + return true; +} diff --git a/src/types/aspa.h b/src/types/aspa.h new file mode 100644 index 00000000..d4ea1907 --- /dev/null +++ b/src/types/aspa.h @@ -0,0 +1,29 @@ +#ifndef SRC_TYPES_ASPA_H_ +#define SRC_TYPES_ASPA_H_ + +#include +#include +#include + +struct aspa_providers { + /* Can be NULL and zero. If this happens, just withdraw. */ + uint32_t *asids; + size_t count; +}; + +struct aspa { + uint32_t customer; + struct aspa_providers providers; + + int refs; +}; + +typedef int (*aspa_foreach_cb)(struct aspa const *, void *); + +void aspa_refget(struct aspa *); +void aspa_refput(struct aspa *); +int aspa_print(struct aspa const *, void *); + +bool providers_equal(struct aspa_providers *, struct aspa_providers *); + +#endif /* SRC_TYPES_ASPA_H_ */ diff --git a/src/types/delta.c b/src/types/delta.c index dacdf195..1b9d8959 100644 --- a/src/types/delta.c +++ b/src/types/delta.c @@ -31,3 +31,10 @@ delta_rk_print(struct delta_router_key const *delta, void *arg) print_flag(delta->flags); return router_key_print(&delta->router_key, arg); } + +int +delta_aspa_print(struct delta_aspa const *delta, void *arg) +{ + print_flag(delta->flags); + return aspa_print(delta->aspa, arg); +} diff --git a/src/types/delta.h b/src/types/delta.h index 9b7914ea..c29f3c4d 100644 --- a/src/types/delta.h +++ b/src/types/delta.h @@ -1,6 +1,7 @@ #ifndef SRC_TYPES_DELTA_H_ #define SRC_TYPES_DELTA_H_ +#include "types/aspa.h" #include "types/router_key.h" #include "types/vrp.h" @@ -17,11 +18,18 @@ struct delta_router_key { uint8_t flags; }; +struct delta_aspa { + struct aspa *aspa; + uint8_t flags; +}; + typedef int (*delta_vrp_foreach_cb)(struct delta_vrp const *, void *); typedef int (*delta_router_key_foreach_cb)(struct delta_router_key const *, void *); +typedef int (*delta_aspa_foreach_cb)(struct delta_aspa const *, void *); int delta_vrp_print(struct delta_vrp const *, void *); int delta_rk_print(struct delta_router_key const *, void *); +int delta_aspa_print(struct delta_aspa const *, void *); #endif /* SRC_TYPES_DELTA_H_ */ diff --git a/src/validation_handler.c b/src/validation_handler.c index 9b02ac1e..4c976328 100644 --- a/src/validation_handler.c +++ b/src/validation_handler.c @@ -56,3 +56,15 @@ vhandler_handle_router_key(unsigned char const *ski, ? handler->handle_router_key(ski, asns, spk, handler->arg) : 0; } + +int +vhandler_handle_aspa(struct aspa *aspa) +{ + struct validation_handler const *handler; + + handler = get_current_threads_handler(); + + return (handler->handle_aspa != NULL) + ? handler->handle_aspa(aspa, handler->arg) + : 0; +} diff --git a/src/validation_handler.h b/src/validation_handler.h index 2e284c1f..c6b50222 100644 --- a/src/validation_handler.h +++ b/src/validation_handler.h @@ -30,6 +30,8 @@ struct validation_handler { /** Called every time Fort has successfully validated a BGPsec cert */ int (*handle_router_key)(unsigned char const *, struct asn_range const *, unsigned char const *, void *); + /* Swallows the ASPA. */ + int (*handle_aspa)(struct aspa *, void *); /** Generic user-defined argument for the functions above. */ void *arg; }; @@ -38,5 +40,6 @@ int vhandler_handle_roa_v4(uint32_t, struct ipv4_prefix const *, uint8_t); int vhandler_handle_roa_v6(uint32_t, struct ipv6_prefix const *, uint8_t); int vhandler_handle_router_key(unsigned char const *, struct asn_range const *, unsigned char const *); +int vhandler_handle_aspa(struct aspa *); #endif /* SRC_VALIDATION_HANDLER_H_ */ diff --git a/test/rtr/db/db_table_test.c b/test/rtr/db/db_table_test.c index b19ae402..d3492cf3 100644 --- a/test/rtr/db/db_table_test.c +++ b/test/rtr/db/db_table_test.c @@ -19,6 +19,7 @@ static bool roas_found[TOTAL_ROAS]; static unsigned int total_found; __MOCK_ABORT(config_get_local_repository, char const *, "tmp/dbt", void) +MOCK_UINT(config_get_max_aspa_providers, 10, void) static bool vrp_equals_v4(struct vrp const *vrp, uint8_t as, uint32_t addr, @@ -166,6 +167,83 @@ START_TEST(test_basic) } END_TEST +static void +init_providers(struct aspa_providers *provs, ...) +{ + int asn; + size_t a; + va_list ap; + + va_start(ap, provs); + for (a = 0; (asn = va_arg(ap, int)) != 0; a++) + ; + va_end(ap); + + provs->asids = pcalloc(a, sizeof(uint32_t)); + provs->count = a; + + va_start(ap, provs); + for (a = 0; (asn = va_arg(ap, int)) != 0; a++) + provs->asids[a] = asn; + va_end(ap); +} + +static void +ck_merge(struct aspa_providers *a1, struct aspa_providers *a2, ...) +{ + struct aspa_providers res; + va_list ap; + size_t a; + int asn; + + res = merge_providers(a1, a2); + + va_start(ap, a2); + for (a = 0; (asn = va_arg(ap, int)) != 0; a++) + ck_assert_uint_eq(asn, res.asids[a]); + va_end(ap); + ck_assert_uint_eq(a, res.count); + free(res.asids); + + res = merge_providers(a2, a1); + va_start(ap, a2); + for (a = 0; (asn = va_arg(ap, int)) != 0; a++) + ck_assert_uint_eq(asn, res.asids[a]); + va_end(ap); + ck_assert_uint_eq(a, res.count); + free(res.asids); +} + +START_TEST(test_aspa_merge) +{ + struct aspa_providers a, b; + + init_providers(&a, 1, 2, 3, 0); + init_providers(&b, 5, 6, 7, 0); + ck_merge(&a, &b, 1, 2, 3, 5, 6, 7, 0); + + init_providers(&a, 1, 3, 5, 0); + init_providers(&b, 2, 4, 6, 0); + ck_merge(&a, &b, 1, 2, 3, 4, 5, 6, 0); + + init_providers(&a, 2, 4, 10, 0); + init_providers(&b, 6, 8, 12, 0); + ck_merge(&a, &b, 2, 4, 6, 8, 10, 12, 0); + + init_providers(&a, 1, 2, 3, 4, 0); + init_providers(&b, 1, 2, 3, 4, 0); + ck_merge(&a, &b, 1, 2, 3, 4, 0); + + init_providers(&a, 1, 2, 3, 0); + init_providers(&b, 1, 2, 4, 6, 0); + ck_merge(&a, &b, 1, 2, 3, 4, 6, 0); + + init_providers(&a, 1, 2, 3, 0); + init_providers(&b, 1, 2, 3, 4, 5, 0); + ck_merge(&a, &b, 1, 2, 3, 4, 5, 0); +} +END_TEST + static Suite *pdu_suite(void) { Suite *suite; @@ -173,6 +251,7 @@ static Suite *pdu_suite(void) core = tcase_create("Core"); tcase_add_test(core, test_basic); + tcase_add_test(core, test_aspa_merge); suite = suite_create("DB Table"); suite_add_tcase(suite, core); diff --git a/test/rtr/db/vrps_test.c b/test/rtr/db/vrps_test.c index 63e93de9..479ea7dc 100644 --- a/test/rtr/db/vrps_test.c +++ b/test/rtr/db/vrps_test.c @@ -73,6 +73,7 @@ MOCK_ABORT_ENUM(config_get_output_format, output_format, void) MOCK_ABORT_INT(hash_local_file, char const *uri, unsigned char *result, unsigned int *result_len) __MOCK_ABORT(config_get_local_repository, char const *, "tmp/vrps", void) +MOCK_UINT(config_get_max_aspa_providers, 10, void) /* Test functions */ diff --git a/test/rtr/pdu_handler_test.c b/test/rtr/pdu_handler_test.c index bff21116..9ddcc1e9 100644 --- a/test/rtr/pdu_handler_test.c +++ b/test/rtr/pdu_handler_test.c @@ -25,6 +25,7 @@ MOCK_INT(slurm_apply, 0, struct db_table *base, struct db_slurm **slurm) MOCK_ABORT_VOID(db_slurm_destroy, struct db_slurm *db) MOCK_VOID(output_print_data, struct db_table const *db) __MOCK_ABORT(config_get_local_repository, char const *, "tmp/pdu", void) +MOCK_UINT(config_get_max_aspa_providers, 10, void) /* Mocks end */