-Move directory loading and file filtering to common.h.
-Accept a TALs directory in configuration.
-Don't check for overriden PDUs if there's only 1 delta to send.
-Add roa_table merge function, this allows to merge distinct roa tables so that the VRPs base can have all the data from multiple TALs.
-Remove 'loop' var (isn't necessary) and make 'sigaction act' a global var.
#include "common.h"
#include <errno.h>
+#include <dirent.h>
#include <stdlib.h>
+#include <string.h>
#include "log.h"
int
return error;
}
+static int
+process_file(char const *dir_name, char const *file_name, char const *file_ext,
+ process_file_cb cb, void *arg)
+{
+ char *ext, *fullpath, *tmp;
+ int error;
+
+ if (file_ext != NULL) {
+ ext = strrchr(file_name, '.');
+ /* Ignore file if extension isn't the expected */
+ if (ext == NULL || strcmp(ext, file_ext) != 0)
+ return 0;
+ }
+
+ /* Get the full file path */
+ tmp = strdup(dir_name);
+ if (tmp == NULL)
+ return -pr_errno(errno, "Couldn't create temporal char");
+
+ tmp = realloc(tmp, strlen(tmp) + 1 + strlen(file_name) + 1);
+ if (tmp == NULL)
+ return -pr_errno(errno, "Couldn't reallocate temporal char");
+
+ strcat(tmp, "/");
+ strcat(tmp, file_name);
+ fullpath = realpath(tmp, NULL);
+ if (fullpath == NULL) {
+ free(tmp);
+ return -pr_errno(errno,
+ "Error getting real path for file '%s' at dir '%s'",
+ dir_name, file_name);
+ }
+
+ error = cb(fullpath, arg);
+ free(tmp);
+ free(fullpath);
+ return error;
+}
+
+int
+process_dir_files(char const *location, char const *file_ext,
+ process_file_cb cb, void *arg)
+{
+ DIR *dir_loc;
+ struct dirent *dir_ent;
+ int error;
+
+ dir_loc = opendir(location);
+ if (dir_loc == NULL) {
+ error = -pr_errno(errno, "Couldn't open dir %s", location);
+ goto end;
+ }
+
+ errno = 0;
+ while ((dir_ent = readdir(dir_loc)) != NULL) {
+ error = process_file(location, dir_ent->d_name, file_ext, cb,
+ arg);
+ if (error) {
+ pr_err("The error was at file %s", dir_ent->d_name);
+ goto close_dir;
+ }
+ errno = 0;
+ }
+ if (errno) {
+ pr_err("Error reading dir %s", location);
+ error = -errno;
+ }
+close_dir:
+ closedir(dir_loc);
+end:
+ return error;
+}
/** Also boilerplate. */
int close_thread(pthread_t thread, char const *what);
+typedef int (*process_file_cb)(char const *, void *);
+int process_dir_files(char const *, char const *, process_file_cb, void *);
+
#endif /* SRC_RTR_COMMON_H_ */
* Assuming you don't need to create a data type, that should be all.
*/
struct rpki_config {
- /** TAL file name or directory. TODO (urgent) support directories */
+ /** TAL file name or directory. */
char *tal;
/** Path of our local clone of the repository */
char *local_repository;
.name = "tal",
.type = >_string,
.offset = offsetof(struct rpki_config, tal),
- .doc = "Path to the TAL file",
+ .doc = "Path to the TAL file or TALs directory",
.arg_doc = "<file>",
}, {
.id = 'r',
{
return (rpki_config.tal != NULL)
? 0
- : pr_err("The TAL file (--tal) is mandatory.");
+ : pr_err("The TAL file/directory (--tal) is mandatory.");
}
static void
{
struct validation_handler handler;
+ handler.merge = NULL;
+ handler.merge_arg = NULL;
handler.reset = NULL;
handler.traverse_down = NULL;
handler.traverse_up = NULL;
#include "object/certificate.h"
#include "rsync/rsync.h"
+#define TAL_FILE_EXTENSION ".tal"
+
struct uris {
char **array; /* This is an array of string pointers. */
unsigned int count;
break;
}
} else {
- error = 1;
+ error = vhandler_merge(arg);
+ if (error)
+ error = ENSURE_NEGATIVE(error);
+ else
+ error = 1;
}
end: validation_destroy(state);
return error;
}
-int
-perform_standalone_validation(struct validation_handler *handler)
+static int
+do_file_validation(char const *tal_file, void *arg)
{
struct tal *tal;
int error;
- fnstack_init();
- fnstack_push(config_get_tal());
+ fnstack_push(tal_file);
- error = tal_load(config_get_tal(), &tal);
+ error = tal_load(tal_file, &tal);
if (error)
goto end;
if (config_get_shuffle_tal_uris())
tal_shuffle_uris(tal);
- error = foreach_uri(tal, handle_tal_uri, handler);
+ error = foreach_uri(tal, handle_tal_uri, arg);
if (error > 0)
error = 0;
else if (error == 0)
- error = pr_err("None of the URIs of the TAL yielded a successful traversal.");
+ error = pr_err("None of the URIs of the TAL '%s' yielded a successful traversal.",
+ tal_file);
end: tal_destroy(tal);
fnstack_pop();
+ return error;
+}
+
+int
+perform_standalone_validation(struct validation_handler *handler)
+{
+ char const *config_tal;
+ struct stat attr;
+ int error;
+
+ config_tal = config_get_tal();
+ error = stat(config_tal, &attr);
+ if (error) {
+ pr_errno(errno, "Error reading path '%s'", config_tal);
+ return -errno;
+ }
+
+ fnstack_init();
+ if (S_ISDIR(attr.st_mode) == 0)
+ error = do_file_validation(config_tal, handler);
+ else
+ error = process_dir_files(config_tal, TAL_FILE_EXTENSION,
+ do_file_validation, handler);
+
fnstack_cleanup();
return error;
}
return 0;
}
+static int
+duplicate_roa(struct roa_table *dst, struct hashable_roa *new)
+{
+ struct vrp vrp;
+ struct ipv4_prefix prefix4;
+ struct ipv6_prefix prefix6;
+
+ vrp = new->data;
+ switch (vrp.addr_fam) {
+ case AF_INET:
+ prefix4.addr = vrp.prefix.v4;
+ prefix4.len = vrp.prefix_length;
+ return rtrhandler_handle_roa_v4(dst, vrp.asn, &prefix4,
+ vrp.max_prefix_length);
+ case AF_INET6:
+ prefix6.addr = vrp.prefix.v6;
+ prefix6.len = vrp.prefix_length;
+ return rtrhandler_handle_roa_v6(dst, vrp.asn, &prefix6,
+ vrp.max_prefix_length);
+ }
+ return pr_crit("Unknown address family: %d", vrp.addr_fam);
+}
+
+static int
+roa_table_merge(struct roa_table *dst, struct roa_table *src)
+{
+ struct hashable_roa *node, *tmp, *found;
+ int error;
+
+ /** Must look for it due to the new mem allocation */
+ HASH_ITER(hh, src->roas, node, tmp) {
+ HASH_FIND(hh, dst->roas, &node->data, sizeof(node->data),
+ found);
+ if (found != NULL)
+ continue;
+ error = duplicate_roa(dst, node);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+int
+rtrhandler_merge(struct roa_table *dst, struct roa_table *src)
+{
+ return roa_table_merge(dst, src);
+}
+
void
roa_table_remove_roa(struct roa_table *table, struct vrp *del)
{
struct ipv4_prefix const *, uint8_t);
int rtrhandler_handle_roa_v6(struct roa_table *, uint32_t,
struct ipv6_prefix const *, uint8_t);
+int rtrhandler_merge(struct roa_table *, struct roa_table *);
int compute_deltas(struct roa_table *, struct roa_table *, struct deltas **);
pthread_rwlock_destroy(&lock); /* Nothing to do with error code */
}
+static int
+__merge(void *dst, void *src)
+{
+ return rtrhandler_merge(dst, src);
+}
+
static int
__reset(void *arg)
{
static int
__perform_standalone_validation(struct roa_table **result)
{
- struct roa_table *roas;
+ struct roa_table *roas, *global_roas;
struct validation_handler validation_handler;
int error;
if (roas == NULL)
return pr_enomem();
+ global_roas = roa_table_create();
+ if (global_roas == NULL) {
+ roa_table_destroy(roas);
+ return pr_enomem();
+ }
+
+ validation_handler.merge = __merge;
+ validation_handler.merge_arg = global_roas;
validation_handler.reset = __reset;
validation_handler.traverse_down = NULL;
validation_handler.traverse_up = NULL;
validation_handler.arg = roas;
error = perform_standalone_validation(&validation_handler);
+ roa_table_destroy(roas);
if (error) {
- roa_table_destroy(roas);
+ roa_table_destroy(global_roas);
return error;
}
- *result = roas;
+ *result = global_roas;
return 0;
}
return len;
}
-/* TODO Include Router Key PDU serials */
+/* TODO (next iteration) Include Router Key PDU serials */
/**
* static uint32_t
* length_router_key_pdu(struct router_key_pdu *pdu)
struct vrp_node *ptr;
int error;
+ /** Just 1 delta to send, no need to check for overridden PDUs */
+ if (*common->start_serial == *common->end_serial - 1)
+ return vrps_foreach_delta_roa(*common->start_serial,
+ *common->end_serial, send_prefix_pdu, common);
+
SLIST_INIT(&filtered_vrps);
error = vrps_foreach_delta_roa(*common->start_serial,
*common->end_serial, vrp_ovrd_remove, &filtered_vrps);
/* TODO (next iteration) Support both RTR v0 and v1 */
#define RTR_VERSION_SUPPORTED RTR_V0
-/* TODO (urgent) this needs to be atomic */
-volatile bool loop;
+struct sigaction act;
/*
* Arguments that the server socket thread will send to the client
int err;
uint8_t rtr_version;
- while (loop) { /* For each PDU... */
+ while (true) { /* For each PDU... */
err = pdu_load(param->client_fd, &pdu, &meta, &rtr_version);
if (err)
return end_client(param->client_fd, NULL, NULL);
static int
init_signal_handler(void)
{
- struct sigaction act;
int error;
memset(&act, 0, sizeof act);
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = signal_handler;
- /* TODO is this really legal? @act might need to be global. */
error = sigaction(SIGINT, &act, NULL);
if (error) {
pr_errno(errno, "Error initializing signal handler");
{
struct thread_node *ptr;
- loop = false;
while (!SLIST_EMPTY(&threads)) {
ptr = SLIST_FIRST(&threads);
SLIST_REMOVE_HEAD(&threads, next);
goto revert_server_socket;
/* Init global vars */
- loop = true;
SLIST_INIT(&threads);
error = init_signal_handler();
#include "slurm_loader.h"
-#include <errno.h>
-#include <dirent.h>
#include <stdlib.h>
#include <stdbool.h>
-#include <string.h>
#include "log.h"
#include "config.h"
+#include "common.h"
#include "slurm/slurm_db.h"
#include "slurm/slurm_parser.h"
#define SLURM_FILE_EXTENSION ".slurm"
-static int
-single_slurm_load(const char *dir_name, const char *file_name)
-{
- char *ext, *fullpath, *tmp;
- int error;
-
- ext = strrchr(file_name, '.');
- /* Ignore file if extension isn't the expected */
- if (ext == NULL || strcmp(ext, SLURM_FILE_EXTENSION) != 0)
- return 0;
-
- /* Get the full file path */
- tmp = strdup(dir_name);
- if (tmp == NULL)
- return -pr_errno(errno,
- "Couldn't create temporal char for SLURM");
-
- tmp = realloc(tmp, strlen(tmp) + 1 + strlen(file_name) + 1);
- if (tmp == NULL)
- return -pr_errno(errno,
- "Couldn't reallocate temporal char for SLURM");
-
- strcat(tmp, "/");
- strcat(tmp, file_name);
- fullpath = realpath(tmp, NULL);
- if (fullpath == NULL) {
- free(tmp);
- return -pr_errno(errno,
- "Error getting real path for file '%s' at dir '%s'",
- dir_name, file_name);
- }
-
- error = slurm_parse(fullpath);
- free(tmp);
- free(fullpath);
- return error;
-}
-
static int
slurm_load(bool *loaded)
{
- DIR *dir_loc;
- struct dirent *dir_ent;
- char const *slurm_dir;
- int error;
-
/* Optional configuration */
*loaded = false;
- slurm_dir = config_get_slurm_location();
- if (slurm_dir == NULL)
+ if (config_get_slurm_location() == NULL)
return 0;
*loaded = true;
slurm_db_init();
- dir_loc = opendir(slurm_dir);
- if (dir_loc == NULL) {
- error = -pr_errno(errno, "Couldn't open dir %s", slurm_dir);
- goto end;
- }
-
- errno = 0;
- while ((dir_ent = readdir(dir_loc)) != NULL) {
- error = single_slurm_load(slurm_dir, dir_ent->d_name);
- if (error) {
- pr_err("The error was at SLURM file %s",
- dir_ent->d_name);
- goto close_dir;
- }
- errno = 0;
- }
- if (errno) {
- pr_err("Error reading dir %s", slurm_dir);
- error = -errno;
- }
-close_dir:
- closedir(dir_loc);
-end:
- return error;
+ return process_dir_files(config_get_slurm_location(),
+ SLURM_FILE_EXTENSION, slurm_parse, NULL);
}
static void
error = slurm_pfx_assertions_apply(base);
- /** TODO Apply BGPsec filters and assertions */
+ /** TODO (next iteration) Apply BGPsec filters and assertions */
cleanup:
slurm_cleanup();
static int handle_json(json_t *);
int
-slurm_parse(char const *location)
+slurm_parse(char const *location, void *arg)
{
json_t *json_root;
json_error_t json_error;
return pr_err("'%s' couldn't be decoded", str_encoded);
/*
- * TODO Validate that 'routerPublicKey' is: "the equivalent to the
- * subjectPublicKeyInfo value of the router certificate's public key,
- * as described in [RFC8208]. This is the full ASN.1 DER encoding of
- * the subjectPublicKeyInfo, including the ASN.1 tag and length values
+ * TODO (next iteration) Reuse the functions to validate that
+ * 'routerPublicKey' is: "the equivalent to the subjectPublicKeyInfo
+ * value of the router certificate's public key, as described in
+ * [RFC8208]. This is the full ASN.1 DER encoding of the
+ * subjectPublicKeyInfo, including the ASN.1 tag and length values
* of the subjectPublicKeyInfo SEQUENCE.
- */
- /*
- * TODO When the merge is done, reuse the functions at fort-validator
*
* #include <libcmscodec/SubjectPublicKeyInfo.h>
* #include "asn1/decode.h"
};
-int slurm_parse(char const *);
+int slurm_parse(char const *, void *);
#endif /* SRC_SLURM_SLURM_PARSER_H_ */
#include "log.h"
#include "thread_var.h"
+int
+vhandler_merge(struct validation_handler *handler)
+{
+ return (handler->merge != NULL) ?
+ handler->merge(handler->merge_arg, handler->arg) : 0;
+}
+
int
vhandler_reset(struct validation_handler *handler)
{
struct validation_handler {
/* All of these can be NULL. */
+ int (*merge)(void *, void *);
+ void *merge_arg;
int (*reset)(void *);
int (*traverse_down)(struct rfc5280_name *, void *);
int (*traverse_up)(void *);
void *arg;
};
+int vhandler_merge(struct validation_handler *);
int vhandler_reset(struct validation_handler *);
int vhandler_traverse_down(struct rfc5280_name *);
int vhandler_traverse_up(void);
for (i = 0; i < TOTAL_ROAS; i++)
ck_assert_int_eq(true, roas_found[i]);
- roa_table_put(table);
+ roa_table_destroy(table);
+}
+END_TEST
+
+START_TEST(test_merge)
+{
+ struct ipv4_prefix prefix4;
+ struct ipv6_prefix prefix6;
+ struct roa_table *left, *right, *merged;
+ array_index i;
+ int left_count, right_count, total_merged;
+
+ left = roa_table_create();
+ ck_assert_ptr_ne(NULL, left);
+ right = roa_table_create();
+ ck_assert_ptr_ne(NULL, right);
+ merged = roa_table_create();
+ ck_assert_ptr_ne(NULL, merged);
+
+ prefix4.addr.s_addr = ADDR1;
+ prefix4.len = 24;
+ prefix6.addr.s6_addr32[0] = htonl(0x20010DB8);
+ prefix6.addr.s6_addr32[1] = 0;
+ prefix6.addr.s6_addr32[2] = 0;
+ prefix6.addr.s6_addr32[3] = htonl(0x00000001);
+ prefix6.len = 120;
+
+ left_count = 0;
+ right_count = 0;
+ total_merged = 0;
+
+ /** Add the same roas on both tables*/
+ ck_assert_int_eq(0, rtrhandler_handle_roa_v4(left, 10, &prefix4, 32));
+ left_count++;
+ ck_assert_int_eq(0, rtrhandler_handle_roa_v4(right, 10, &prefix4, 32));
+ right_count++;
+ ck_assert_int_eq(0, rtrhandler_handle_roa_v4(left, 11, &prefix4, 32));
+ left_count++;
+ ck_assert_int_eq(0, rtrhandler_handle_roa_v4(right, 11, &prefix4, 32));
+ right_count++;
+
+ /** And add distinct roas on each table */
+ prefix4.addr.s_addr = ADDR2;
+ ck_assert_int_eq(0, rtrhandler_handle_roa_v4(left, 10, &prefix4, 32));
+ left_count++;
+
+ prefix4.addr.s_addr = ADDR1;
+ prefix4.len = 25;
+ ck_assert_int_eq(0, rtrhandler_handle_roa_v4(right, 10, &prefix4, 32));
+ right_count++;
+
+ prefix4.len = 24;
+ ck_assert_int_eq(0, rtrhandler_handle_roa_v4(left, 10, &prefix4, 30));
+ left_count++;
+
+ /* IPv6 */
+ ck_assert_int_eq(0, rtrhandler_handle_roa_v6(right, 10, &prefix6, 128));
+ right_count++;
+ ck_assert_int_eq(0, rtrhandler_handle_roa_v6(left, 11, &prefix6, 128));
+ left_count++;
+
+ prefix6.addr.s6_addr32[3] = htonl(0x00000002);
+ ck_assert_int_eq(0, rtrhandler_handle_roa_v6(right, 10, &prefix6, 128));
+ right_count++;
+
+ prefix6.addr.s6_addr32[3] = htonl(0x00000001);
+ prefix6.len = 121;
+ ck_assert_int_eq(0, rtrhandler_handle_roa_v6(left, 10, &prefix6, 128));
+ left_count++;
+
+ prefix6.len = 120;
+ ck_assert_int_eq(0, rtrhandler_handle_roa_v6(right, 10, &prefix6, 127));
+ right_count++;
+
+ /** Do the merge */
+ ck_assert_int_eq(0, rtrhandler_merge(merged, left));
+ ck_assert_int_eq(0, rtrhandler_merge(merged, right));
+
+ /**
+ * Must have:
+ * count(left) + count(right) - 2 (duplicated elements)
+ */
+ total_merged = left_count + right_count - 2;
+ ck_assert_int_eq(total_merged, TOTAL_ROAS);
+
+ /* Check table contents and that merged table has new memory refs */
+ roa_table_destroy(left);
+ roa_table_destroy(right);
+
+ memset(roas_found, 0, sizeof(roas_found));
+ total_found = 0;
+ ck_assert_int_eq(0, roa_table_foreach_roa(merged, foreach_cb, NULL));
+ ck_assert_int_eq(TOTAL_ROAS, total_found);
+ for (i = 0; i < TOTAL_ROAS; i++)
+ ck_assert_int_eq(true, roas_found[i]);
+
+ roa_table_destroy(merged);
}
END_TEST
Suite *pdu_suite(void)
{
Suite *suite;
- TCase *core;
+ TCase *core, *merge;
core = tcase_create("Core");
tcase_add_test(core, test_basic);
+ merge = tcase_create("Merge");
+ tcase_add_test(core, test_merge);
+
suite = suite_create("ROA Table");
suite_add_tcase(suite, core);
+ suite_add_tcase(suite, merge);
return suite;
}