]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Rewrite the core loop and its threading
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 5 Nov 2024 23:16:49 +0000 (17:16 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 5 Nov 2024 23:16:49 +0000 (17:16 -0600)
Decouples threads from TALs; threads claim RPPs now.

Aside from scaling better, this unclogs the way to several future
improvements.

33 files changed:
src/Makefile.am
src/abbreviations.txt
src/asn1/asn1c/ROAIPAddressFamily.c
src/cache.c
src/config.c
src/main.c
src/object/bgpsec.c
src/object/certificate.c
src/object/certificate.h
src/object/roa.c
src/object/tal.c
src/object/tal.h
src/resource.c
src/rtr/db/db_table.c
src/rtr/db/vrps.c
src/rtr/db/vrps.h
src/slurm/db_slurm.c
src/state.c [deleted file]
src/state.h [deleted file]
src/stream.c
src/stream.h
src/task.c [new file with mode: 0644]
src/task.h [new file with mode: 0644]
src/thread_var.c
src/thread_var.h
src/types/address.c
src/validation_handler.c
src/validation_handler.h
test/Makefile.am
test/mock.c
test/object/tal_test.c
test/rtr/db/rtr_db_mock.c
test/task_test.c [new file with mode: 0644]

index 86cc34aa78b6a36fbe50d8c8d9f4b1df6b06385e..5f41650b512ae01f21f1bcc7788605eea36164d6 100644 (file)
@@ -72,7 +72,8 @@ fort_SOURCES += sig.h sig.c
 fort_SOURCES += slurm/db_slurm.c slurm/db_slurm.h
 fort_SOURCES += slurm/slurm_loader.c slurm/slurm_loader.h
 fort_SOURCES += slurm/slurm_parser.c slurm/slurm_parser.h
-fort_SOURCES += state.h state.c
+fort_SOURCES += stream.h stream.c
+fort_SOURCES += task.h task.c
 fort_SOURCES += thread_pool.c thread_pool.h
 fort_SOURCES += thread_var.h thread_var.c
 fort_SOURCES += types/address.h types/address.c
index cfdb82dcea634bad45cbb95daba7afb742724edc..c06e6f6cde30e3f58e9bb56e6895636289c3f037 100644 (file)
@@ -27,6 +27,7 @@ notif: (RRDP) Notification
 pdu: Protocol Data Unit (RFC 6810)
 pp: Publication Point
 pr: print
+pfx: prefix
 ptr: pointer
 refget: reference get (+1 to reference counter)
 refput: reference put (-1 to reference counter)
index 29f1aa51a7b3a0a839639d1bc42360184568292f..66101e79c3b7711f09110b9bc6a7db29c45a6c1e 100644 (file)
@@ -47,14 +47,14 @@ static json_t *
 prefix6_to_json(struct ROAIPAddress *addr)
 {
        struct ipv6_prefix prefix6;
-       char buff[INET6_ADDRSTRLEN];
+       char buf[INET6_ADDRSTRLEN];
 
        if (prefix6_decode(&addr->address, &prefix6) != 0)
                return NULL;
-       if (inet_ntop(AF_INET6, &prefix6.addr, buff, INET6_ADDRSTRLEN) == NULL)
+       if (inet_ntop(AF_INET6, &prefix6.addr, buf, INET6_ADDRSTRLEN) == NULL)
                return NULL;
 
-       return prefix2json(buff, prefix6.len);
+       return prefix2json(buf, prefix6.len);
 }
 
 static json_t *
index 704cf95abde506de93b77d8539dae9cc8a1e3958..a3e6359d63c7d4b37c982e60e33d10c334ca8935 100644 (file)
@@ -74,7 +74,7 @@ struct cache_commit {
        STAILQ_ENTRY(cache_commit) lh;
 };
 
-STAILQ_HEAD(cache_commits, cache_commit) commits = STAILQ_HEAD_INITIALIZER(commits);
+static STAILQ_HEAD(cache_commits, cache_commit) commits = STAILQ_HEAD_INITIALIZER(commits);
 
 #define LOCKFILE ".lock"
 #define INDEX_FILE "index.json"
index e49d4699081ca04a542774732f543924a991ed96..f6ada01830890b346cb68729bda29a8c22c4a1cf 100644 (file)
@@ -22,7 +22,6 @@
 #include "init.h"
 #include "json_handler.h"
 #include "log.h"
-#include "state.h"
 #include "thread_pool.h"
 #include "types/array.h"
 #include "types/path.h"
index 994bf7d7e0b4b54e1d2e82dcf740fd3359775df8..ccb789aa8df7187e271d643255c4bc66dd150c3f 100644 (file)
 #include "output_printer.h"
 #include "print_file.h"
 #include "relax_ng.h"
+#include "rtr/db/vrps.h"
 #include "rtr/rtr.h"
 #include "rsync.h"
 #include "sig.h"
+#include "task.h"
 #include "thread_var.h"
 
 static int
@@ -159,6 +161,7 @@ main(int argc, char **argv)
        error = output_setup();
        if (error)
                goto revert_vrps;
+       task_setup();
 
        /* Meat */
 
@@ -175,6 +178,7 @@ main(int argc, char **argv)
        }
 
        /* End */
+       task_teardown();
 revert_vrps:
        vrps_destroy();
 revert_relax_ng:
index 7847405d695cf3c56eeec85f296f56c7bf9ab6d6..092095e1a951b69718897b11a0e43dafc8ca7ede 100644 (file)
@@ -20,7 +20,7 @@ asn_cb(struct asn_range const *range, void *arg)
                return pr_val_err("BGPsec certificate is not allowed to contain ASN range %u-%u.",
                    range->min, range->max);
 
-       return vhandler_handle_router_key(params->ski, range, params->spk);
+       return vhandle_router_key(params->ski, range, params->spk);
 }
 
 int
index 5ad8af7d44390057c40e5bbfdb0ae22db9e57904..9de21803e77eb23fdf62cc75be03e734d3ee1f1a 100644 (file)
@@ -24,6 +24,8 @@
 #include "object/ghostbusters.h"
 #include "object/manifest.h"
 #include "object/roa.h"
+#include "object/tal.h"
+#include "task.h"
 #include "thread_var.h"
 #include "types/name.h"
 #include "types/path.h"
@@ -215,15 +217,13 @@ fail:     fnstack_pop();
 }
 
 static int
