Decouples threads from TALs; threads claim RPPs now.
Aside from scaling better, this unclogs the way to several future
improvements.
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
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)
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 *
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"
#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"
#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
error = output_setup();
if (error)
goto revert_vrps;
+ task_setup();
/* Meat */
}
/* End */
+ task_teardown();
revert_vrps:
vrps_destroy();
revert_relax_ng:
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
#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"
}
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:
}
static int
-validate_public_key(X509 *cert, enum cert_type type)
+validate_public_key(struct rpki_certificate *cert)
{
X509_PUBKEY *pubkey;
EVP_PKEY *evppkey;
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");
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);
* 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;
}
/* 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;
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)
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
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)
{
/* 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;
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);
* 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 {
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;
}
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);
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;
-}
#define SRC_OBJECT_CERTIFICATE_H_
#include <openssl/x509.h>
+#include <stdatomic.h>
#include <sys/queue.h>
#include "asn1/asn1c/ANY.h"
#include "cache.h"
#include "certificate_refs.h"
#include "resource.h"
-#include "state.h"
#include "types/rpp.h"
/* Certificate types in the RPKI */
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 *,
*/
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_ */
#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)
____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",
}
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",
}
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
#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"
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)
{
*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;
}
#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_ */
IPAddress_t *addr)
{
struct ipv4_prefix prefix;
+ char buf[INET_ADDRSTRLEN];
int error;
if (parent && (resources->ip4s == parent->ip4s))
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);
}
}
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;
}
IPAddress_t *addr)
{
struct ipv6_prefix prefix;
+ char buf[INET6_ADDRSTRLEN];
int error;
if (parent && (resources->ip6s == parent->ip6s))
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);
}
}
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;
}
IPAddressRange_t *input)
{
struct ipv4_range range;
+ char buf1[INET_ADDRSTRLEN];
+ char buf2[INET_ADDRSTRLEN];
int error;
if (parent && (resources->ip4s == parent->ip4s))
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));
}
}
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;
}
IPAddressRange_t *input)
{
struct ipv6_range range;
+ char buf1[INET6_ADDRSTRLEN];
+ char buf2[INET6_ADDRSTRLEN];
int error;
if (parent && (resources->ip6s == parent->ip6s))
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));
}
}
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;
}
#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"
struct db_table {
struct hashable_roa *roas;
struct hashable_key *router_keys;
+ pthread_mutex_t lock;
};
struct db_table *
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;
}
free(rk);
}
+ pthread_mutex_destroy(&table->lock);
+
free(table);
}
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));
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));
#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;
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.
*
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) {
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);
struct slurm_csum_list csum_list;
};
-static char addr_buf[INET6_ADDRSTRLEN];
-
static void
slurm_bgpsec_wrap_refget(struct slurm_bgpsec_wrap *elem)
{
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(" }");
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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_ */
#include <stdbool.h>
#include <stddef.h>
+#include "alloc.h"
+#include "log.h"
+
void
read_stream_init(struct read_stream *stream, int fd)
{
#ifndef SRC_STREAM_H_
#define SRC_STREAM_H_
+#include <stddef.h>
+
struct read_stream {
int fd;
unsigned char *buffer;
--- /dev/null
+#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;
+}
--- /dev/null
+#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_ */
#include "alloc.h"
#include "log.h"
-static pthread_key_t state_key;
static pthread_key_t filenames_key;
struct filename_stack {
{
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.
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)
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);
-}
#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);
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_ */
int
prefix4_decode(IPAddress_t const *str, struct ipv4_prefix *result)
{
+ char buf[INET_ADDRSTRLEN];
int len;
if (str->size > 4) {
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;
prefix6_decode(IPAddress_t const *str, struct ipv6_prefix *result)
{
struct in6_addr suffix;
+ char buf[INET6_ADDRSTRLEN];
int len;
if (str->size > 16) {
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;
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;
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;
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));
}
/**
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 */
}
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
#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;
}
#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_ */
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
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}
#include "config.h"
#include "incidence.h"
#include "log.h"
-#include "state.h"
#include "thread_var.h"
/* Some core functions, as linked from unit tests. */
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 */
#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,
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;
}
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).",
}
serial++;
- return handler.arg;
+ return 0;
+}
+
+struct db_table *
+vhandle_claim(void)
+{
+ struct db_table *tmp = test_table;
+ test_table = NULL;
+ return tmp;
}
--- /dev/null
+#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;
+}