-validate_spki(X509_PUBKEY *cert_spki)
+validate_spki(struct tal *tal, X509_PUBKEY *cert_spki)
 {
-       struct tal *tal;
        X509_PUBKEY *tal_spki;
        int error;
 
-       tal = validation_tal(state_retrieve());
        if (tal == NULL)
-               pr_crit("Validation state has no TAL.");
+               pr_crit("TAL is NULL.");
 
        /*
         * We have a problem at this point:
@@ -392,7 +392,7 @@ validate_subject_public_key(X509_PUBKEY *pubkey)
 }
 
 static int
-validate_public_key(X509 *cert, enum cert_type type)
+validate_public_key(struct rpki_certificate *cert)
 {
        X509_PUBKEY *pubkey;
        EVP_PKEY *evppkey;
@@ -401,7 +401,7 @@ validate_public_key(X509 *cert, enum cert_type type)
        int error;
 
        /* Reminder: X509_PUBKEY is the same as SubjectPublicKeyInfo. */
-       pubkey = X509_get_X509_PUBKEY(cert);
+       pubkey = X509_get_X509_PUBKEY(cert->x509);
        if (pubkey == NULL)
                return val_crypto_err("X509_get_X509_PUBKEY() returned NULL");
 
@@ -409,7 +409,7 @@ validate_public_key(X509 *cert, enum cert_type type)
        if (!ok)
                return val_crypto_err("X509_PUBKEY_get0_param() returned %d", ok);
 
-       if (type == CERTYPE_BGPSEC)
+       if (cert->type == CERTYPE_BGPSEC)
                return validate_certificate_public_key_algorithm_bgpsec(pa);
 
        error = validate_certificate_public_key_algorithm(pa);
@@ -432,13 +432,13 @@ validate_public_key(X509 *cert, enum cert_type type)
         * getting the message.
         */
 
-       if (type == CERTYPE_TA) {
-               error = validate_spki(pubkey);
+       if (cert->type == CERTYPE_TA) {
+               error = validate_spki(cert->tal, pubkey);
                if (error)
                        return error;
-               if ((evppkey = X509_get0_pubkey(cert)) == NULL)
+               if ((evppkey = X509_get0_pubkey(cert->x509)) == NULL)
                        return val_crypto_err("X509_get0_pubkey() returned NULL");
-               if (X509_verify(cert, evppkey) != 1)
+               if (X509_verify(cert->x509, evppkey) != 1)
                        return -EINVAL;
        }
 
@@ -490,7 +490,7 @@ certificate_validate_rfc6487(struct rpki_certificate *cert)
 
        /* rfc6487#section-4.7 */
        /* Fragment of rfc8630#section-2.3 */
-       error = validate_public_key(cert->x509, cert->type);
+       error = validate_public_key(cert);
        if (error)
                return error;
 
@@ -773,21 +773,6 @@ end:       BIO_free(bio);
        return cert;
 }
 
-static void
-certificate_stack_push(struct cert_stack *stack, struct cache_mapping *map,
-    struct rpki_certificate *parent)
-{
-       struct rpki_certificate *cert;
-
-       cert = pzalloc(sizeof(*cert));
-       map_copy(&cert->map, map);
-       cert->parent = parent;
-       cert->refcount = 1;
-       SLIST_INSERT_HEAD(stack, cert, lh);
-
-       parent->refcount++;
-}
-
 void
 rpki_certificate_init_ee(struct rpki_certificate *ee,
     struct rpki_certificate *parent, bool force_inherit)
@@ -797,9 +782,9 @@ rpki_certificate_init_ee(struct rpki_certificate *ee,
        ee->policy = RPKI_POLICY_RFC6484;
        ee->resources = resources_create(RPKI_POLICY_RFC6484, force_inherit);
        ee->parent = parent;
-       ee->refcount = 1;
+       atomic_init(&ee->refcount, 1);
 
-       parent->refcount++;
+       atomic_fetch_add(&parent->refcount, 1);
 }
 
 void
@@ -819,13 +804,47 @@ rpki_certificate_cleanup(struct rpki_certificate *cert)
 void
 rpki_certificate_free(struct rpki_certificate *cert)
 {
-       cert->refcount--;
-       if (cert->refcount == 0) {
+       if (atomic_fetch_sub(&cert->refcount, 1) == 1) {
                rpki_certificate_cleanup(cert);
                free(cert);
        }
 }
 
+/*
+ * It appears that this function is called by LibreSSL whenever it finds an
+ * error while validating.
+ * It is expected to return "okay" status: Nonzero if the error should be
+ * ignored, zero if the error is grounds to abort the validation.
+ *
+ * Note to myself: During my tests, this function was called in
+ * X509_verify_cert(ctx) -> check_chain_extensions(0, ctx),
+ * and then twice again in
+ * X509_verify_cert(ctx) -> internal_verify(1, ctx).
+ *
+ * Regarding the ok argument: I'm not 100% sure that I get it; I don't
+ * understand why this function would be called with ok = 1.
+ * http://openssl.cs.utah.edu/docs/crypto/X509_STORE_CTX_set_verify_cb.html
+ * The logic I implemented is the same as the second example: Always ignore the
+ * error that's troubling the library, otherwise try to be as unintrusive as
+ * possible.
+ */
+static int
+cb(int ok, X509_STORE_CTX *ctx)
+{
+       int error;
+
+       /*
+        * We need to handle two new critical extensions (IP Resources and ASN
+        * Resources), so unknown critical extensions are fine as far as
+        * LibreSSL is concerned.
+        * Unfortunately, LibreSSL has no way of telling us *which* is the
+        * unknown critical extension, but since RPKI defines its own set of
+        * valid extensions, we'll have to figure it out later anyway.
+        */
+       error = X509_STORE_CTX_get_error(ctx);
+       return (error == X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION) ? 1 : ok;
+}
+
 static STACK_OF(X509) *
 build_trusted_stack(struct rpki_certificate *cert)
 {
@@ -906,6 +925,8 @@ certificate_validate_chain(struct rpki_certificate *cert)
        /* Reference: openbsd/src/usr.bin/openssl/verify.c */
 
        X509_STORE_CTX *ctx;
+       X509_STORE *store;
+       X509_VERIFY_PARAM *params;
        STACK_OF(X509) *trusted;
        STACK_OF(X509_CRL) *crls;
        int ok;
@@ -915,30 +936,45 @@ certificate_validate_chain(struct rpki_certificate *cert)
                return 0; /* No chain to validate. */
 
        ctx = X509_STORE_CTX_new();
-       if (ctx == NULL) {
-               val_crypto_err("X509_STORE_CTX_new() returned NULL");
-               return EINVAL;
+       if (ctx == NULL)
+               return val_crypto_err("X509_STORE_CTX_new() returned NULL");
+
+       store = X509_STORE_new();
+       if (!store) {
+               error = val_crypto_err("X509_STORE_new() returned NULL");
+               goto end1;
        }
 
+       params = X509_VERIFY_PARAM_new();
+       if (params == NULL) {
+               error = val_crypto_err("X509_VERIFY_PARAM_new() returned NULL");
+               goto end2;
+       }
+
+       X509_VERIFY_PARAM_set_flags(params, X509_V_FLAG_CRL_CHECK);
+       if (config_get_validation_time() != 0)
+               X509_VERIFY_PARAM_set_time(params, config_get_validation_time());
+       X509_STORE_set1_param(store, params);
+       X509_STORE_set_verify_cb(store, cb);
+
        /* Returns 0 or 1 , all callers test ! only. */
-       ok = X509_STORE_CTX_init(ctx, validation_store(state_retrieve()),
-           cert->x509, NULL);
+       ok = X509_STORE_CTX_init(ctx, store, cert->x509, NULL);
        if (!ok) {
                error = val_crypto_err("X509_STORE_CTX_init() returned %d", ok);
-               goto end1;
+               goto end3;
        }
 
        trusted = build_trusted_stack(cert);
        if (!trusted) {
                error = EINVAL;
-               goto end1;
+               goto end3;
        }
        X509_STORE_CTX_trusted_stack(ctx, trusted);
 
        crls = build_crl_stack(cert);
        if (!crls) {
                error = EINVAL;
-               goto end2;
+               goto end4;
        }
        X509_STORE_CTX_set0_crls(ctx, crls);
 
@@ -961,9 +997,10 @@ certificate_validate_chain(struct rpki_certificate *cert)
                 * error code is stored in the context.
                 */
                error = X509_STORE_CTX_get_error(ctx);
-               if (error == X509_V_ERR_CRL_HAS_EXPIRED)
+               if (error == X509_V_ERR_CRL_HAS_EXPIRED) {
                        complain_crl_stale(cert->parent->rpp.crl.obj);
-               else if (error)
+                       error = EINVAL;
+               } else if (error)
                        pr_val_err("Certificate validation failed: %s",
                            X509_verify_cert_error_string(error));
                else {
@@ -975,13 +1012,15 @@ certificate_validate_chain(struct rpki_certificate *cert)
                        val_crypto_err("Certificate validation failed: %d", ok);
                        error = EINVAL;
                }
-               goto end3;
+               goto end5;
        }
 
        error = 0;
 
-end3:  sk_X509_CRL_free(crls);
-end2:  sk_X509_free(trusted);
+end5:  sk_X509_CRL_free(crls);
+end4:  sk_X509_free(trusted);
+end3:  X509_VERIFY_PARAM_free(params);
+end2:  X509_STORE_free(store);
 end1:  X509_STORE_CTX_free(ctx);
        return error;
 }
@@ -1850,14 +1889,15 @@ end:    fnstack_pop();
        return error;
 }
 
-static int
-certificate_traverse(struct rpki_certificate *ca, struct cert_stack *stack)
+int
+certificate_traverse(struct rpki_certificate *ca)
 {
        struct cache_cage *cage;
        char const *mft;
        array_index i;
        struct cache_mapping *map;
        char const *ext;
+       unsigned int queued;
        int error;
 
        error = certificate_validate(ca);
@@ -1889,58 +1929,22 @@ retry:  mft = cage_map_file(cage, ca->sias.rpkiManifest);
                goto end;
        }
 
+       queued = 0;
        for (i = 0; i < ca->rpp.nfiles; i++) {
                map = ca->rpp.files + i;
                ext = map->url + strlen(map->url) - 4;
                if (strcmp(ext, ".cer") == 0)
-                       certificate_stack_push(stack, map, ca);
+                       queued += task_enqueue(map, ca);
                else if (strcmp(ext, ".roa") == 0)
                        roa_traverse(map, ca);
                else if (strcmp(ext, ".gbr") == 0)
                        ghostbusters_traverse(map, ca);
        }
 
+       if (queued > 0)
+               task_wakeup();
        cache_commit_rpp(ca->sias.caRepository, &ca->rpp);
 
 end:   free(cage);
        return error;
 }
-
-int
-traverse_tree(struct cache_mapping const *ta_map, struct validation *state)
-{
-       struct cert_stack stack;
-       struct rpki_certificate *ta;
-       struct rpki_certificate *ca;
-       int error;
-
-       SLIST_INIT(&stack);
-
-       /* == Root certificate == */
-       ta = pzalloc(sizeof(struct rpki_certificate));
-       map_copy(&ta->map, ta_map);
-       ta->refcount = 1;
-
-       error = certificate_traverse(ta, &stack);
-       if (error)
-               goto end;
-
-       /*
-        * From now on, the tree should be considered valid, even if subsequent
-        * certificates fail.
-        * (the root validated successfully; subtrees are isolated problems.)
-        */
-
-       /* == Every other certificate == */
-       while (!SLIST_EMPTY(&stack)) {
-               ca = SLIST_FIRST(&stack);
-               SLIST_REMOVE_HEAD(&stack, lh);
-
-               certificate_traverse(ca, &stack);
-
-               rpki_certificate_free(ca);
-       }
-
-end:   rpki_certificate_free(ta);
-       return error;
-}
index e4cc99940418edefdc7c406fea0a43ee32d6126f..212f537b9fca89ce3dfe3b6840cfaebd1af69ad2 100644 (file)
@@ -2,6 +2,7 @@
 #define SRC_OBJECT_CERTIFICATE_H_
 
 #include <openssl/x509.h>
+#include <stdatomic.h>
 #include <sys/queue.h>
 
 #include "asn1/asn1c/ANY.h"
@@ -9,7 +10,6 @@
 #include "cache.h"
 #include "certificate_refs.h"
 #include "resource.h"
-#include "state.h"
 #include "types/rpp.h"
 
 /* Certificate types in the RPKI */
@@ -30,11 +30,12 @@ struct rpki_certificate {
        struct resources *resources;
        struct sia_uris sias;
 
+       struct tal *tal;                        /* Only needed by TAs for now */
        struct rpki_certificate *parent;
        struct rpp rpp;                         /* Nonexistent on EEs */
 
        SLIST_ENTRY(rpki_certificate) lh;       /* List Hook */
-       unsigned int refcount;
+       atomic_uint refcount;
 };
 
 void rpki_certificate_init_ee(struct rpki_certificate *,
@@ -82,6 +83,6 @@ int certificate_validate_extensions_bgpsec(void);
  */
 int certificate_validate_aia(struct rpki_certificate *);
 
-int traverse_tree(struct cache_mapping const *, struct validation *);
+int certificate_traverse(struct rpki_certificate *);
 
 #endif /* SRC_OBJECT_CERTIFICATE_H_ */
index 6319fe578d072b788ed88c79c51faf523e97ffbf..5b2c487a40ac3917ef8c2ee48aa62bc91e90ac6b 100644 (file)
@@ -5,6 +5,7 @@
 #include "log.h"
 #include "object/signed_object.h"
 #include "thread_var.h"
+#include "validation_handler.h"
 
 static int
 decode_roa(struct signed_object *sobj, struct RouteOriginAttestation **result)
@@ -21,18 +22,19 @@ static int
 ____handle_roa_v4(struct resources *parent, unsigned long asn,
     struct ROAIPAddress *roa_addr)
 {
-       struct ipv4_prefix prefix;
-       unsigned long max_length;
+       struct ipv4_prefix pfx;
+       unsigned long maxlen;
+       char buf[INET_ADDRSTRLEN];
        int error;
 
-       error = prefix4_decode(&roa_addr->address, &prefix);
+       error = prefix4_decode(&roa_addr->address, &pfx);
        if (error)
                return error;
 
-       pr_val_debug("address: %s/%u", v4addr2str(&prefix.addr), prefix.len);
+       pr_val_debug("address: %s/%u", addr2str4(&pfx.addr, buf), pfx.len);
 
        if (roa_addr->maxLength != NULL) {
-               error = asn_INTEGER2ulong(roa_addr->maxLength, &max_length);
+               error = asn_INTEGER2ulong(roa_addr->maxLength, &maxlen);
                if (error) {
                        if (errno) {
                                pr_val_err("Error casting ROA's IPv4 maxLength: %s",
@@ -40,46 +42,47 @@ ____handle_roa_v4(struct resources *parent, unsigned long asn,
                        }
                        return pr_val_err("The ROA's IPv4 maxLength isn't a valid unsigned long");
                }
-               pr_val_debug("maxLength: %lu", max_length);
+               pr_val_debug("maxLength: %lu", maxlen);
 
-               if (max_length > 32) {
+               if (maxlen > 32) {
                        return pr_val_err("maxLength (%lu) is out of bounds (0-32).",
-                           max_length);
+                           maxlen);
                }
 
-               if (prefix.len > max_length) {
+               if (pfx.len > maxlen) {
                        return pr_val_err("Prefix length (%u) > maxLength (%lu)",
-                           prefix.len, max_length);
+                           pfx.len, maxlen);
                }
 
        } else {
-               max_length = prefix.len;
+               maxlen = pfx.len;
        }
 
-       if (!resources_contains_ipv4(parent, &prefix)) {
+       if (!resources_contains_ipv4(parent, &pfx)) {
                return pr_val_err("ROA is not allowed to advertise %s/%u.",
-                   v4addr2str(&prefix.addr), prefix.len);
+                   addr2str4(&pfx.addr, buf), pfx.len);
        }
 
-       return vhandler_handle_roa_v4(asn, &prefix, max_length);
+       return vhandle_roa_v4(asn, &pfx, maxlen);
 }
 
 static int
 ____handle_roa_v6(struct resources *parent, unsigned long asn,
     struct ROAIPAddress *roa_addr)
 {
-       struct ipv6_prefix prefix;
-       unsigned long max_length;
+       struct ipv6_prefix pfx;
+       unsigned long maxlen;
+       char buf[INET6_ADDRSTRLEN];
        int error;
 
-       error = prefix6_decode(&roa_addr->address, &prefix);
+       error = prefix6_decode(&roa_addr->address, &pfx);
        if (error)
                return error;
 
-       pr_val_debug("address: %s/%u", v6addr2str(&prefix.addr), prefix.len);
+       pr_val_debug("address: %s/%u", addr2str6(&pfx.addr, buf), pfx.len);
 
        if (roa_addr->maxLength != NULL) {
-               error = asn_INTEGER2ulong(roa_addr->maxLength, &max_length);
+               error = asn_INTEGER2ulong(roa_addr->maxLength, &maxlen);
                if (error) {
                        if (errno) {
                                pr_val_err("Error casting ROA's IPv6 maxLength: %s",
@@ -87,28 +90,28 @@ ____handle_roa_v6(struct resources *parent, unsigned long asn,
                        }
                        return pr_val_err("The ROA's IPv6 maxLength isn't a valid unsigned long");
                }
-               pr_val_debug("maxLength: %lu", max_length);
+               pr_val_debug("maxLength: %lu", maxlen);
 
-               if (max_length > 128) {
+               if (maxlen > 128) {
                        return pr_val_err("maxLength (%lu) is out of bounds (0-128).",
-                           max_length);
+                           maxlen);
                }
 
-               if (prefix.len > max_length) {
+               if (pfx.len > maxlen) {
                        return pr_val_err("Prefix length (%u) > maxLength (%lu)",
-                           prefix.len, max_length);
+                           pfx.len, maxlen);
                }
 
        } else {
-               max_length = prefix.len;
+               maxlen = pfx.len;
        }
 
-       if (!resources_contains_ipv6(parent, &prefix)) {
+       if (!resources_contains_ipv6(parent, &pfx)) {
                return pr_val_err("ROA is not allowed to advertise %s/%u.",
-                   v6addr2str(&prefix.addr), prefix.len);
+                   addr2str6(&pfx.addr, buf), pfx.len);
        }
 
-       return vhandler_handle_roa_v6(asn, &prefix, max_length);
+       return vhandle_roa_v6(asn, &pfx, maxlen);
 }
 
 static int
index ffa68f8cb69452d1c39a92fd945d27b0d56ca51f..084412ea3d5aed7938fad9ca09c6452941dd1211 100644 (file)
@@ -11,6 +11,7 @@
 #include "file.h"
 #include "log.h"
 #include "object/certificate.h"
+#include "task.h"
 #include "thread_var.h"
 #include "types/path.h"
 #include "types/str.h"
@@ -23,18 +24,6 @@ struct tal {
        size_t spki_len;
 };
 
-struct validation_thread {
-       pthread_t pid;
-       char *tal_file; /* TAL file name */
-       struct db_table *db;
-       int error;
-       /* This should also only be manipulated by the parent thread. */
-       SLIST_ENTRY(validation_thread) next;
-};
-
-/* List of threads, one per TAL file */
-SLIST_HEAD(threads_list, validation_thread);
-
 static char *
 find_newline(char *str)
 {
@@ -146,180 +135,121 @@ tal_get_spki(struct tal *tal, unsigned char const **buffer, size_t *len)
        *len = tal->spki_len;
 }
 
-static void
-__do_file_validation(struct validation_thread *thread)
+static int
+validate_ta(struct tal *tal, struct cache_mapping const *ta_map)
+{
+       struct rpki_certificate *ta;
+       int error;
+
+       ta = pzalloc(sizeof(struct rpki_certificate));
+       map_copy(&ta->map, ta_map);
+       ta->tal = tal;
+       atomic_init(&ta->refcount, 1);
+
+       error = certificate_traverse(ta);
+
+       rpki_certificate_free(ta);
+       return error;
+}
+
+static int
+traverse_tal(char const *tal_path, void *arg)
 {
        struct tal tal;
-       struct validation_handler collector;
-       struct db_table *db;
-       struct validation *state;
        char **url;
        struct cache_mapping map;
+       int error;
 
-       thread->error = tal_init(&tal, thread->tal_file);
-       if (thread->error)
-               return;
-
-       collector.handle_roa_v4 = handle_roa_v4;
-       collector.handle_roa_v6 = handle_roa_v6;
-       collector.handle_router_key = handle_router_key;
-       collector.arg = db = db_table_create();
+       fnstack_push(tal_path);
 
-       thread->error = validation_prepare(&state, &tal, &collector);
-       if (thread->error) {
-               db_table_destroy(db);
+       error = tal_init(&tal, tal_path);
+       if (error)
                goto end1;
-       }
 
+       /* Online attempts */
        ARRAYLIST_FOREACH(&tal.urls, url) {
                map.url = *url;
                map.path = cache_refresh_url(*url);
                if (!map.path)
                        continue;
-               if (traverse_tree(&map, state) != 0)
+               if (validate_ta(&tal, &map) != 0)
                        continue;
                goto end2; /* Happy path */
        }
 
+       /* Offline fallback attempts */
        ARRAYLIST_FOREACH(&tal.urls, url) {
                map.url = *url;
                map.path = cache_fallback_url(*url);
                if (!map.path)
                        continue;
-               if (traverse_tree(&map, state) != 0)
+               if (validate_ta(&tal, &map) != 0)
                        continue;
                goto end2; /* Happy path */
        }
 
        pr_op_err("None of the TAL URIs yielded a successful traversal.");
-       thread->error = EINVAL;
-       db_table_destroy(db);
-       db = NULL;
+       error = EINVAL;
 
-end2:  thread->db = db;
-       validation_destroy(state);
-end1:  tal_cleanup(&tal);
+end2:  tal_cleanup(&tal);
+end1:  fnstack_pop();
+       return error;
 }
 
 static void *
-do_file_validation(void *arg)
+pick_up_work(void *arg)
 {
-       struct validation_thread *thread = arg;
-       time_t start, finish;
-
-       start = time(NULL);
-
-       fnstack_init();
-       fnstack_push(thread->tal_file);
-
-       __do_file_validation(thread);
+       struct validation_task *task = NULL;
 
-       fnstack_cleanup();
+       while ((task = task_dequeue(task)) != NULL)
+               certificate_traverse(task->ca);
 
-       finish = time(NULL);
-       if (start != ((time_t) -1) && finish != ((time_t) -1))
-               pr_op_debug("The %s tree took %.0lf seconds.",
-                   path_filename(thread->tal_file),
-                   difftime(finish, start));
        return NULL;
 }
 
-static void
-thread_destroy(struct validation_thread *thread)
-{
-       free(thread->tal_file);
-       db_table_destroy(thread->db);
-       free(thread);
-}
-
-/* Creates a thread for the @tal_file TAL */
-static int
-spawn_tal_thread(char const *tal_file, void *arg)
-{
-       struct threads_list *threads = arg;
-       struct validation_thread *thread;
-       int error;
-
-       thread = pmalloc(sizeof(struct validation_thread));
-
-       thread->tal_file = pstrdup(tal_file);
-       thread->db = NULL;
-       thread->error = -EINTR;
-       SLIST_INSERT_HEAD(threads, thread, next);
-
-       error = pthread_create(&thread->pid, NULL, do_file_validation, thread);
-       if (error) {
-               pr_op_err("Could not spawn validation thread for %s: %s",
-                   tal_file, strerror(error));
-               free(thread->tal_file);
-               free(thread);
-       }
-
-       return error;
-}
-
-struct db_table *
+int
 perform_standalone_validation(void)
 {
-       struct threads_list threads = SLIST_HEAD_INITIALIZER(threads);
-       struct validation_thread *thread;
-       struct db_table *db = NULL;
+       pthread_t threads[5]; // XXX variabilize
+       unsigned int ids[5];
+       array_index t, t2;
        int error;
-       int tmperr;
 
        error = cache_prepare();
        if (error)
-               return NULL;
-
-       /* TODO (fine) Maybe don't spawn threads if there's only one TAL */
-       if (foreach_file(config_get_tal(), ".tal", true, spawn_tal_thread,
-                        &threads) != 0) {
-               while (!SLIST_EMPTY(&threads)) {
-                       thread = SLIST_FIRST(&threads);
-                       SLIST_REMOVE_HEAD(&threads, next);
-                       thread_destroy(thread);
-               }
+               return error;
+       fnstack_init();
+       task_start();
 
-               /*
-                * Commit even on failure, as there's no reason to throw away
-                * something we recently downloaded if it's marked as valid.
-                */
+       if (foreach_file(config_get_tal(), ".tal", true, traverse_tal, NULL)!=0)
                goto end;
-       }
 
-       /* Wait for all */
-       while (!SLIST_EMPTY(&threads)) {
-               thread = SLIST_FIRST(&threads);
-               tmperr = pthread_join(thread->pid, NULL);
-               if (tmperr)
-                       pr_crit("pthread_join() threw '%s' on the '%s' thread.",
-                           strerror(tmperr), thread->tal_file);
-               SLIST_REMOVE_HEAD(&threads, next);
-               if (thread->error) {
-                       error = thread->error;
-                       pr_op_warn("Validation from TAL '%s' yielded '%s'; "
-                           "discarding all validation results.",
-                           thread->tal_file, strerror(abs(error)));
-               }
-
-               if (!error) {
-                       if (db == NULL) {
-                               db = thread->db;
-                               thread->db = NULL;
-                       } else {
-                               error = db_table_join(db, thread->db);
-                       }
+       for (t = 0; t < 5; t++) {
+               ids[t] = t;
+               error = pthread_create(&threads[t], NULL, pick_up_work, &ids[t]);
+               if (error) {
+                       pr_op_err("Could not spawn validation thread %zu: %s",
+                           t, strerror(error));
+                       break;
                }
-
-               thread_destroy(thread);
        }
 
-       /* If at least one thread had a fatal error, the table is unusable. */
-       if (error) {
-               db_table_destroy(db);
-               db = NULL;
+       if (t == 0) {
+               pick_up_work(NULL);
+               error = 0;
+       } else for (t2 = 0; t2 < t; t2++) {
+               error = pthread_join(threads[t2], NULL);
+               if (error)
+                       pr_crit("pthread_join(%zu) failed: %s",
+                           t2, strerror(error));
        }
 
-end:   cache_commit();
-       return db;
+end:   task_stop();
+       fnstack_cleanup();
+       /*
+        * Commit even on failure, as there's no reason to throw away something
+        * we might have recently downloaded if it managed to be marked valid.
+        */
+       cache_commit();
+       return error;
 }
index 0bf235cd5bac6d29f49a83dd6cd53fafec4bde4b..70f73303e9f527d8aad6569fa778954eb6bb3a96 100644 (file)
@@ -1,15 +1,15 @@
 #ifndef SRC_OBJECT_TAL_H_
 #define SRC_OBJECT_TAL_H_
 
-/* This is RFC 8630. */
+#include "stddef.h"
 
-#include "rtr/db/db_table.h"
+/* This is RFC 8630. */
 
 struct tal;
 
 char const *tal_get_file_name(struct tal *);
 void tal_get_spki(struct tal *, unsigned char const **, size_t *);
 
-struct db_table *perform_standalone_validation(void);
+int perform_standalone_validation(void);
 
 #endif /* SRC_OBJECT_TAL_H_ */
index 67d76f615780182905ffcab25bd833f18b70bac4..4468bccec6a507d4c0b48b5866d88e214394f2d3 100644 (file)
@@ -113,6 +113,7 @@ add_prefix4(struct resources *resources, struct resources *parent,
     IPAddress_t *addr)
 {
        struct ipv4_prefix prefix;
+       char buf[INET_ADDRSTRLEN];
        int error;
 
        if (parent && (resources->ip4s == parent->ip4s))
@@ -126,10 +127,10 @@ add_prefix4(struct resources *resources, struct resources *parent,
                switch (resources->policy) {
                case RPKI_POLICY_RFC6484:
                        return pr_val_err("Parent certificate doesn't own IPv4 prefix '%s/%u'.",
-                           v4addr2str(&prefix.addr), prefix.len);
+                           addr2str4(&prefix.addr, buf), prefix.len);
                case RPKI_POLICY_RFC8360:
                        return pr_val_warn("Certificate is overclaiming the IPv4 prefix '%s/%u'.",
-                           v4addr2str(&prefix.addr), prefix.len);
+                           addr2str4(&prefix.addr, buf), prefix.len);
                }
        }
 
@@ -139,12 +140,12 @@ add_prefix4(struct resources *resources, struct resources *parent,
        error = res4_add_prefix(resources->ip4s, &prefix);
        if (error) {
                pr_val_err("Error adding IPv4 prefix '%s/%u' to certificate resources: %s",
-                   v4addr2str(&prefix.addr), prefix.len,
+                   addr2str4(&prefix.addr, buf), prefix.len,
                    sarray_err2str(error));
                return error;
        }
 
-       pr_val_debug("Prefix: %s/%u", v4addr2str(&prefix.addr), prefix.len);
+       pr_val_debug("Prefix: %s/%u", addr2str4(&prefix.addr, buf), prefix.len);
        return 0;
 }
 
@@ -153,6 +154,7 @@ add_prefix6(struct resources *resources, struct resources *parent,
     IPAddress_t *addr)
 {
        struct ipv6_prefix prefix;
+       char buf[INET6_ADDRSTRLEN];
        int error;
 
        if (parent && (resources->ip6s == parent->ip6s))
@@ -166,10 +168,10 @@ add_prefix6(struct resources *resources, struct resources *parent,
                switch (resources->policy) {
                case RPKI_POLICY_RFC6484:
                        return pr_val_err("Parent certificate doesn't own IPv6 prefix '%s/%u'.",
-                           v6addr2str(&prefix.addr), prefix.len);
+                           addr2str6(&prefix.addr, buf), prefix.len);
                case RPKI_POLICY_RFC8360:
                        return pr_val_warn("Certificate is overclaiming the IPv6 prefix '%s/%u'.",
-                           v6addr2str(&prefix.addr), prefix.len);
+                           addr2str6(&prefix.addr, buf), prefix.len);
                }
        }
 
@@ -179,12 +181,12 @@ add_prefix6(struct resources *resources, struct resources *parent,
        error = res6_add_prefix(resources->ip6s, &prefix);
        if (error) {
                pr_val_err("Error adding IPv6 prefix '%s/%u' to certificate resources: %s",
-                   v6addr2str(&prefix.addr), prefix.len,
+                   addr2str6(&prefix.addr, buf), prefix.len,
                    sarray_err2str(error));
                return error;
        }
 
-       pr_val_debug("Prefix: %s/%u", v6addr2str(&prefix.addr), prefix.len);
+       pr_val_debug("Prefix: %s/%u", addr2str6(&prefix.addr, buf), prefix.len);
        return 0;
 }
 
@@ -208,6 +210,8 @@ add_range4(struct resources *resources, struct resources *parent,
     IPAddressRange_t *input)
 {
        struct ipv4_range range;
+       char buf1[INET_ADDRSTRLEN];
+       char buf2[INET_ADDRSTRLEN];
        int error;
 
        if (parent && (resources->ip4s == parent->ip4s))
@@ -221,10 +225,12 @@ add_range4(struct resources *resources, struct resources *parent,
                switch (resources->policy) {
                case RPKI_POLICY_RFC6484:
                        return pr_val_err("Parent certificate doesn't own IPv4 range '%s-%s'.",
-                           v4addr2str(&range.min), v4addr2str2(&range.max));
+                           addr2str4(&range.min, buf1),
+                           addr2str4(&range.max, buf2));
                case RPKI_POLICY_RFC8360:
                        return pr_val_warn("Certificate is overclaiming the IPv4 range '%s-%s'.",
-                           v4addr2str(&range.min), v4addr2str2(&range.max));
+                           addr2str4(&range.min, buf1),
+                           addr2str4(&range.max, buf2));
                }
        }
 
@@ -234,13 +240,15 @@ add_range4(struct resources *resources, struct resources *parent,
        error = res4_add_range(resources->ip4s, &range);
        if (error) {
                pr_val_err("Error adding IPv4 range '%s-%s' to certificate resources: %s",
-                   v4addr2str(&range.min), v4addr2str2(&range.max),
+                   addr2str4(&range.min, buf1),
+                   addr2str4(&range.max, buf2),
                    sarray_err2str(error));
                return error;
        }
 
-       pr_val_debug("Range: %s-%s", v4addr2str(&range.min),
-           v4addr2str2(&range.max));
+       pr_val_debug("Range: %s-%s",
+           addr2str4(&range.min, buf1),
+           addr2str4(&range.max, buf2));
        return 0;
 }
 
@@ -249,6 +257,8 @@ add_range6(struct resources *resources, struct resources *parent,
     IPAddressRange_t *input)
 {
        struct ipv6_range range;
+       char buf1[INET6_ADDRSTRLEN];
+       char buf2[INET6_ADDRSTRLEN];
        int error;
 
        if (parent && (resources->ip6s == parent->ip6s))
@@ -262,10 +272,12 @@ add_range6(struct resources *resources, struct resources *parent,
                switch (resources->policy) {
                case RPKI_POLICY_RFC6484:
                        return pr_val_err("Parent certificate doesn't own IPv6 range '%s-%s'.",
-                           v6addr2str(&range.min), v6addr2str2(&range.max));
+                           addr2str6(&range.min, buf1),
+                           addr2str6(&range.max, buf2));
                case RPKI_POLICY_RFC8360:
                        return pr_val_warn("Certificate is overclaiming the IPv6 range '%s-%s'.",
-                           v6addr2str(&range.min), v6addr2str2(&range.max));
+                           addr2str6(&range.min, buf1),
+                           addr2str6(&range.max, buf2));
                }
        }
 
@@ -275,13 +287,15 @@ add_range6(struct resources *resources, struct resources *parent,
        error = res6_add_range(resources->ip6s, &range);
        if (error) {
                pr_val_err("Error adding IPv6 range '%s-%s' to certificate resources: %s",
-                   v6addr2str(&range.min), v6addr2str2(&range.max),
+                   addr2str6(&range.min, buf1),
+                   addr2str6(&range.max, buf2),
                    sarray_err2str(error));
                return error;
        }
 
-       pr_val_debug("Range: %s-%s", v6addr2str(&range.min),
-           v6addr2str2(&range.max));
+       pr_val_debug("Range: %s-%s",
+           addr2str6(&range.min, buf1),
+           addr2str6(&range.max, buf2));
        return 0;
 }
 
index abb1ef49c4ff968a25d8e73f53db147538413ca3..8818606ae1282e1b2aeb084b81ca26b015e06237 100644 (file)
@@ -1,8 +1,10 @@
 #include "rtr/db/db_table.h"
 
 #include <errno.h>
+#include <pthread.h>
 
 #include "alloc.h"
+#include "common.h"
 #include "log.h"
 #include "types/uthash.h"
 
@@ -19,6 +21,7 @@ struct hashable_key {
 struct db_table {
        struct hashable_roa *roas;
        struct hashable_key *router_keys;
+       pthread_mutex_t lock;
 };
 
 struct db_table *
@@ -29,6 +32,8 @@ db_table_create(void)
        table = pmalloc(sizeof(struct db_table));
        table->roas = NULL;
        table->router_keys = NULL;
+       panic_on_fail(pthread_mutex_init(&table->lock, NULL),
+           "pthread_mutex_init");
 
        return table;
 }
@@ -52,6 +57,8 @@ db_table_destroy(struct db_table *table)
                free(rk);
        }
 
+       pthread_mutex_destroy(&table->lock);
+
        free(table);
 }
 
@@ -61,9 +68,14 @@ add_roa(struct db_table *table, struct hashable_roa *new)
        struct hashable_roa *old;
        int error;
 
+       mutex_lock(&table->lock);
+
        errno = 0;
        HASH_REPLACE(hh, table->roas, data, sizeof(new->data), new, old);
        error = errno;
+
+       mutex_unlock(&table->lock);
+
        if (error) {
                pr_val_err("ROA couldn't be added to hash table: %s",
                    strerror(error));
@@ -81,9 +93,14 @@ add_router_key(struct db_table *table, struct hashable_key *new)
        struct hashable_key *old;
        int error;
 
+       mutex_lock(&table->lock);
+
        errno = 0;
        HASH_REPLACE(hh, table->router_keys, data, sizeof(new->data), new, old);
        error = errno;
+
+       mutex_unlock(&table->lock);
+
        if (error) {
                pr_val_err("Router Key couldn't be added to hash table: %s",
                    strerror(error));
index d43751c60ee052c279236cde0b2335acc22463f1..b94e8339c41ee0512ecefe98a4a28b702b723fe0 100644 (file)
@@ -12,6 +12,7 @@
 #include "rtr/db/deltas_array.h"
 #include "rtr/pdu.h"
 #include "slurm/slurm_loader.h"
+#include "validation_handler.h"
 
 struct vrp_node {
        struct delta_vrp delta;
@@ -126,41 +127,6 @@ vrps_destroy(void)
                db_table_destroy(state.base);
 }
 
-int
-handle_roa_v4(uint32_t as, struct ipv4_prefix const *prefix,
-    uint8_t max_length, void *arg)
-{
-       return rtrhandler_handle_roa_v4(arg, as, prefix, max_length);
-}
-
-int
-handle_roa_v6(uint32_t as, struct ipv6_prefix const * prefix,
-    uint8_t max_length, void *arg)
-{
-       return rtrhandler_handle_roa_v6(arg, as, prefix, max_length);
-}
-
-int
-handle_router_key(unsigned char const *ski, struct asn_range const *asns,
-    unsigned char const *spk, void *arg)
-{
-       uint64_t asn;
-       int error;
-
-       /*
-        * TODO (warning) Umm... this is begging for a limit.
-        * If the issuer gets it wrong, we can iterate up to 2^32 times.
-        * The RFCs don't seem to care about this.
-        */
-       for (asn = asns->min; asn <= asns->max; asn++) {
-               error = rtrhandler_handle_router_key(arg, ski, asn, spk);
-               if (error)
-                       return error;
-       }
-
-       return 0;
-}
-
 /*
  * High level validator function.
  *
@@ -184,15 +150,20 @@ __vrps_update(bool *changed)
        struct deltas *new_deltas;
        int error;
 
+       vhandle_init();
+
        if (changed != NULL)
                *changed = false;
        old_base = state.base;
-       new_base = NULL;
        new_deltas = NULL;
 
-       new_base = perform_standalone_validation();
-       if (new_base == NULL)
-               return EINVAL;
+       error = perform_standalone_validation();
+       if (error) {
+               db_table_destroy(vhandle_claim());
+               return error;
+       }
+
+       new_base = vhandle_claim();
 
        error = slurm_apply(new_base, &state.slurm);
        if (error) {
index b10e20a387fa206417d686c0fa0d4e6ba63bdd01..aab5b39000c993e9b4c5c64fb222eaa0b386ac22 100644 (file)
@@ -28,11 +28,6 @@ int vrps_foreach_delta_since(serial_t, serial_t *, delta_vrp_foreach_cb,
     delta_router_key_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 *);
-
 uint16_t get_current_session_id(uint8_t);
 
 void vrps_print_base(void);
index 6816c993f52ff49b09d49b845099aafca7b797f2..432d2be54f0102cb0054ee9fd7d590b12b450bc3 100644 (file)
@@ -37,8 +37,6 @@ struct db_slurm {
        struct slurm_csum_list csum_list;
 };
 
-static char addr_buf[INET6_ADDRSTRLEN];
-
 static void
 slurm_bgpsec_wrap_refget(struct slurm_bgpsec_wrap *elem)
 {
@@ -437,20 +435,21 @@ db_slurm_foreach_assertion_bgpsec(struct db_slurm *db, bgpsec_foreach_cb cb,
 static int
 print_prefix_data(struct slurm_prefix *prefix, void *arg)
 {
-       char *pad = "     ";
+       char addr_buf[INET6_ADDRSTRLEN];
 
        pr_op_info("    {");
        if (prefix->data_flag & SLURM_COM_FLAG_ASN)
-               pr_op_info("%s ASN: %u", pad, prefix->vrp.asn);
+               pr_op_info("      ASN: %u", prefix->vrp.asn);
 
        if (prefix->data_flag & SLURM_PFX_FLAG_PREFIX) {
-               pr_op_info("%s Prefix: %s/%u", pad,
+               pr_op_info("      Prefix: %s/%u",
                    inet_ntop(prefix->vrp.addr_fam, &prefix->vrp.prefix,
-                   addr_buf, INET6_ADDRSTRLEN), prefix->vrp.prefix_length);
+                       addr_buf, INET6_ADDRSTRLEN),
+                   prefix->vrp.prefix_length);
        }
 
        if (prefix->data_flag & SLURM_PFX_FLAG_MAX_LENGTH)
-               pr_op_info("%s Max prefix length: %u", pad,
+               pr_op_info("      Max prefix length: %u",
                    prefix->vrp.max_prefix_length);
        pr_op_info("    }");
 
diff --git a/src/state.c b/src/state.c
deleted file mode 100644 (file)
index f32901b..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-#include "state.h"
-
-#include "alloc.h"
-#include "config.h"
-#include "log.h"
-#include "thread_var.h"
-
-/**
- * Just a bunch of thread-specific variables that are too much of a pain
- * to keep passing around.
- *
- * Should be refactored away, honestly.
- */
-struct validation {
-       struct tal *tal;
-
-       struct x509_data {
-               /** https://www.openssl.org/docs/man1.1.1/man3/X509_STORE_load_locations.html */
-               X509_STORE *store;
-               X509_VERIFY_PARAM *params;
-       } x509_data;
-
-       /**
-        * Two buffers calling code will store stringified IP addresses in,
-        * to prevent proliferation of similar buffers on the stack.
-        *
-        * They are meant to be large enough to contain both IPv4 and IPv6
-        * addresses.
-        */
-       char addr_buffer1[INET6_ADDRSTRLEN];
-       char addr_buffer2[INET6_ADDRSTRLEN];
-
-       struct validation_handler validation_handler;
-};
-
-/*
- * It appears that this function is called by LibreSSL whenever it finds an
- * error while validating.
- * It is expected to return "okay" status: Nonzero if the error should be
- * ignored, zero if the error is grounds to abort the validation.
- *
- * Note to myself: During my tests, this function was called in
- * X509_verify_cert(ctx) -> check_chain_extensions(0, ctx),
- * and then twice again in
- * X509_verify_cert(ctx) -> internal_verify(1, ctx).
- *
- * Regarding the ok argument: I'm not 100% sure that I get it; I don't
- * understand why this function would be called with ok = 1.
- * http://openssl.cs.utah.edu/docs/crypto/X509_STORE_CTX_set_verify_cb.html
- * The logic I implemented is the same as the second example: Always ignore the
- * error that's troubling the library, otherwise try to be as unintrusive as
- * possible.
- */
-static int
-cb(int ok, X509_STORE_CTX *ctx)
-{
-       int error;
-
-       /*
-        * We need to handle two new critical extensions (IP Resources and ASN
-        * Resources), so unknown critical extensions are fine as far as
-        * LibreSSL is concerned.
-        * Unfortunately, LibreSSL has no way of telling us *which* is the
-        * unknown critical extension, but since RPKI defines its own set of
-        * valid extensions, we'll have to figure it out later anyway.
-        */
-       error = X509_STORE_CTX_get_error(ctx);
-       return (error == X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION) ? 1 : ok;
-}
-
-/**
- * Creates a struct validation, puts it in thread local, and (incidentally)
- * returns it.
- */
-int
-validation_prepare(struct validation **out, struct tal *tal,
-    struct validation_handler *validation_handler)
-{
-       struct validation *result;
-       X509_VERIFY_PARAM *params;
-       int error;
-
-       result = pmalloc(sizeof(struct validation));
-
-       error = state_store(result);
-       if (error)
-               goto undo_result;
-
-       result->tal = tal;
-
-       result->x509_data.store = X509_STORE_new();
-       if (!result->x509_data.store) {
-               error = val_crypto_err("X509_STORE_new() returned NULL");
-               goto undo_result;
-       }
-
-       params = X509_VERIFY_PARAM_new();
-       if (params == NULL)
-               enomem_panic();
-
-       X509_VERIFY_PARAM_set_flags(params, X509_V_FLAG_CRL_CHECK);
-       if (config_get_validation_time() != 0)
-               X509_VERIFY_PARAM_set_time(params, config_get_validation_time());
-       X509_STORE_set1_param(result->x509_data.store, params);
-       X509_STORE_set_verify_cb(result->x509_data.store, cb);
-
-       result->validation_handler = *validation_handler;
-       result->x509_data.params = params; /* Ownership transfered */
-
-       *out = result;
-       return 0;
-
-undo_result:
-       free(result);
-       return error;
-}
-
-void
-validation_destroy(struct validation *state)
-{
-       X509_VERIFY_PARAM_free(state->x509_data.params);
-       X509_STORE_free(state->x509_data.store);
-       free(state);
-}
-
-struct tal *
-validation_tal(struct validation *state)
-{
-       return (state != NULL) ? state->tal : NULL;
-}
-
-X509_STORE *
-validation_store(struct validation *state)
-{
-       return state->x509_data.store;
-}
-
-char *
-validation_get_ip_buffer1(struct validation *state)
-{
-       return state->addr_buffer1;
-}
-
-char *
-validation_get_ip_buffer2(struct validation *state)
-{
-       return state->addr_buffer2;
-}
-
-struct validation_handler const *
-validation_get_validation_handler(struct validation *state)
-{
-       return &state->validation_handler;
-}
diff --git a/src/state.h b/src/state.h
deleted file mode 100644 (file)
index d168a71..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef SRC_STATE_H_
-#define SRC_STATE_H_
-
-#include <openssl/x509.h>
-
-#include "object/tal.h"
-#include "validation_handler.h"
-
-struct validation;
-
-int validation_prepare(struct validation **, struct tal *,
-    struct validation_handler *);
-void validation_destroy(struct validation *);
-
-struct tal *validation_tal(struct validation *);
-X509_STORE *validation_store(struct validation *);
-
-void validation_pubkey_valid(struct validation *);
-void validation_pubkey_invalid(struct validation *);
-
-char *validation_get_ip_buffer1(struct validation *);
-char *validation_get_ip_buffer2(struct validation *);
-
-struct validation_handler const *
-validation_get_validation_handler(struct validation *);
-
-#endif /* SRC_STATE_H_ */
index 0a4055e1e19d21c232f0c8aca5851f198e3669c7..8ffd905c182c2c80f801e5a8d4c89ac7168c1b64 100644 (file)
@@ -4,6 +4,9 @@
 #include <stdbool.h>
 #include <stddef.h>
 
+#include "alloc.h"
+#include "log.h"
+
 void
 read_stream_init(struct read_stream *stream, int fd)
 {
index f0f343981604e43ba8be8c9dbe074954a5f3ff81..4c9ed95e2eb59cfc14954c9afe9ff1bf27334e79 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef SRC_STREAM_H_
 #define SRC_STREAM_H_
 
+#include <stddef.h>
+
 struct read_stream {
        int fd;
        unsigned char *buffer;
diff --git a/src/task.c b/src/task.c
new file mode 100644 (file)
index 0000000..555f751
--- /dev/null
@@ -0,0 +1,182 @@
+#include "task.h"
+
+#include <errno.h>
+
+#include "alloc.h"
+#include "common.h"
+#include "log.h"
+
+/* Queued, not yet claimed tasks */
+static STAILQ_HEAD(validation_tasks, validation_task) tasks;
+/* Active tasks (length of @tasks plus number of running tasks) */
+static int active;
+
+static bool enabled = true;
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t awakener = PTHREAD_COND_INITIALIZER;
+
+static void
+task_free(struct validation_task *task)
+{
+       rpki_certificate_free(task->ca);
+       free(task);
+}
+
+void
+task_setup(void)
+{
+       STAILQ_INIT(&tasks);
+       active = 0;
+       enabled = true;
+       panic_on_fail(pthread_mutex_init(&lock, NULL), "pthread_mutex_init");
+       panic_on_fail(pthread_cond_init(&awakener, NULL), "pthread_cond_init");
+}
+
+static void
+cleanup(void)
+{
+       struct validation_task *task;
+
+       enabled = false;
+       active = 0;
+       while (!STAILQ_EMPTY(&tasks)) {
+               task = STAILQ_FIRST(&tasks);
+               STAILQ_REMOVE_HEAD(&tasks, lh);
+               task_free(task);
+       }
+}
+
+void
+task_start(void)
+{
+       cleanup();
+       enabled = true;
+}
+
+void
+task_stop(void)
+{
+       mutex_lock(&lock);
+       cleanup();
+       mutex_unlock(&lock);
+}
+
+void
+task_teardown(void)
+{
+       pthread_mutex_destroy(&lock);
+       pthread_cond_destroy(&awakener);
+}
+
+/*
+ * Defers a task for later.
+ * Call task_wakeup() once you've queued all your tasks.
+ */
+unsigned int
+task_enqueue(struct cache_mapping *map, struct rpki_certificate *parent)
+{
+       struct validation_task *task;
+       struct rpki_certificate *ca;
+
+       atomic_fetch_add(&parent->refcount, 1);
+
+       ca = pzalloc(sizeof(struct rpki_certificate));
+       ca->map.url = pstrdup(map->url);
+       ca->map.path = pstrdup(map->path);
+       ca->parent = parent;
+       atomic_init(&ca->refcount, 1);
+
+       task = pmalloc(sizeof(struct validation_task));
+       task->ca = ca;
+
+       mutex_lock(&lock);
+
+       if (enabled) {
+               STAILQ_INSERT_TAIL(&tasks, task, lh);
+               active++;
+       } else {
+               task_free(task);
+       }
+
+       mutex_unlock(&lock);
+
+       return 1;
+}
+
+/* Wakes up threads currently waiting for tasks. */
+void
+task_wakeup(void)
+{
+       mutex_lock(&lock);
+       panic_on_fail(pthread_cond_broadcast(&awakener),
+           "pthread_cond_broadcast");
+       mutex_unlock(&lock);
+}
+
+/*
+ * Frees the @prev previous task, and returns the next one.
+ *
+ * If no task is available yet, will sleep until someone calls task_wakeup().
+ * If all the tasks are done, returns NULL.
+ *
+ * Assumes at least one task has been queued before the first dequeue.
+ */
+struct validation_task *
+task_dequeue(struct validation_task *prev)
+{
+       struct validation_task *task;
+       struct timespec timeout;
+       int error;
+
+       if (prev)
+               task_free(prev);
+       timeout.tv_nsec = 0;
+
+       mutex_lock(&lock);
+
+       if (!enabled)
+               goto end;
+
+       if (prev) {
+               active--;
+               if (active < 0)
+                       pr_crit("active < 0: %d", active);
+       }
+
+       while (active > 0) {
+               pr_op_debug("task_dequeue(): %u tasks active.", active);
+
+               task = STAILQ_FIRST(&tasks);
+               if (task != NULL) {
+                       STAILQ_REMOVE_HEAD(&tasks, lh);
+                       mutex_unlock(&lock);
+                       pr_op_debug("task_dequeue(): Claimed task '%s'.",
+                           task->ca->map.url);
+                       return task;
+               }
+
+               pr_op_debug("task_dequeue(): Sleeping...");
+               timeout.tv_sec = time_fatal() + 10;
+               error = pthread_cond_timedwait(&awakener, &lock, &timeout);
+               switch (error) {
+               case 0:
+                       pr_op_debug("task_dequeue(): Woke up by cond.");
+                       break;
+               case ETIMEDOUT:
+                       pr_op_debug("task_dequeue(): Woke up by timeout.");
+                       break;
+               case EINTR:
+                       pr_op_debug("task_dequeue(): Interrupted by signal.");
+                       goto end;
+               default:
+                       panic_on_fail(error, "pthread_cond_wait");
+               }
+       }
+
+       pr_op_debug("task_dequeue(): No more tasks; done.");
+       panic_on_fail(pthread_cond_broadcast(&awakener),
+           "pthread_cond_broadcast");
+end:   mutex_unlock(&lock);
+       return NULL;
+}
diff --git a/src/task.h b/src/task.h
new file mode 100644 (file)
index 0000000..155e8e4
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef SRC_TASK_H_
+#define SRC_TASK_H_
+
+#include <sys/queue.h>
+
+#include "types/map.h"
+#include "object/certificate.h"
+
+struct validation_task {
+       struct rpki_certificate *ca;
+       STAILQ_ENTRY(validation_task) lh;
+};
+
+void task_setup(void);
+void task_start(void);
+void task_stop(void);
+void task_teardown(void);
+
+unsigned int task_enqueue(struct cache_mapping *, struct rpki_certificate *);
+void task_wakeup(void);
+struct validation_task *task_dequeue(struct validation_task *);
+
+#endif /* SRC_TASK_H_ */
index ab09259c8bdff7e32160d5bde8a8f204ffe6a4a8..c5a817eeac3ef56de600d3a59eb4b58edd300289 100644 (file)
@@ -5,7 +5,6 @@
 #include "alloc.h"
 #include "log.h"
 
-static pthread_key_t state_key;
 static pthread_key_t filenames_key;
 
 struct filename_stack {
@@ -29,14 +28,6 @@ thvar_init(void)
 {
        int error;
 
-       error = pthread_key_create(&state_key, NULL);
-       if (error) {
-               pr_op_err(
-                   "Fatal: Errcode %d while initializing the validation state thread variable.",
-                   error);
-               return error;
-       }
-
        /*
         * Hm. It's a little odd.
         * fnstack_discard() is not being called on program termination.
@@ -54,29 +45,6 @@ thvar_init(void)
        return 0;
 }
 
-/* Puts @state in the current thread's variable pool. Call once per thread. */
-int
-state_store(struct validation *state)
-{
-       int error;
-
-       error = pthread_setspecific(state_key, state);
-       if (error)
-               pr_op_err("pthread_setspecific() returned %d.", error);
-
-       return error;
-}
-
-/*
- * Returns the current thread's validation state. Should not be used outside of
- * validation threads.
- */
-struct validation *
-state_retrieve(void)
-{
-       return pthread_getspecific(state_key);
-}
-
 /** Initializes the current thread's fnstack. Call once per thread. */
 void
 fnstack_init(void)
@@ -188,47 +156,3 @@ fnstack_pop(void)
 
        files->len--;
 }
-
-static char const *
-addr2str(int af, void const *addr, char *(*buffer_cb)(struct validation *))
-{
-       struct validation *state = state_retrieve();
-       return inet_ntop(af, addr, buffer_cb(state), INET6_ADDRSTRLEN);
-}
-
-/*
- * Returns @addr, converted to a printable string. Intended for minimal clutter
- * address printing.
- *
- * The buffer the string is stored in was allocated in a thread variable, so it
- * will be overridden the next time you call this function. Also, you should not
- * free it.
- *
- * The buffer is the same as v6addr2str()'s, so don't mix them either.
- */
-char const *
-v4addr2str(struct in_addr const *addr)
-{
-       return addr2str(AF_INET, addr, validation_get_ip_buffer1);
-}
-
-/* Same as v4addr2str(), except a different buffer is used. */
-char const *
-v4addr2str2(struct in_addr const *addr)
-{
-       return addr2str(AF_INET, addr, validation_get_ip_buffer2);
-}
-
-/* See v4addr2str(). */
-char const *
-v6addr2str(struct in6_addr const *addr)
-{
-       return addr2str(AF_INET6, addr, validation_get_ip_buffer1);
-}
-
-/* See v4addr2str2(). */
-char const *
-v6addr2str2(struct in6_addr const *addr)
-{
-       return addr2str(AF_INET6, addr, validation_get_ip_buffer2);
-}
index 23bca5ae6cede447c98c229877bc53bfd901dbf1..42fb318c70238de81fbe48c84d1451bc9d2809b6 100644 (file)
@@ -1,14 +1,10 @@
 #ifndef SRC_THREAD_VAR_H_
 #define SRC_THREAD_VAR_H_
 
-#include "state.h"
 #include "types/map.h"
 
 int thvar_init(void); /* This function does not need cleanup. */
 
-int state_store(struct validation *);
-struct validation *state_retrieve(void);
-
 void fnstack_init(void);
 void fnstack_cleanup(void);
 
@@ -17,10 +13,4 @@ void fnstack_push_map(struct cache_mapping const *);
 char const *fnstack_peek(void);
 void fnstack_pop(void);
 
-/* Please remember that these functions can only be used during validations. */
-char const *v4addr2str(struct in_addr const *);
-char const *v4addr2str2(struct in_addr const *);
-char const *v6addr2str(struct in6_addr const *);
-char const *v6addr2str2(struct in6_addr const *);
-
 #endif /* SRC_THREAD_VAR_H_ */
index 911015fb48b0cd45e42fb38ab57eb3e35ba18000..6843c653e53719fd6b10c9ebbe5b67d94ebaba0a 100644 (file)
@@ -159,6 +159,7 @@ prefix6_equals(struct ipv6_prefix const *a, struct ipv6_prefix const *b)
 int
 prefix4_decode(IPAddress_t const *str, struct ipv4_prefix *result)
 {
+       char buf[INET_ADDRSTRLEN];
        int len;
 
        if (str->size > 4) {
@@ -183,7 +184,7 @@ prefix4_decode(IPAddress_t const *str, struct ipv4_prefix *result)
 
        if ((result->addr.s_addr & be32_suffix_mask(result->len)) != 0) {
                return pr_val_err("IPv4 prefix '%s/%u' has enabled suffix bits.",
-                   v4addr2str(&result->addr), result->len);
+                   addr2str4(&result->addr, buf), result->len);
        }
 
        return 0;
@@ -196,6 +197,7 @@ int
 prefix6_decode(IPAddress_t const *str, struct ipv6_prefix *result)
 {
        struct in6_addr suffix;
+       char buf[INET6_ADDRSTRLEN];
        int len;
 
        if (str->size > 16) {
@@ -222,7 +224,7 @@ prefix6_decode(IPAddress_t const *str, struct ipv6_prefix *result)
        ipv6_suffix_mask(result->len, &suffix);
        if (addr6_bitwise_and(&result->addr, &suffix)) {
                return pr_val_err("IPv6 prefix '%s/%u' has enabled suffix bits.",
-                   v6addr2str(&result->addr), result->len);
+                   addr2str6(&result->addr, buf), result->len);
        }
 
        return 0;
@@ -231,9 +233,13 @@ prefix6_decode(IPAddress_t const *str, struct ipv6_prefix *result)
 static int
 check_order4(struct ipv4_range *result)
 {
+       char buf1[INET_ADDRSTRLEN];
+       char buf2[INET_ADDRSTRLEN];
+
        if (ntohl(result->min.s_addr) > ntohl(result->max.s_addr)) {
                return pr_val_err("The IPv4 range '%s-%s' is inverted.",
-                   v4addr2str(&result->min), v4addr2str2(&result->max));
+                   addr2str4(&result->min, buf1),
+                   addr2str4(&result->max, buf2));
        }
 
        return 0;
@@ -247,6 +253,8 @@ check_order4(struct ipv4_range *result)
 static int
 check_encoding4(struct ipv4_range *range)
 {
+       char buf1[INET_ADDRSTRLEN];
+       char buf2[INET_ADDRSTRLEN];
        const uint32_t MIN = ntohl(range->min.s_addr);
        const uint32_t MAX = ntohl(range->max.s_addr);
        uint32_t mask;
@@ -260,7 +268,7 @@ check_encoding4(struct ipv4_range *range)
                        return 0;
 
        return pr_val_err("IPAddressRange '%s-%s' is a range, but should have been encoded as a prefix.",
-           v4addr2str(&range->min), v4addr2str2(&range->max));
+           addr2str4(&range->min, buf1), addr2str4(&range->max, buf2));
 }
 
 /**
@@ -295,14 +303,16 @@ check_order6(struct ipv6_range *result)
        uint32_t min;
        uint32_t max;
        unsigned int quadrant;
+       char buf1[INET6_ADDRSTRLEN];
+       char buf2[INET6_ADDRSTRLEN];
 
        for (quadrant = 0; quadrant < 4; quadrant++) {
                min = addr6_get_quadrant(&result->min, quadrant);
                max = addr6_get_quadrant(&result->max, quadrant);
                if (min > max) {
                        return pr_val_err("The IPv6 range '%s-%s' is inverted.",
-                           v6addr2str(&result->min),
-                           v6addr2str2(&result->max));
+                           addr2str6(&result->min, buf1),
+                           addr2str6(&result->max, buf2));
                } else if (min < max) {
                        return 0; /* result->min < result->max */
                }
@@ -314,8 +324,11 @@ check_order6(struct ipv6_range *result)
 static int
 pr_bad_encoding(struct ipv6_range *range)
 {
+       char buf1[INET6_ADDRSTRLEN];
+       char buf2[INET6_ADDRSTRLEN];
        return pr_val_err("IPAddressRange %s-%s is a range, but should have been encoded as a prefix.",
-           v6addr2str(&range->min), v6addr2str2(&range->max));
+           addr2str6(&range->min, buf1),
+           addr2str6(&range->max, buf2));
 }
 
 static int
index 9b02ac1e0a86c1f22cfafd773805a0b3496877f0..34298ebbca7f143323f9527cbff02fe0c03a7b02 100644 (file)
@@ -1,58 +1,52 @@
 #include "validation_handler.h"
 
-#include "log.h"
-#include "thread_var.h"
-
-/*
- * Never returns NULL by contract.
- */
-static struct validation_handler const *
-get_current_threads_handler(void)
-{
-       struct validation_handler const *handler;
+#include "rtr/db/db_table.h"
 
-       handler = validation_get_validation_handler(state_retrieve());
-       if (handler == NULL)
-               pr_crit("This thread lacks a validation handler.");
+static struct db_table *table;
 
-       return handler;
+void
+vhandle_init(void)
+{
+       table = db_table_create();
 }
 
-int
-vhandler_handle_roa_v4(uint32_t as, struct ipv4_prefix const *prefix,
-    uint8_t max_length)
+struct db_table *
+vhandle_claim(void)
 {
-       struct validation_handler const *handler;
-
-       handler = get_current_threads_handler();
-
-       return (handler->handle_roa_v4 != NULL)
-           ? handler->handle_roa_v4(as, prefix, max_length, handler->arg)
-           : 0;
+       struct db_table *result = table;
+       table = NULL;
+       return result;
 }
 
 int
-vhandler_handle_roa_v6(uint32_t as, struct ipv6_prefix const *prefix,
-    uint8_t max_length)
+vhandle_roa_v4(uint32_t as, struct ipv4_prefix const *pfx, uint8_t maxlen)
 {
-       struct validation_handler const *handler;
-
-       handler = get_current_threads_handler();
-
-       return (handler->handle_roa_v6 != NULL)
-           ? handler->handle_roa_v6(as, prefix, max_length, handler->arg)
-           : 0;
+       return rtrhandler_handle_roa_v4(table, as, pfx, maxlen);
 }
 
 int
-vhandler_handle_router_key(unsigned char const *ski,
-    struct asn_range const *asns, unsigned char const *spk)
+vhandle_roa_v6(uint32_t as, struct ipv6_prefix const *pfx, uint8_t maxlen)
 {
-       struct validation_handler const *handler;
-
-       handler = get_current_threads_handler();
+       return rtrhandler_handle_roa_v6(table, as, pfx, maxlen);
+}
 
-       return (handler->handle_router_key != NULL)
-           ? handler->handle_router_key(ski, asns, spk, handler->arg)
-           : 0;
+int
+vhandle_router_key(unsigned char const *ski, struct asn_range const *asns,
+    unsigned char const *spk)
+{
+       uint64_t asn;
+       int error;
+
+       /*
+        * TODO (warning) Umm... this is begging for a limit.
+        * If the issuer gets it wrong, we can iterate up to 2^32 times.
+        * The RFCs don't seem to care about this.
+        */
+       for (asn = asns->min; asn <= asns->max; asn++) {
+               error = rtrhandler_handle_router_key(table, ski, asn, spk);
+               if (error)
+                       return error;
+       }
+
+       return 0;
 }
index 2e284c1f923297e00185646fc1b0ed953d6fc87e..d06ae1478ea464b9199c2c538b1cfc512db0ad6c 100644 (file)
@@ -3,40 +3,15 @@
 
 #include "rtr/db/vrps.h"
 
-/**
- * Functions that handle validation results.
- *
- * At some point, I believe we will end up separating the validator code into a
- * library, so it can be used by other applications aside from Fort's RTR
- * server.
- *
- * This structure is designed with that in mind; it's the callback collection
- * that the library's user application will fill up, so it can do whatever it
- * wants with the validated ROAs.
- *
- * Because it's intended to be used by arbitrary applications, it needs to be
- * generic. Please refrain from adding callbacks that are specifically meant for
- * a particular use case.
- *
- * All of these functions can be NULL.
- */
-struct validation_handler {
-       /** Called every time Fort has successfully validated an IPv4 ROA. */
-       int (*handle_roa_v4)(uint32_t, struct ipv4_prefix const *, uint8_t,
-           void *);
-       /** Called every time Fort has successfully validated an IPv6 ROA. */
-       int (*handle_roa_v6)(uint32_t, struct ipv6_prefix const *, uint8_t,
-           void *);
-       /** 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 *);
-       /** Generic user-defined argument for the functions above. */
-       void *arg;
-};
+void vhandle_init(void);
+struct db_table *vhandle_claim(void);
 
-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 *,
+/* Called every time Fort has successfully validated an IPv4 ROA. */
+int vhandle_roa_v4(uint32_t, struct ipv4_prefix const *, uint8_t);
+/* Called every time Fort has successfully validated an IPv6 ROA. */
+int vhandle_roa_v6(uint32_t, struct ipv6_prefix const *, uint8_t);
+/* Called every time Fort has successfully validated a BGPsec cert. */
+int handle_router_key(unsigned char const *, struct asn_range const *,
     unsigned char const *);
 
 #endif /* SRC_VALIDATION_HANDLER_H_ */
index 11a8e446b9577a3da3ac29af469345090e54d75d..85f9867ce69e575b1f829ad08ec28d5ddc1abee7 100644 (file)
@@ -36,6 +36,7 @@ check_PROGRAMS += rrdp_update.test
 check_PROGRAMS += rsync.test
 check_PROGRAMS += serial.test
 check_PROGRAMS += tal.test
+check_PROGRAMS += task.test
 check_PROGRAMS += thread_pool.test
 check_PROGRAMS += url.test
 check_PROGRAMS += uthash.test
@@ -91,6 +92,9 @@ serial_test_LDADD = ${MY_LDADD}
 tal_test_SOURCES = object/tal_test.c
 tal_test_LDADD = ${MY_LDADD}
 
+task_test_SOURCES = task_test.c
+task_test_LDADD = ${MY_LDADD}
+
 thread_pool_test_SOURCES = thread_pool_test.c
 thread_pool_test_LDADD = ${MY_LDADD}
 
index 89a76fbdbb358ba6b760fdf222023b9ad8fee244..25b5d97ce673d47f966b1dc8ca18efd284ad522d 100644 (file)
@@ -5,7 +5,6 @@
 #include "config.h"
 #include "incidence.h"
 #include "log.h"
-#include "state.h"
 #include "thread_var.h"
 
 /* Some core functions, as linked from unit tests. */
index 1f0c4feb628432d6d36d3e0edc17cdd3e1ef4415..a8388915bf2d9a2378a9c5cda828b3e4852bd8c1 100644 (file)
@@ -25,11 +25,6 @@ MOCK_ABORT_INT(handle_roa_v6, uint32_t as, struct ipv6_prefix const *prefix,
     uint8_t max_length, void *arg)
 MOCK_ABORT_INT(handle_router_key, unsigned char const *ski,
     struct asn_range const *asns, unsigned char const *spk, void *arg)
-MOCK(state_retrieve, struct validation *, NULL, void)
-MOCK_ABORT_VOID(validation_destroy, struct validation *state)
-MOCK_ABORT_INT(validation_prepare, struct validation **out, struct tal *tal,
-    struct validation_handler *validation_handler)
-MOCK(validation_tal, struct tal *, NULL, struct validation *state)
 
 /* Tests */
 
index 491f5956ce4c9c9d0b1c0056277709a3e41303c4..86e15db62904db6c88d2e23dd47c1f435275ca13 100644 (file)
@@ -1,7 +1,8 @@
 #include <check.h>
-#include "object/tal.h"
 
+#include "object/tal.h"
 #include "types/address.c"
+#include "types/asn.h"
 
 static unsigned char db_imp_ski[] = {
     0x0e, 0xe9, 0x6a, 0x8e, 0x2f, 0xac, 0x50, 0xce, 0x6c, 0x5f,
@@ -21,57 +22,35 @@ static unsigned char db_imp_spk[] = {
 
 static int serial = 1;
 
+static struct db_table *test_table;
+
 static void
-add_v4(struct validation_handler *handler, uint32_t as)
+add_v4(struct db_table *table, uint32_t as)
 {
        struct ipv4_prefix prefix;
        prefix.addr.s_addr = htonl(0xC0000200);
        prefix.len = 24;
-       ck_assert_int_eq(0, handler->handle_roa_v4(as, &prefix, 32,
-           handler->arg));
+       ck_assert_int_eq(0, rtrhandler_handle_roa_v4(table, as, &prefix, 32));
 }
 
 static void
-add_v6(struct validation_handler *handler, uint32_t as)
+add_v6(struct db_table *table, uint32_t as)
 {
        struct ipv6_prefix prefix;
        in6_addr_init(&prefix.addr, 0x20010DB8u, 0, 0, 0);
        prefix.len = 96;
-       ck_assert_int_eq(0, handler->handle_roa_v6(as, &prefix, 120,
-           handler->arg));
-}
-
-static void
-add_rk(struct validation_handler *handler, uint32_t as)
-{
-       struct asn_range range = { .min = as, .max = as };
-       ck_assert_int_eq(0, handler->handle_router_key(db_imp_ski, &range,
-           db_imp_spk, handler->arg));
-}
-
-static int
-__handle_roa_v4(uint32_t as, struct ipv4_prefix const *prefix,
-    uint8_t max_length, void *arg)
-{
-       return rtrhandler_handle_roa_v4(arg, as, prefix, max_length);
-}
-
-static int
-__handle_roa_v6(uint32_t as, struct ipv6_prefix const *prefix,
-    uint8_t max_length, void *arg)
-{
-       return rtrhandler_handle_roa_v6(arg, as, prefix, max_length);
+       ck_assert_int_eq(0, rtrhandler_handle_roa_v6(table, as, &prefix, 120));
 }
 
 static int
-__handle_router_key(unsigned char const *ski, struct asn_range const *range,
-    unsigned char const *spk, void *arg)
+__handle_router_key(struct db_table *table, struct asn_range const *range)
 {
        uint64_t as;
        int error;
 
        for (as = range->min; as <= range->max; as++) {
-               error = rtrhandler_handle_router_key(arg, ski, as, spk);
+               error = rtrhandler_handle_router_key(table, db_imp_ski, as,
+                   db_imp_spk);
                if (error)
                        return error;
        }
@@ -79,39 +58,45 @@ __handle_router_key(unsigned char const *ski, struct asn_range const *range,
        return 0;
 }
 
-struct db_table *
-perform_standalone_validation(void)
+static void
+add_rk(struct db_table *table, uint32_t as)
 {
-       struct validation_handler handler;
+       struct asn_range range = { .min = as, .max = as };
+       ck_assert_int_eq(0, __handle_router_key(table, &range));
+}
 
-       handler.handle_roa_v4 = __handle_roa_v4;
-       handler.handle_roa_v6 = __handle_roa_v6;
-       handler.handle_router_key = __handle_router_key;
-       handler.arg = db_table_create();
+void
+vhandle_init(void)
+{
+       test_table = db_table_create();
+}
 
+int
+perform_standalone_validation(void)
+{
        switch (serial) {
        case 1:
-               add_v4(&handler, 0);
-               add_v6(&handler, 0);
-               add_rk(&handler, 0);
+               add_v4(test_table, 0);
+               add_v6(test_table, 0);
+               add_rk(test_table, 0);
                break;
        case 2:
-               add_v4(&handler, 0);
-               add_v6(&handler, 0);
-               add_rk(&handler, 0);
-               add_v4(&handler, 1);
-               add_v6(&handler, 1);
-               add_rk(&handler, 1);
+               add_v4(test_table, 0);
+               add_v6(test_table, 0);
+               add_rk(test_table, 0);
+               add_v4(test_table, 1);
+               add_v6(test_table, 1);
+               add_rk(test_table, 1);
                break;
        case 3:
-               add_v4(&handler, 1);
-               add_v6(&handler, 1);
-               add_rk(&handler, 1);
+               add_v4(test_table, 1);
+               add_v6(test_table, 1);
+               add_rk(test_table, 1);
                break;
        case 4:
-               add_v4(&handler, 0);
-               add_v6(&handler, 0);
-               add_rk(&handler, 0);
+               add_v4(test_table, 0);
+               add_v6(test_table, 0);
+               add_rk(test_table, 0);
                break;
        default:
                ck_abort_msg("perform_standalone_validation() was called too many times (%d).",
@@ -119,5 +104,13 @@ perform_standalone_validation(void)
        }
 
        serial++;
-       return handler.arg;
+       return 0;
+}
+
+struct db_table *
+vhandle_claim(void)
+{
+       struct db_table *tmp = test_table;
+       test_table = NULL;
+       return tmp;
 }
diff --git a/test/task_test.c b/test/task_test.c
new file mode 100644 (file)
index 0000000..fe322e9
--- /dev/null
@@ -0,0 +1,164 @@
+#include "task.c"
+
+#include <check.h>
+
+#include "alloc.c"
+#include "common.c"
+#include "mock.c"
+#include "types/map.c"
+#include "types/path.c"
+
+void
+rpki_certificate_free(struct rpki_certificate *cert)
+{
+       if (atomic_fetch_sub(&cert->refcount, 1) == 1) {
+               map_cleanup(&cert->map);
+               free(cert);
+       }
+}
+
+static void
+queue_1(char *mapstr)
+{
+       struct cache_mapping map;
+       struct rpki_certificate parent = { 0 };
+
+       map.url = map.path = mapstr;
+       ck_assert_int_eq(1, task_enqueue(&map, &parent));
+}
+
+static struct validation_task *
+dequeue_1(char *mapstr, struct validation_task *prev)
+{
+       struct validation_task *task;
+       task = task_dequeue(prev);
+       ck_assert_str_eq(mapstr, task->ca->map.url);
+       return task;
+}
+
+static void
+check_empty(struct validation_task *prev)
+{
+       ck_assert_ptr_eq(NULL, task_dequeue(prev));
+}
+
+static void
+test_1(char *mapstr)
+{
+       queue_1(mapstr);
+       check_empty(dequeue_1(mapstr, NULL));
+}
+
+START_TEST(test_queue_empty)
+{
+       task_setup();
+
+       task_start();
+       ck_assert_ptr_eq(NULL, task_dequeue(NULL));
+       ck_assert_ptr_eq(NULL, task_dequeue(NULL));
+       task_stop();
+
+       task_teardown();
+}
+END_TEST
+
+START_TEST(test_queue_1)
+{
+       task_setup();
+       task_start(); test_1("a"); task_stop();
+       task_teardown();
+}
+END_TEST
+
+START_TEST(test_queue_3)
+{
+       struct validation_task *prev;
+
+       task_setup();
+       task_start();
+
+       queue_1("b");
+       queue_1("c");
+       queue_1("d");
+
+       prev = dequeue_1("b", NULL);
+       prev = dequeue_1("c", prev);
+       prev = dequeue_1("d", prev);
+       check_empty(prev);
+
+       task_stop();
+       task_teardown();
+}
+END_TEST
+
+START_TEST(test_queue_multiple)
+{
+       task_setup();
+
+       task_start(); test_1("a"); task_stop();
+       task_start(); test_1("b"); task_stop();
+       task_start(); test_1("c"); task_stop();
+
+       task_teardown();
+}
+END_TEST
+
+START_TEST(test_queue_interrupted)
+{
+       struct validation_task *prev;
+
+       task_setup();
+
+       task_start();
+       queue_1("1");
+       queue_1("2");
+       task_stop();
+
+       check_empty(NULL);
+       check_empty(NULL);
+
+       task_start();
+       check_empty(NULL);
+       queue_1("3");
+       queue_1("4");
+       prev = dequeue_1("3", NULL);
+       task_stop();
+
+       check_empty(prev);
+
+       task_teardown();
+}
+END_TEST
+
+static Suite *create_suite(void)
+{
+       Suite *suite;
+       TCase *queue;
+
+       queue = tcase_create("queue");
+       tcase_add_test(queue, test_queue_empty);
+       tcase_add_test(queue, test_queue_1);
+       tcase_add_test(queue, test_queue_3);
+       tcase_add_test(queue, test_queue_multiple);
+       tcase_add_test(queue, test_queue_interrupted);
+
+       suite = suite_create("task");
+       suite_add_tcase(suite, queue);
+       return suite;
+}
+
+int main(void)
+{
+       Suite *suite;
+       SRunner *runner;
+       int tests_failed;
+
+       suite = create_suite();
+
+       runner = srunner_create(suite);
+       srunner_run_all(runner, CK_NORMAL);
+       tests_failed = srunner_ntests_failed(runner);
+       srunner_free(runner);
+
+       return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}