From: Alberto Leiva Popper Date: Wed, 10 Apr 2019 21:58:11 +0000 (-0500) Subject: Optimize the ROA code X-Git-Tag: v0.0.2~50 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=344ed0973e0f0db6e99b3ece9b3ef3084ba01c49;p=thirdparty%2FFORT-validator.git Optimize the ROA code Changed a bunch of arraylists into a tree. Helps with substantially reducing comparisons needed to compute deltas. --- diff --git a/src/Makefile.am b/src/Makefile.am index c6b56a88..fd901afc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ # Comment these out during releases. # Also increase optimization I guess (-O0 -> -On) -CFLAGS_DEBUG = -DDEBUG +#CFLAGS_DEBUG = -DDEBUG LDFLAGS_DEBUG = -rdynamic bin_PROGRAMS = fort @@ -9,8 +9,8 @@ fort_SOURCES = main.c fort_SOURCES += address.h address.c fort_SOURCES += algorithm.h algorithm.c -fort_SOURCES += array_list.h fort_SOURCES += certificate_refs.h certificate_refs.c +fort_SOURCES += console_handler.h console_handler.c fort_SOURCES += common.h fort_SOURCES += config.h config.c fort_SOURCES += debug.h debug.c @@ -28,6 +28,7 @@ fort_SOURCES += str.h str.c fort_SOURCES += thread_var.h thread_var.c fort_SOURCES += uri.h uri.c fort_SOURCES += json_handler.h json_handler.c +fort_SOURCES += validation_handler.h validation_handler.c fort_SOURCES += rsync/rsync.h rsync/rsync.c @@ -45,9 +46,14 @@ fort_SOURCES += config/types.h fort_SOURCES += config/uint.c config/uint.h fort_SOURCES += config/uint32.c config/uint32.h +fort_SOURCES += data_structure/array_list.h + fort_SOURCES += crypto/base64.h crypto/base64.c fort_SOURCES += crypto/hash.h crypto/hash.c +fort_SOURCES += data_structure/circular_indexer.h +fort_SOURCES += data_structure/circular_indexer.c + fort_SOURCES += object/certificate.h object/certificate.c fort_SOURCES += object/crl.h object/crl.c fort_SOURCES += object/ghostbusters.h object/ghostbusters.c @@ -67,7 +73,6 @@ fort_SOURCES += clients.c clients.h fort_SOURCES += common.c common.h fort_SOURCES += notify.c notify.h fort_SOURCES += updates_daemon.c updates_daemon.h -fort_SOURCES += vrps.c vrps.h fort_SOURCES += rtr/err_pdu.c rtr/err_pdu.h fort_SOURCES += rtr/pdu_handler.c rtr/pdu_handler.h @@ -78,20 +83,25 @@ fort_SOURCES += rtr/primitive_reader.c rtr/primitive_reader.h fort_SOURCES += rtr/primitive_writer.c rtr/primitive_writer.h fort_SOURCES += rtr/rtr.c rtr/rtr.h +fort_SOURCES += rtr/db/delta.c rtr/db/delta.h +fort_SOURCES += rtr/db/roa_tree.c rtr/db/roa_tree.h +fort_SOURCES += rtr/db/roa.c rtr/db/roa.h +fort_SOURCES += rtr/db/vrps.c rtr/db/vrps.h + fort_CFLAGS = -Wall # Feel free to temporarily remove this one if you're not using gcc 7.3.0. -fort_CFLAGS += $(GCC_WARNS) +#fort_CFLAGS += $(GCC_WARNS) fort_CFLAGS += -std=gnu99 -O0 -g $(CFLAGS_DEBUG) fort_LDFLAGS = $(LDFLAGS_DEBUG) fort_LDADD = ${JANSSON_LIBS} # I'm tired of scrolling up, but feel free to comment this out. -#GCC_WARNS = -fmax-errors=1 +GCC_WARNS = -fmax-errors=1 # Please ready some arguments if you want to permanently remove some of these # flags. I gave a quick read to the documentation of all of these warnings, and # generally liked each of them. -GCC_WARNS = -Wpedantic -pedantic-errors -Waddress -Walloc-zero -Walloca +GCC_WARNS += -Wpedantic -pedantic-errors -Waddress -Walloc-zero -Walloca GCC_WARNS += -Wno-aggressive-loop-optimizations -Warray-bounds=2 -Wbool-compare GCC_WARNS += -Wbool-operation -Wno-builtin-declaration-mismatch -Wcast-align GCC_WARNS += -Wcast-qual -Wchar-subscripts -Wchkp -Wclobbered -Wcomment diff --git a/src/address.c b/src/address.c index c37e583c..cdde1ec6 100644 --- a/src/address.c +++ b/src/address.c @@ -112,11 +112,34 @@ ipv6_suffix_mask(unsigned int prefix_len, struct in6_addr *result) } } +bool +prefix4_equals(struct ipv4_prefix const *a, struct ipv4_prefix const *b) +{ + return (a->addr.s_addr == b->addr.s_addr) && (a->len == b->len); +} + +bool +prefix6_equals(struct ipv6_prefix const *a, struct ipv6_prefix const *b) +{ + unsigned int i; + + /* + * Not sure if I can use a memcmp() instead. + * I feel like in6_addr's union could cause padding in weird + * implementations. + */ + for (i = 0; i < 16; i++) + if (a->addr.s6_addr[i] != b->addr.s6_addr[i]) + return false; + + return a->len == b->len; +} + /** * Translates an `IPAddress_t` to its equivalent `struct ipv4_prefix`. */ int -prefix4_decode(IPAddress_t *str, struct ipv4_prefix *result) +prefix4_decode(IPAddress_t const *str, struct ipv4_prefix *result) { int len; @@ -152,7 +175,7 @@ prefix4_decode(IPAddress_t *str, struct ipv4_prefix *result) * Translates an `IPAddress_t` to its equivalent `struct ipv6_prefix`. */ int -prefix6_decode(IPAddress_t *str, struct ipv6_prefix *result) +prefix6_decode(IPAddress_t const *str, struct ipv6_prefix *result) { struct in6_addr suffix; int len; @@ -226,7 +249,7 @@ check_encoding4(struct ipv4_range *range) * Translates an `IPAddressRange_t` to its equivalent `struct ipv4_range`. */ int -range4_decode(IPAddressRange_t *input, struct ipv4_range *result) +range4_decode(IPAddressRange_t const *input, struct ipv4_range *result) { struct ipv4_prefix prefix; int error; @@ -319,7 +342,7 @@ check_encoding6(struct ipv6_range *range) * Translates an `IPAddressRange_t` to its equivalent `struct ipv6_range`. */ int -range6_decode(IPAddressRange_t *input, struct ipv6_range *result) +range6_decode(IPAddressRange_t const *input, struct ipv6_range *result) { struct ipv6_prefix prefix; int error; diff --git a/src/address.h b/src/address.h index 75126079..74dba9c3 100644 --- a/src/address.h +++ b/src/address.h @@ -30,9 +30,12 @@ uint32_t u32_suffix_mask(unsigned int); uint32_t be32_suffix_mask(unsigned int); void ipv6_suffix_mask(unsigned int, struct in6_addr *); -int prefix4_decode(IPAddress_t *, struct ipv4_prefix *); -int prefix6_decode(IPAddress_t *, struct ipv6_prefix *); -int range4_decode(IPAddressRange_t *, struct ipv4_range *); -int range6_decode(IPAddressRange_t *, struct ipv6_range *); +bool prefix4_equals(struct ipv4_prefix const *, struct ipv4_prefix const *); +bool prefix6_equals(struct ipv6_prefix const *, struct ipv6_prefix const *); + +int prefix4_decode(IPAddress_t const *, struct ipv4_prefix *); +int prefix6_decode(IPAddress_t const *, struct ipv6_prefix *); +int range4_decode(IPAddressRange_t const *, struct ipv4_range *); +int range6_decode(IPAddressRange_t const *, struct ipv6_range *); #endif /* SRC_ADDRESS_H_ */ diff --git a/src/asn1/signed_data.c b/src/asn1/signed_data.c index 69671982..0ad8b314 100644 --- a/src/asn1/signed_data.c +++ b/src/asn1/signed_data.c @@ -1,4 +1,4 @@ -#include "signed_data.h" +#include "asn1/signed_data.h" #include #include @@ -31,6 +31,7 @@ signed_object_args_init(struct signed_object_args *args, args->uri = uri; args->crls = crls; memset(&args->refs, 0, sizeof(args->refs)); + args->subject_name = NULL; return 0; } @@ -39,6 +40,8 @@ signed_object_args_cleanup(struct signed_object_args *args) { resources_destroy(args->res); refs_cleanup(&args->refs); + if (args->subject_name != NULL) + x509_name_put(args->subject_name); } static int @@ -93,7 +96,7 @@ handle_sdata_certificate(ANY_t *cert_encoded, struct signed_object_args *args, error = certificate_validate_chain(cert, args->crls); if (error) goto end2; - error = certificate_validate_rfc6487(cert, false); + error = certificate_validate_rfc6487(cert, &args->subject_name, false); if (error) goto end2; error = certificate_validate_extensions_ee(cert, sid, &args->refs, diff --git a/src/asn1/signed_data.h b/src/asn1/signed_data.h index 75ca7089..67ce0ea6 100644 --- a/src/asn1/signed_data.h +++ b/src/asn1/signed_data.h @@ -23,6 +23,8 @@ struct signed_object_args { * recorded for future validation. */ struct certificate_refs refs; + /** Certificate's subject name field */ + struct rfc5280_name *subject_name; }; int signed_object_args_init(struct signed_object_args *, diff --git a/src/clients.c b/src/clients.c index 53d960a1..bd072f37 100644 --- a/src/clients.c +++ b/src/clients.c @@ -1,14 +1,14 @@ #include "clients.h" -#include "array_list.h" #include "common.h" +#include "data_structure/array_list.h" #define SADDR_IN(addr) ((struct sockaddr_in *)addr) #define SADDR_IN6(addr) ((struct sockaddr_in6 *)addr) ARRAY_LIST(clientsdb, struct client) -struct clientsdb clients_db; +static struct clientsdb clients_db; /* Read and Write locks */ sem_t rlock, wlock; diff --git a/src/console_handler.c b/src/console_handler.c new file mode 100644 index 00000000..e10a34e3 --- /dev/null +++ b/src/console_handler.c @@ -0,0 +1,38 @@ +#include "console_handler.h" + +#include "thread_var.h" +#include "validation_handler.h" +#include "object/tal.h" + +static int +print_v4_roa(uint32_t as, struct ipv4_prefix const *prefix, uint8_t max_length, + void *arg) +{ + printf("AS%u,%s/%u,%u\n", as, v4addr2str(&prefix->addr), prefix->len, + max_length); + return 0; +} + +static int +print_v6_roa(uint32_t as, struct ipv6_prefix const *prefix, uint8_t max_length, + void *arg) +{ + printf("AS%u,%s/%u,%u\n", as, v6addr2str(&prefix->addr), prefix->len, + max_length); + return 0; +} + +int +validate_into_console(void) +{ + struct validation_handler handler; + + handler.reset = NULL; + handler.traverse_down = NULL; + handler.traverse_up = NULL; + handler.handle_roa_v4 = print_v4_roa; + handler.handle_roa_v6 = print_v6_roa; + handler.arg = NULL; + + return perform_standalone_validation(&handler); +} diff --git a/src/console_handler.h b/src/console_handler.h new file mode 100644 index 00000000..b712f246 --- /dev/null +++ b/src/console_handler.h @@ -0,0 +1,6 @@ +#ifndef SRC_CONSOLE_HANDLER_H_ +#define SRC_CONSOLE_HANDLER_H_ + +int validate_into_console(void); + +#endif /* SRC_CONSOLE_HANDLER_H_ */ diff --git a/src/array_list.h b/src/data_structure/array_list.h similarity index 66% rename from src/array_list.h rename to src/data_structure/array_list.h index 0453c1f1..595600c5 100644 --- a/src/array_list.h +++ b/src/data_structure/array_list.h @@ -1,25 +1,29 @@ -#ifndef SRC_ARRAY_LIST_H_ -#define SRC_ARRAY_LIST_H_ +#ifndef SRC_DATA_STRUCTURE_ARRAY_LIST_H_ +#define SRC_DATA_STRUCTURE_ARRAY_LIST_H_ #include #include #include "log.h" +#include "data_structure/common.h" -#define ARRAY_LIST(name, elem_type) \ +/* TODO sizes used to be unsigned ints. Check callers. */ +#define DEFINE_ARRAY_LIST_STRUCT(name, elem_type) \ struct name { \ /** Unidimensional array. */ \ elem_type *array; \ /** Number of elements in @array. */ \ - unsigned int len; \ + size_t len; \ /** Actual allocated slots in @array. */ \ - unsigned int capacity; \ - }; \ - \ + size_t capacity; \ + } + +#define DEFINE_ARRAY_LIST_FUNCTIONS(name, elem_type) \ static int \ name##_init(struct name *list) \ { \ list->capacity = 8; \ list->len = 0; \ + /* TODO I need lazy initialization of this badly */ \ list->array = malloc(list->capacity \ * sizeof(elem_type)); \ return (list->array != NULL) ? 0 : pr_enomem(); \ @@ -28,9 +32,11 @@ static void \ name##_cleanup(struct name *list, void (*cb)(elem_type *)) \ { \ - unsigned int i; \ - for (i = 0; i < list->len; i++) \ - cb(&list->array[i]); \ + array_index i; \ + /* TODO recently added this. Use it more */ \ + if (cb != NULL) \ + for (i = 0; i < list->len; i++) \ + cb(&list->array[i]); \ free(list->array); \ } \ \ @@ -55,10 +61,14 @@ return 0; \ } +#define ARRAY_LIST(name, elem_type) \ + DEFINE_ARRAY_LIST_STRUCT(name, elem_type); \ + DEFINE_ARRAY_LIST_FUNCTIONS(name, elem_type) + #define ARRAYLIST_FOREACH(list, cursor) for ( \ cursor = (list)->array; \ (cursor - ((typeof(cursor)) ((list)->array))) < (list)->len; \ cursor++ \ ) -#endif /* SRC_ARRAY_LIST_H_ */ +#endif /* SRC_DATA_STRUCTURE_ARRAY_LIST_H_ */ diff --git a/src/data_structure/circular_indexer.c b/src/data_structure/circular_indexer.c new file mode 100644 index 00000000..f12a3ca6 --- /dev/null +++ b/src/data_structure/circular_indexer.c @@ -0,0 +1,164 @@ +#include "data_structure/circular_indexer.h" + +#include +#include "log.h" + +void +arridx_init(struct circular_indexer *result, size_t len) +{ + result->indexes = NULL; + result->first = len - 1; + result->current = len - 1; + result->top = 0; + result->len = len; + result->allow_another_lap = false; +} + +void +arridx_cleanup(struct circular_indexer *indexer) +{ + if (indexer->indexes != NULL) + free(indexer->indexes); +} + +static struct circular_indexer_node * +get_node(struct circular_indexer *indexer, array_index index) +{ + return &indexer->indexes[index - indexer->top]; +} + +static struct circular_indexer_node * +get_current(struct circular_indexer *indexer) +{ + return get_node(indexer, indexer->current); +} + +array_index * +arridx_first(struct circular_indexer *indexer) +{ + indexer->allow_another_lap = true; + + if (arridx_next(indexer) == NULL) + return NULL; + + indexer->first = indexer->current; + indexer->allow_another_lap = false; + return &indexer->current; +} + +array_index * +arridx_next(struct circular_indexer *indexer) +{ + array_index result; + + if (indexer->len == 0) + return NULL; + + if (indexer->indexes == NULL) { + result = indexer->current + 1; + if ((result - indexer->top) == indexer->len) + result = indexer->top; + } else { + result = get_current(indexer)->next; + } + + if (result == indexer->first) { + if (!indexer->allow_another_lap) + return NULL; + indexer->allow_another_lap = false; + } + + indexer->current = result; + return &indexer->current; +} + +static int +initialize_indexes(struct circular_indexer *indexer) +{ + struct circular_indexer_node *array; + size_t len; + array_index i; + + len = indexer->len; + array = calloc(len, sizeof(struct circular_indexer_node)); + if (array == NULL) + return pr_enomem(); + + array[0].previous = len - 1; + if (len > 1) { + array[0].next = 1; + for (i = 1; i < len - 1; i++) { + array[i].previous = i - 1; + array[i].next = i + 1; + } + array[len - 1].previous = len - 2; + } + array[len - 1].next = 0; + + indexer->indexes = array; + return 0; +} + +int +arridx_remove(struct circular_indexer *indexer) +{ + struct circular_indexer_node *node; + int error; + + if (indexer->len == 0) { + /* + * BTW: This also means that calling code used this function + * outside of a loop, so double no cookies. + */ + return pr_crit("Attempting to remove an element from an empty circular array."); + } + + if (indexer->indexes == NULL) { + if (indexer->top == indexer->current) { + indexer->top++; + if (indexer->first == indexer->current) { + indexer->first++; + indexer->allow_another_lap = true; + } + goto success; + } + + error = initialize_indexes(indexer); + if (error) + return error; + } + + node = get_current(indexer); + + if (indexer->first == indexer->current) { + indexer->first = node->next; + indexer->allow_another_lap = true; + } + + indexer->indexes[node->previous].next = node->next; + indexer->indexes[node->next].previous = node->previous; + +success: + indexer->len--; + return 0; +} + +void +arridx_print(char const *prefix, struct circular_indexer *i) +{ + array_index o; + array_index p; + + pr_info("%s:", prefix); + if (i->indexes != NULL) { + pr_info(" indexes:"); + p = i->first; + for (o = 0; o < i->len; o++) { + pr_info(" %zu", p); + p = i->indexes[p].next; + } + } + + pr_info(" first:%zu current:%zu top:%zu len:%zu allow:%d", i->first, + i->current, i->top, i->len, i->allow_another_lap); +} diff --git a/src/data_structure/circular_indexer.h b/src/data_structure/circular_indexer.h new file mode 100644 index 00000000..f065d8e3 --- /dev/null +++ b/src/data_structure/circular_indexer.h @@ -0,0 +1,108 @@ +#ifndef SRC_DATA_STRUCTURE_CIRCULAR_INDEXER_H_ +#define SRC_DATA_STRUCTURE_CIRCULAR_INDEXER_H_ + +#include +#include "data_structure/common.h" + +/* + * What I call a "circular indexer" is a data structure meant to add *temporal* + * efficient list-like operations to an already existing array. + * + * (The operations are O(1) removal and subsequent circular bidirectional + * iteration. Of course however, creating the indexer is O(n).) + * + * In pragmatic terms, a "circular indexer" is an iterator-like thingy which + * will keep returning removal-sensitive indexes that can be used to dereference + * another array. It's called "circular" because the iteration will wrap + * (although each foreach will stop after one lap.) + * + * It's designed to be used by the ROA tree. While computing deltas, it's useful + * to keep removing elements, not only to efficiently prevent re-traversal of + * already handled nodes, but also to naturally end up with a list of unused + * nodes. At the same time, delta computing is not supposed to destroy the tree. + */ + +struct circular_indexer_node { + array_index previous; + array_index next; +}; + +struct circular_indexer { + /* + * This is the array where we store the links between the nodes. + * + * Will be initialized lazily, because most iterations and removals will + * actually require nothing more than the variables below, and we don't + * want to allocate. + * + * `indexes[i]` always corresponds to `other_array[i + top]` + */ + struct circular_indexer_node *indexes; + + /* + * This is the index of some valid element in @indexes. It's called + * "first" because iteration always begins at this element. + * + * In practice, the code is set up so this points to the successor of + * the element in which the last iteration was interrupted. This is + * because this element has a high chance of being the element that + * calling code is going to be looking up in the next loop. + */ + array_index first; + /** Element the iteration is currently traversing. */ + array_index current; + /** + * Index of the first element that hasn't been removed. + * For example, if @top is 10, then elements 0-9 have been removed, + * and 10-* still exist. + * + * This is a white box optimization. We know that calling code most + * often uses the circular indexer to compare two identical arrays, and + * that any identical elements need to be removed along. + * + * So, most of the time, @top is all that is needed, and we can postpone + * the initialization of @indexes to never. + */ + array_index top; + + /** + * Number of elements remaining. (ie. that haven't been removed.) + * (The length of @indexes is not stored because it's never needed.) + */ + size_t len; + + /** + * Iteration normally stops when @current reaches @first a second time. + * But when the first element is removed, @first now points to the next + * one, so @current will need to "touch" @first again. + * + * This member reminds us that iteration needs to continue the next time + * @current reaches @first. + */ + bool allow_another_lap; +}; + +/* + * Types: + * @indexer: pointer to the struct circular_indexer you want to iterate. + * @i: pointer to array_index. You will have to dereference it to get the index + * cursor. + * + * Every time you start a foreach, iteration will continue where the last one + * stopped. (But will do another full lap.) + */ +#define ARRIDX_FOREACH(indexer, i) \ + for (i = arridx_first(indexer); i != NULL; i = arridx_next(indexer)) + +void arridx_init(struct circular_indexer *, size_t); +void arridx_cleanup(struct circular_indexer *); + +array_index *arridx_first(struct circular_indexer *); +array_index *arridx_next(struct circular_indexer *); +/* Removes the *current* element. (You must be iterating.) */ +int arridx_remove(struct circular_indexer *); + +/* TODO remove me */ +void arridx_print(char const *, struct circular_indexer *); + +#endif /* SRC_DATA_STRUCTURE_CIRCULAR_INDEXER_H_ */ diff --git a/src/data_structure/common.h b/src/data_structure/common.h new file mode 100644 index 00000000..ff5cdc73 --- /dev/null +++ b/src/data_structure/common.h @@ -0,0 +1,8 @@ +#ifndef SRC_DATA_STRUCTURE_COMMON_H_ +#define SRC_DATA_STRUCTURE_COMMON_H_ + +#include + +typedef size_t array_index; + +#endif /* SRC_DATA_STRUCTURE_COMMON_H_ */ diff --git a/src/main.c b/src/main.c index d236e2bc..909eead7 100644 --- a/src/main.c +++ b/src/main.c @@ -1,19 +1,20 @@ #include "clients.h" #include "config.h" +#include "console_handler.h" #include "debug.h" #include "extension.h" #include "nid.h" #include "thread_var.h" -#include "vrps.h" #include "rsync/rsync.h" #include "rtr/rtr.h" +#include "rtr/db/vrps.h" static int start_rtr_server(void) { int error; - error = deltas_db_init(); + error = vrps_init(); if (error) goto end1; @@ -25,7 +26,7 @@ start_rtr_server(void) rtr_cleanup(); /* TODO shouldn't this only happen on !error? */ clients_db_destroy(); -end2: deltas_db_destroy(); +end2: vrps_destroy(); end1: return error; } @@ -54,13 +55,9 @@ main(int argc, char **argv) if (error) goto revert_rsync; - error = perform_standalone_validation(NULL); - if (error) - goto revert_rsync; - - if (config_get_server_address() != NULL) - error = start_rtr_server(); - /* Otherwise, no server requested. */ + error = (config_get_server_address() != NULL) + ? start_rtr_server() + : validate_into_console(); revert_rsync: rsync_destroy(); diff --git a/src/notify.c b/src/notify.c index 79a7cd3e..db19f7e9 100644 --- a/src/notify.c +++ b/src/notify.c @@ -1,9 +1,9 @@ #include "notify.h" #include -#include "rtr/pdu_sender.h" #include "clients.h" -#include "vrps.h" +#include "rtr/pdu_sender.h" +#include "rtr/db/vrps.h" static int send_notify(int fd, uint8_t rtr_version) diff --git a/src/object/certificate.c b/src/object/certificate.c index 968cfe01..fe02b769 100644 --- a/src/object/certificate.c +++ b/src/object/certificate.c @@ -15,9 +15,11 @@ #include "thread_var.h" #include "asn1/decode.h" #include "asn1/oid.h" +#include "asn1/signed_data.h" #include "crypto/hash.h" #include "object/name.h" #include "rsync/rsync.h" +#include "rtr/db/roa_tree.h" #include @@ -76,7 +78,7 @@ static int validate_issuer(X509 *cert, bool is_ta) { X509_NAME *issuer; - struct rfc5280_name name; + struct rfc5280_name *name; int error; issuer = X509_get_issuer_name(cert); @@ -86,16 +88,16 @@ validate_issuer(X509 *cert, bool is_ta) error = x509_name_decode(issuer, "issuer", &name); if (!error) - x509_name_cleanup(&name); + x509_name_put(name); return error; } static int -validate_subject(X509 *cert) +validate_subject(X509 *cert, struct rfc5280_name **subject_name) { struct validation *state; - struct rfc5280_name name; + struct rfc5280_name *name; int error; state = state_retrieve(); @@ -106,11 +108,14 @@ validate_subject(X509 *cert) if (error) return error; - error = validation_store_subject(state, &name); - if (error) - x509_name_cleanup(&name); + error = validation_store_subject(state, name); + if (error) { + x509_name_put(name); + return error; + } - return error; + *subject_name = name; /* Transfer ownership */ + return 0; } static int @@ -264,7 +269,8 @@ validate_public_key(X509 *cert, bool is_root) } int -certificate_validate_rfc6487(X509 *cert, bool is_root) +certificate_validate_rfc6487(X509 *cert, struct rfc5280_name **subject_name, + bool is_root) { int error; @@ -306,7 +312,7 @@ certificate_validate_rfc6487(X509 *cert, bool is_root) * "An issuer SHOULD use a different subject name if the subject's * key pair has changed" (it's a SHOULD, so [for now] avoid validation) */ - error = validate_subject(cert); + error = validate_subject(cert, subject_name); if (error) return error; @@ -1435,6 +1441,7 @@ certificate_traverse(struct rpp *rpp_parent, struct rpki_uri const *cert_uri, { struct validation *state; X509 *cert; + struct rfc5280_name *subject_name; struct rpki_uri mft; struct certificate_refs refs; enum rpki_policy policy; @@ -1455,46 +1462,57 @@ certificate_traverse(struct rpp *rpp_parent, struct rpki_uri const *cert_uri, /* -- Validate the certificate (@cert) -- */ error = certificate_load(cert_uri, &cert); if (error) - goto end1; + goto revert_fnstack_and_debug; if (!is_ta) { error = certificate_validate_chain(cert, crls); if (error) - goto end2; + goto revert_cert; } - error = certificate_validate_rfc6487(cert, is_ta); + error = certificate_validate_rfc6487(cert, &subject_name, is_ta); if (error) - goto end2; + goto revert_cert; error = is_ta ? certificate_validate_extensions_ta(cert, &mft, &policy) : certificate_validate_extensions_ca(cert, &mft, &refs, &policy); if (error) - goto end2; + goto revert_subject_name; error = refs_validate_ca(&refs, is_ta, rpp_parent); if (error) - goto end3; + goto revert_uri_and_refs; /* -- Validate the manifest (@mft) pointed by the certificate -- */ error = validation_push_cert(state, cert_uri, cert, policy, is_ta); if (error) - goto end3; + goto revert_uri_and_refs; error = handle_manifest(&mft, crls, &pp); if (error) - goto end4; + goto revert_cert_push; /* -- Validate & traverse the RPP (@pp) described by the manifest -- */ + error = vhandler_traverse_down(subject_name); + if (error) + goto revert_rpp; + error = rpp_traverse(pp); + if (error) + goto revert_rpp; + + error = vhandler_traverse_up(); +revert_rpp: rpp_destroy(pp); -end4: +revert_cert_push: validation_pop_cert(state); /* Error code is useless. */ -end3: +revert_uri_and_refs: uri_cleanup(&mft); refs_cleanup(&refs); -end2: +revert_subject_name: + x509_name_put(subject_name); +revert_cert: X509_free(cert); -end1: +revert_fnstack_and_debug: fnstack_pop(); pr_debug_rm("}"); return error; diff --git a/src/object/certificate.h b/src/object/certificate.h index 643425d7..2c879d5d 100644 --- a/src/object/certificate.h +++ b/src/object/certificate.h @@ -9,6 +9,7 @@ #include "resource.h" #include "rpp.h" #include "uri.h" +#include "object/name.h" int certificate_load(struct rpki_uri const *, X509 **); @@ -21,7 +22,7 @@ int certificate_validate_chain(X509 *, STACK_OF(X509_CRL) *); * Validates RFC 6487 compliance. * (Except extensions.) */ -int certificate_validate_rfc6487(X509 *, bool); +int certificate_validate_rfc6487(X509 *, struct rfc5280_name **, bool); int certificate_validate_signature(X509 *, ANY_t *coded, SignatureValue_t *); diff --git a/src/object/name.c b/src/object/name.c index 9c07fe5c..2aed1570 100644 --- a/src/object/name.c +++ b/src/object/name.c @@ -7,6 +7,18 @@ #include "log.h" #include "thread_var.h" +/** + * It's an RFC5280 name, but from RFC 6487's perspective. + * Meaning, only commonName and serialNumbers are allowed, and the latter is + * optional. + */ +struct rfc5280_name { + char *commonName; + char *serialNumber; + /** Reference counter */ + unsigned int references; +}; + static int name2string(X509_NAME_ENTRY *name, char **_result) { @@ -30,15 +42,21 @@ name2string(X509_NAME_ENTRY *name, char **_result) int x509_name_decode(X509_NAME *name, char const *what, - struct rfc5280_name *result) + struct rfc5280_name **_result) { + struct rfc5280_name *result; int i; X509_NAME_ENTRY *entry; int nid; int error; + result = malloc(sizeof(struct rfc5280_name)); + if (result == NULL) + return pr_enomem(); + result->commonName = NULL; result->serialNumber = NULL; + result->references = 1; for (i = 0; i < X509_NAME_entry_count(name); i++) { entry = X509_NAME_get_entry(name, i); @@ -66,18 +84,41 @@ x509_name_decode(X509_NAME *name, char const *what, goto fail; } + *_result = result; return 0; fail: - x509_name_cleanup(result); + x509_name_put(result); return error; } void -x509_name_cleanup(struct rfc5280_name *name) +x509_name_get(struct rfc5280_name *name) +{ + name->references++; +} + +void +x509_name_put(struct rfc5280_name *name) +{ + name->references--; + if (name->references == 0) { + free(name->commonName); + free(name->serialNumber); + free(name); + } +} + +char const * +x509_name_commonName(struct rfc5280_name *name) { - free(name->commonName); - free(name->serialNumber); + return name->commonName; +} + +char const * +x509_name_serialNumber(struct rfc5280_name *name) +{ + return name->serialNumber; } /** @@ -106,8 +147,8 @@ validate_issuer_name(char const *container, X509_NAME *issuer) { struct validation *state; X509 *parent; - struct rfc5280_name parent_subject; - struct rfc5280_name child_issuer; + struct rfc5280_name *parent_subject; + struct rfc5280_name *child_issuer; int error; /* @@ -134,22 +175,24 @@ validate_issuer_name(char const *container, X509_NAME *issuer) if (error) goto end; - if (!x509_name_equals(&parent_subject, &child_issuer)) { + if (!x509_name_equals(parent_subject, child_issuer)) { + char const *parent_serial; + char const *child_serial; + + parent_serial = x509_name_serialNumber(parent_subject); + child_serial = x509_name_serialNumber(child_issuer); + error = pr_err("%s's issuer name ('%s%s%s') does not equal issuer certificate's name ('%s%s%s').", container, - parent_subject.commonName, - (parent_subject.serialNumber != NULL) ? "/" : "", - (parent_subject.serialNumber != NULL) - ? parent_subject.serialNumber - : "", - child_issuer.commonName, - (child_issuer.serialNumber != NULL) ? "/" : "", - (child_issuer.serialNumber != NULL) - ? child_issuer.serialNumber - : ""); + x509_name_commonName(parent_subject), + (parent_serial != NULL) ? "/" : "", + (parent_serial != NULL) ? parent_serial : "", + x509_name_commonName(child_issuer), + (child_serial != NULL) ? "/" : "", + (child_serial != NULL) ? child_serial : ""); } - x509_name_cleanup(&child_issuer); -end: x509_name_cleanup(&parent_subject); + x509_name_put(child_issuer); +end: x509_name_put(parent_subject); return error; } diff --git a/src/object/name.h b/src/object/name.h index c686979b..85d487e0 100644 --- a/src/object/name.h +++ b/src/object/name.h @@ -4,18 +4,17 @@ #include #include -/** - * It's an RFC5280 name, but from RFC 6487's perspective. - * Meaning, only commonName and serialNumbers are allowed, and the latter is - * optional. - */ -struct rfc5280_name { - char *commonName; - char *serialNumber; -}; +struct rfc5280_name; -int x509_name_decode(X509_NAME *, char const *, struct rfc5280_name *); -void x509_name_cleanup(struct rfc5280_name *); +/* Constructor */ +int x509_name_decode(X509_NAME *, char const *, struct rfc5280_name **); +/* Reference counting */ +void x509_name_get(struct rfc5280_name *); +void x509_name_put(struct rfc5280_name *); + +/* Getters */ +char const *x509_name_commonName(struct rfc5280_name *); +char const *x509_name_serialNumber(struct rfc5280_name *); bool x509_name_equals(struct rfc5280_name *, struct rfc5280_name *); diff --git a/src/object/roa.c b/src/object/roa.c index 000bea34..7de259ae 100644 --- a/src/object/roa.c +++ b/src/object/roa.c @@ -2,6 +2,7 @@ #include #include +#include #include #include "config.h" @@ -10,8 +11,7 @@ #include "asn1/decode.h" #include "asn1/oid.h" #include "object/signed_object.h" - -#include +#include "rtr/db/roa_tree.h" static int roa_decode(OCTET_STRING_t *string, void *arg) @@ -21,7 +21,7 @@ roa_decode(OCTET_STRING_t *string, void *arg) } static int -print_addr4(struct resources *parent, unsigned long asn, +____handle_roa_v4(struct resources *parent, unsigned long asn, struct ROAIPAddress *roa_addr) { struct ipv4_prefix prefix; @@ -59,18 +59,11 @@ print_addr4(struct resources *parent, unsigned long asn, v4addr2str(&prefix.addr), prefix.len); } - /* TODO I think we're not validating asn boundaries */ - return roa_handle_v4(asn, &prefix, max_length); -} - -int -roa_handle_v4(uint32_t asn, struct ipv4_prefix *prefix, uint8_t max_length) -{ - return -ENOTIMPLEMENTED; + return vhandler_handle_roa_v4(asn, &prefix, max_length); } static int -print_addr6(struct resources *parent, unsigned long asn, +____handle_roa_v6(struct resources *parent, unsigned long asn, struct ROAIPAddress *roa_addr) { struct ipv6_prefix prefix; @@ -108,17 +101,11 @@ print_addr6(struct resources *parent, unsigned long asn, v6addr2str(&prefix.addr), prefix.len); } - return roa_handle_v6(asn, &prefix, max_length); -} - -int -roa_handle_v6(uint32_t asn, struct ipv6_prefix *prefix, uint8_t max_length) -{ - return -ENOTIMPLEMENTED; + return vhandler_handle_roa_v6(asn, &prefix, max_length); } static int -print_addr(struct resources *parent, ASID_t *as_id, uint8_t family, +____handle_roa(struct resources *parent, ASID_t *as_id, uint8_t family, struct ROAIPAddress *roa_addr) { unsigned long asn; @@ -129,11 +116,14 @@ print_addr(struct resources *parent, ASID_t *as_id, uint8_t family, return pr_err("ROA's AS ID couldn't be parsed as unsigned long"); } + if (asn > UINT32_MAX) + return pr_err("AS value (%lu) is out of range.", asn); + switch (family) { case 1: /* IPv4 */ - return print_addr4(parent, asn, roa_addr); + return ____handle_roa_v4(parent, asn, roa_addr); case 2: /* IPv6 */ - return print_addr6(parent, asn, roa_addr); + return ____handle_roa_v6(parent, asn, roa_addr); } return pr_err("Unknown family value: %u", family); @@ -181,7 +171,7 @@ __handle_roa(struct RouteOriginAttestation *roa, struct resources *parent) if (block->addresses.list.array == NULL) return pr_err("ROA's address list array is NULL."); for (a = 0; a < block->addresses.list.count; a++) { - error = print_addr(parent, &roa->asID, + error = ____handle_roa(parent, &roa->asID, block->addressFamily.buf[1], block->addresses.list.array[a]); if (error) @@ -216,10 +206,19 @@ roa_traverse(struct rpki_uri const *uri, struct rpp *pp, if (error) goto end2; + error = vhandler_traverse_down(sobj_args.subject_name); + if (error) + goto end3; + error = __handle_roa(roa, sobj_args.res); if (error) goto end3; + error = vhandler_traverse_up(); + if (error) + goto end3; + + /* TODO why is this happening so late? */ error = refs_validate_ee(&sobj_args.refs, pp, sobj_args.uri); end3: diff --git a/src/object/roa.h b/src/object/roa.h index ed98a22e..961ba3f6 100644 --- a/src/object/roa.h +++ b/src/object/roa.h @@ -9,7 +9,4 @@ int roa_traverse(struct rpki_uri const *, struct rpp *, STACK_OF(X509_CRL) *); -int roa_handle_v4(uint32_t, struct ipv4_prefix *, uint8_t); -int roa_handle_v6(uint32_t, struct ipv6_prefix *, uint8_t); - #endif /* SRC_OBJECT_ROA_H_ */ diff --git a/src/object/tal.c b/src/object/tal.c index 11b799fc..16a69aa8 100644 --- a/src/object/tal.c +++ b/src/object/tal.c @@ -210,7 +210,7 @@ void tal_destroy(struct tal *tal) } int -foreach_uri(struct tal *tal, foreach_uri_cb cb) +foreach_uri(struct tal *tal, foreach_uri_cb cb, void *arg) { struct rpki_uri uri; unsigned int i; @@ -227,7 +227,7 @@ foreach_uri(struct tal *tal, foreach_uri_cb cb) if (error) return error; - error = cb(tal, &uri); + error = cb(tal, &uri, arg); uri_cleanup(&uri); if (error) return error; @@ -273,7 +273,7 @@ tal_get_spki(struct tal *tal, unsigned char const **buffer, size_t *len) * have been extracted from a TAL. */ static int -handle_tal_uri(struct tal *tal, struct rpki_uri const *uri) +handle_tal_uri(struct tal *tal, struct rpki_uri const *uri, void *arg) { /* * Because of the way the foreach iterates, this function must return @@ -298,7 +298,11 @@ handle_tal_uri(struct tal *tal, struct rpki_uri const *uri) uri->global); } - error = validation_prepare(&state, tal); + error = validation_prepare(&state, tal, arg); + if (error) + return ENSURE_NEGATIVE(error); + + error = vhandler_reset(arg); if (error) return ENSURE_NEGATIVE(error); @@ -331,9 +335,8 @@ end: validation_destroy(state); return error; } -/* TODO set @updated */ int -perform_standalone_validation(bool *updated) +perform_standalone_validation(struct validation_handler *handler) { struct tal *tal; int error; @@ -347,7 +350,7 @@ perform_standalone_validation(bool *updated) if (config_get_shuffle_tal_uris()) tal_shuffle_uris(tal); - error = foreach_uri(tal, handle_tal_uri); + error = foreach_uri(tal, handle_tal_uri, handler); if (error > 0) error = 0; else if (error == 0) diff --git a/src/object/tal.h b/src/object/tal.h index a79ee43a..06b53263 100644 --- a/src/object/tal.h +++ b/src/object/tal.h @@ -5,19 +5,20 @@ #include #include "uri.h" +#include "validation_handler.h" struct tal; int tal_load(char const *, struct tal **); void tal_destroy(struct tal *); -typedef int (*foreach_uri_cb)(struct tal *, struct rpki_uri const *); -int foreach_uri(struct tal *, foreach_uri_cb); +typedef int (*foreach_uri_cb)(struct tal *, struct rpki_uri const *, void *); +int foreach_uri(struct tal *, foreach_uri_cb, void *); void tal_shuffle_uris(struct tal *); char const *tal_get_file_name(struct tal *); void tal_get_spki(struct tal *, unsigned char const **, size_t *); -int perform_standalone_validation(bool *); +int perform_standalone_validation(struct validation_handler *); #endif /* TAL_OBJECT_H_ */ diff --git a/src/rpp.c b/src/rpp.c index a546b456..1896355e 100644 --- a/src/rpp.c +++ b/src/rpp.c @@ -1,10 +1,10 @@ #include "rpp.h" #include -#include "array_list.h" #include "log.h" #include "thread_var.h" #include "uri.h" +#include "data_structure/array_list.h" #include "object/certificate.h" #include "object/crl.h" #include "object/ghostbusters.h" diff --git a/src/rtr/db/delta.c b/src/rtr/db/delta.c new file mode 100644 index 00000000..71fa960d --- /dev/null +++ b/src/rtr/db/delta.c @@ -0,0 +1,191 @@ +#include "rtr/db/delta.h" + +#include "data_structure/array_list.h" + +struct delta_v4 { + uint32_t as; + struct ipv4_prefix prefix; + uint8_t max_length; +}; + +struct delta_v6 { + uint32_t as; + struct ipv6_prefix prefix; + uint8_t max_length; +}; + +ARRAY_LIST(deltas_v6, struct delta_v6) +ARRAY_LIST(deltas_v4, struct delta_v4) + +struct deltas { + struct { + struct deltas_v4 adds; + struct deltas_v4 removes; + } v4; + struct { + struct deltas_v6 adds; + struct deltas_v6 removes; + } v6; +}; + +int +deltas_create(struct deltas **_result) +{ + struct deltas *result; + int error; + + result = malloc(sizeof(struct deltas)); + if (result == NULL) + return pr_enomem(); + + error = deltas_v4_init(&result->v4.adds); + if (error) + goto revert_result; + error = deltas_v4_init(&result->v4.removes); + if (error) + goto revert_v4_adds; + error = deltas_v6_init(&result->v6.adds); + if (error) + goto revert_v4_removes; + error = deltas_v6_init(&result->v6.removes); + if (error) + goto revert_v6_adds; + + *_result = result; + return 0; + +revert_v6_adds: + deltas_v6_cleanup(&result->v6.adds, NULL); +revert_v4_removes: + deltas_v4_cleanup(&result->v4.removes, NULL); +revert_v4_adds: + deltas_v4_cleanup(&result->v4.adds, NULL); +revert_result: + free(result); + return error; +} + +void +deltas_destroy(struct deltas *deltas) +{ + deltas_v4_cleanup(&deltas->v4.adds, NULL); + deltas_v4_cleanup(&deltas->v4.removes, NULL); + deltas_v6_cleanup(&deltas->v6.adds, NULL); + deltas_v6_cleanup(&deltas->v6.removes, NULL); + free(deltas); +} + +int +deltas_add_roa_v4(struct deltas *deltas, uint32_t as, struct v4_address *addr, + enum delta_op op) +{ + struct delta_v4 delta = { + .as = as, + .prefix = addr->prefix, + .max_length = addr->max_length, + }; + + switch (op) { + case DELTA_ADD: + return deltas_v4_add(&deltas->v4.adds, &delta); + case DELTA_RM: + return deltas_v4_add(&deltas->v4.removes, &delta); + } + + return pr_crit("Unknown delta operation: %u", op); +} + +int +deltas_add_roa_v6(struct deltas *deltas, uint32_t as, struct v6_address *addr, + enum delta_op op) +{ + struct delta_v6 delta = { + .as = as, + .prefix = addr->prefix, + .max_length = addr->max_length, + }; + + switch (op) { + case DELTA_ADD: + return deltas_v6_add(&deltas->v6.adds, &delta); + case DELTA_RM: + return deltas_v6_add(&deltas->v6.removes, &delta); + } + + return pr_crit("Unknown delta operation: %u", op); +} + +bool +deltas_is_empty(struct deltas *deltas) +{ + return (deltas->v4.adds.len == 0) + && (deltas->v4.removes.len == 0) + && (deltas->v6.adds.len == 0) + && (deltas->v6.removes.len == 0); +} + +static int +__foreach_v4(struct deltas_v4 *array, vrp_foreach_cb cb, void *arg, + uint8_t flags) +{ + struct delta_v4 *d; + struct vrp vrp; + int error; + + vrp.addr_fam = AF_INET; + vrp.flags = flags; + + ARRAYLIST_FOREACH(array, d) { + vrp.asn = d->as; + vrp.prefix.v4 = d->prefix.addr; + vrp.prefix_length = d->prefix.len; + vrp.max_prefix_length = d->max_length; + error = cb(&vrp, arg); + if (error) + return error; + } + + return 0; +} + +static int +__foreach_v6(struct deltas_v6 *array, vrp_foreach_cb cb, void *arg, + uint8_t flags) +{ + struct delta_v6 *d; + struct vrp vrp; + int error; + + vrp.addr_fam = AF_INET6; + vrp.flags = flags; + + ARRAYLIST_FOREACH(array, d) { + vrp.asn = d->as; + vrp.prefix.v6 = d->prefix.addr; + vrp.prefix_length = d->prefix.len; + vrp.max_prefix_length = d->max_length; + error = cb(&vrp, arg); + if (error) + return error; + } + + return 0; +} + +int +deltas_foreach(struct deltas *deltas, vrp_foreach_cb cb, void *arg) +{ + int error; + + error = __foreach_v4(&deltas->v4.adds, cb, arg, FLAG_ANNOUNCEMENT); + if (error) + return error; + error = __foreach_v4(&deltas->v4.removes, cb, arg, FLAG_WITHDRAWAL); + if (error) + return error; + + error = __foreach_v6(&deltas->v6.adds, cb, arg, FLAG_ANNOUNCEMENT); + if (error) + return error; + return __foreach_v6(&deltas->v6.removes, cb, arg, FLAG_WITHDRAWAL); +} diff --git a/src/rtr/db/delta.h b/src/rtr/db/delta.h new file mode 100644 index 00000000..076b0dba --- /dev/null +++ b/src/rtr/db/delta.h @@ -0,0 +1,25 @@ +#ifndef SRC_DELTA_H_ +#define SRC_DELTA_H_ + +#include "rtr/db/roa.h" +#include "rtr/db/vrp.h" + +enum delta_op { + DELTA_ADD, + DELTA_RM, +}; + +struct deltas; + +int deltas_create(struct deltas **); +void deltas_destroy(struct deltas *); + +int deltas_add_roa_v4(struct deltas *, uint32_t, struct v4_address *, + enum delta_op); +int deltas_add_roa_v6(struct deltas *, uint32_t, struct v6_address *, + enum delta_op); + +bool deltas_is_empty(struct deltas *); +int deltas_foreach(struct deltas *, vrp_foreach_cb , void *); + +#endif /* SRC_DELTA_H_ */ diff --git a/src/rtr/db/roa.c b/src/rtr/db/roa.c new file mode 100644 index 00000000..b40bda2e --- /dev/null +++ b/src/rtr/db/roa.c @@ -0,0 +1,83 @@ +#include "rtr/db/roa.h" + +DEFINE_ARRAY_LIST_FUNCTIONS(v4_addresses, struct v4_address) +DEFINE_ARRAY_LIST_FUNCTIONS(v6_addresses, struct v6_address) + +static void +v4_address_destroy(struct v4_address *addr) +{ + free(addr); +} + +static void +v6_address_destroy(struct v6_address *addr) +{ + free(addr); +} + +int +roa_create(u_int32_t as, struct roa **_result) +{ + struct roa *result; + int error; + + result = malloc(sizeof(struct roa)); + if (result == NULL) + return pr_enomem(); + + result->as = as; + error = v4_addresses_init(&result->addrs4); + if (error) + goto revert_result; + error = v6_addresses_init(&result->addrs6); + if (error) + goto revert_addrs4; + + *_result = result; + return 0; + +revert_addrs4: + v4_addresses_cleanup(&result->addrs4, v4_address_destroy); +revert_result: + free(result); + return error; +} + +void +roa_destroy(struct roa *roa) +{ + v4_addresses_cleanup(&roa->addrs4, v4_address_destroy); + v6_addresses_cleanup(&roa->addrs6, v6_address_destroy); +} + +int +roa_add_v4(struct roa *roa, uint32_t as, struct ipv4_prefix const *prefix, + uint8_t max_length) +{ + struct v4_address addr; + + if (roa->as != as) { + return pr_err("ROA has more than one ASN. (%u and %u)", + roa->as, as); + } + + addr.prefix = *prefix; + addr.max_length = max_length; + return v4_addresses_add(&roa->addrs4, &addr); +} + +int +roa_add_v6(struct roa *roa, uint32_t as, struct ipv6_prefix const *prefix, + uint8_t max_length) +{ + struct v6_address addr; + + if (roa->as != as) { + return pr_err("ROA has more than one ASN. (%u and %u)", + roa->as, as); + } + + addr.prefix = *prefix; + addr.max_length = max_length; + return v6_addresses_add(&roa->addrs6, &addr); +} diff --git a/src/rtr/db/roa.h b/src/rtr/db/roa.h new file mode 100644 index 00000000..8271d9b8 --- /dev/null +++ b/src/rtr/db/roa.h @@ -0,0 +1,32 @@ +#ifndef SRC_RTR_DB_ROA_H_ +#define SRC_RTR_DB_ROA_H_ + +#include "address.h" +#include "data_structure/array_list.h" + +struct v4_address { + struct ipv4_prefix prefix; + uint8_t max_length; +}; + +struct v6_address { + struct ipv6_prefix prefix; + uint8_t max_length; +}; + +DEFINE_ARRAY_LIST_STRUCT(v4_addresses, struct v4_address); +DEFINE_ARRAY_LIST_STRUCT(v6_addresses, struct v6_address); + +struct roa { + uint32_t as; + struct v4_addresses addrs4; + struct v6_addresses addrs6; +}; + +int roa_create(u_int32_t, struct roa **); +void roa_destroy(struct roa *); + +int roa_add_v4(struct roa *, uint32_t, struct ipv4_prefix const *, uint8_t); +int roa_add_v6(struct roa *, uint32_t, struct ipv6_prefix const *, uint8_t); + +#endif /* SRC_RTR_DB_ROA_H_ */ diff --git a/src/rtr/db/roa_tree.c b/src/rtr/db/roa_tree.c new file mode 100644 index 00000000..86d84df7 --- /dev/null +++ b/src/rtr/db/roa_tree.c @@ -0,0 +1,525 @@ +#include "rtr/db/roa_tree.h" + +#include "common.h" +#include "data_structure/array_list.h" +#include "data_structure/circular_indexer.h" +#include "rtr/db/roa.h" + +DEFINE_ARRAY_LIST_STRUCT(nodes, struct node); + +struct node { + struct rfc5280_name *subject_name; + struct node *parent; + /* + * BTW: There's nothing in this code stopping both children and roa from + * being not null, but it should never happen naturally. + */ + struct nodes children; + struct roa *roa; +}; + +struct roa_tree { + struct node *root; + struct node *current; + unsigned int references; +}; + +DEFINE_ARRAY_LIST_FUNCTIONS(nodes, struct node) + +static struct node * +node_create(struct rfc5280_name *subject_name, struct node *parent) +{ + struct node *node; + + node = malloc(sizeof(struct node)); + if (node == NULL) + return NULL; + + node->subject_name = subject_name; + x509_name_get(subject_name); + node->parent = parent; + node->children.array = NULL; + node->roa = NULL; + return node; +} + +static void +node_destroy(struct node *node) +{ + x509_name_put(node->subject_name); + if (node->children.array != NULL) + nodes_cleanup(&node->children, node_destroy); + if (node->roa != NULL) + roa_destroy(node->roa); + free(node); +} + +static int +node_add_child(struct node *parent, struct rfc5280_name *subject_name) +{ + struct node *child; + int error; + + if (parent->children.array == NULL) { + error = nodes_init(&parent->children); + if (error) + return error; + } + + child = node_create(subject_name, parent); + if (child == NULL) + return pr_enomem(); + + error = nodes_add(&parent->children, child); + if (error) + node_destroy(child); + + return error; +} + +/** + * Performs lazy initialization if the root does not exist. + */ +static struct node * +get_root(struct roa_tree *tree, struct rfc5280_name *subject_name) +{ + if (tree->root == NULL) + tree->root = node_create(subject_name, NULL); + return tree->root; +} + +struct roa_tree * +roa_tree_create(void) +{ + struct roa_tree *tree; + + tree = malloc(sizeof(struct roa_tree)); + if (tree == NULL) + return NULL; + + tree->root = NULL; + tree->current = NULL; + tree->references = 1; + return tree; +} + +static void +roa_tree_cleanup(struct roa_tree *tree) +{ + if (tree->root != NULL) { + node_destroy(tree->root); + tree->root = NULL; + } + tree->current = NULL; +} + +void +roa_tree_get(struct roa_tree *tree) +{ + tree->references++; +} + +void +roa_tree_put(struct roa_tree *tree) +{ + tree->references--; + if (tree->references == 0) { + roa_tree_cleanup(tree); + free(tree); + } +} + +static int +__foreach_v4(struct roa *roa, vrp_foreach_cb cb, void *arg) +{ + struct v4_address *addr; + struct vrp vrp; + int error; + + vrp.asn = roa->as; + vrp.addr_fam = AF_INET; + vrp.flags = FLAG_ANNOUNCEMENT; + + ARRAYLIST_FOREACH(&roa->addrs4, addr) { + vrp.prefix.v4 = addr->prefix.addr; + vrp.prefix_length = addr->prefix.len; + vrp.max_prefix_length = addr->max_length; + error = cb(&vrp, arg); + if (error) + return error; + } + + return 0; +} + +static int +__foreach_v6(struct roa *roa, vrp_foreach_cb cb, void *arg) +{ + struct v6_address *addr; + struct vrp vrp; + int error; + + vrp.asn = roa->as; + vrp.addr_fam = AF_INET6; + vrp.flags = FLAG_ANNOUNCEMENT; + + ARRAYLIST_FOREACH(&roa->addrs6, addr) { + vrp.prefix.v6 = addr->prefix.addr; + vrp.prefix_length = addr->prefix.len; + vrp.max_prefix_length = addr->max_length; + error = cb(&vrp, arg); + if (error) + return error; + } + + return 0; +} + +int +__foreach(struct node *node, vrp_foreach_cb cb, void *arg) +{ + struct node *child; + int error; + + ARRAYLIST_FOREACH(&node->children, child) { + error = __foreach(child, cb, arg); + if (error) + return error; + } + + if (child->roa != NULL) { + error = __foreach_v4(child->roa, cb, arg); + if (error) + return error; + error = __foreach_v6(child->roa, cb, arg); + if (error) + return error; + } + + return 0; +} + +int +roa_tree_foreach_roa(struct roa_tree *tree, vrp_foreach_cb cb, void *arg) +{ + return (tree->root != NULL) ? __foreach(tree->root, cb, arg) : 0; +} + +int +forthandler_reset(struct roa_tree *tree) +{ + roa_tree_cleanup(tree); + return 0; +} + +int +forthandler_go_down(struct roa_tree *tree, + struct rfc5280_name *subject_name) +{ + if (tree->current != NULL) + return node_add_child(tree->current, subject_name); + + tree->current = get_root(tree, subject_name); + return (tree->current != NULL) ? 0 : pr_enomem(); +} + +int +forthandler_go_up(struct roa_tree *tree) +{ + if (tree->current == NULL) + return pr_crit("Bad tree traversal: Attempting to move to the parent of a root."); + + tree->current = tree->current->parent; + return 0; +} + +static int +get_current_roa(struct roa_tree *tree, uint32_t asn, struct roa **result) +{ + struct roa *roa; + int error; + + if (tree->current == NULL) + return pr_crit("Validator posted ROA during incorrect context."); + + roa = tree->current->roa; + if (roa == NULL) { + error = roa_create(asn, &roa); + if (error) + return error; + tree->current->roa = roa; + } + + *result = roa; + return 0; +} + +int +forthandler_handle_roa_v4(struct roa_tree *tree, uint32_t asn, + struct ipv4_prefix const *prefix4, uint8_t max_length) +{ + struct roa *roa; + int error; + error = get_current_roa(tree, asn, &roa); + return error ? error : roa_add_v4(roa, asn, prefix4, max_length); +} + +int +forthandler_handle_roa_v6(struct roa_tree *tree, uint32_t asn, + struct ipv6_prefix const *prefix6, uint8_t max_length) +{ + struct roa *roa; + int error; + error = get_current_roa(tree, asn, &roa); + return error ? error : roa_add_v6(roa, asn, prefix6, max_length); +} + +static bool +find_subject_name(struct circular_indexer *indexer, + struct rfc5280_name *subject_name, struct node *c2array, + array_index *result) +{ + array_index *i; + + ARRIDX_FOREACH(indexer, i) { + if (x509_name_equals(subject_name, c2array[*i].subject_name)) { + *result = *i; + return true; + } + } + + return false; +} + +static int +add_all_roas_v4(struct deltas *deltas, struct roa *roa, enum delta_op op) +{ + struct v4_address *addr; + int error; + + ARRAYLIST_FOREACH(&roa->addrs4, addr) { + error = deltas_add_roa_v4(deltas, roa->as, addr, op); + if (error) + return error; + } + + return 0; +} + +static int +add_all_roas_v6(struct deltas *deltas, struct roa *roa, enum delta_op op) +{ + struct v6_address *addr; + int error; + + ARRAYLIST_FOREACH(&roa->addrs6, addr) { + error = deltas_add_roa_v6(deltas, roa->as, addr, op); + if (error) + return error; + } + + return 0; +} + +static int +add_all_deltas(struct node *node, struct deltas *deltas, enum delta_op op) +{ + struct node *child; + int error; + + ARRAYLIST_FOREACH(&node->children, child) { + error = add_all_deltas(child, deltas, op); + if (error) + return error; + } + + if (child->roa != NULL) { + error = add_all_roas_v4(deltas, child->roa, op); + if (error) + return error; + error = add_all_roas_v6(deltas, child->roa, op); + if (error) + return error; + } + + return 0; +} + +static int compute_deltas_node(struct node *, struct node *, struct deltas *); + +static int +handle_delta_children(struct nodes *children1, struct nodes *children2, + struct deltas *deltas) +{ + /* + * Most of the time, the arrays will be identical. + * When they are not, most of the time the arrays will be mostly + * identical. + * + * We will try our hardest to traverse the arrays as sequentially as + * possible to exploit these facts. + * + * Notice that this is the same algorithm as HANDLE_ROAS_FN(). + * Changes to one function might need to cascade to the other. + */ + + struct node *c1array; + array_index c1; /* counter for c1array */ + + struct node *c2array; + array_index c2; /* counter for c2array */ + array_index *c2p; /* counter for c2array, pointer */ + + struct circular_indexer c2indexer; + + int error = 0; + + c1array = children1->array; + c2array = children2->array; + arridx_init(&c2indexer, children2->len); + + for (c1 = 0; c1 < children1->len; c1++) { + if (find_subject_name(&c2indexer, c1array[c1].subject_name, + c2array, &c2)) { + error = compute_deltas_node(&c1array[c1], &c2array[c2], + deltas); + if (error) + goto end; + + error = arridx_remove(&c2indexer); + } else { + error = add_all_deltas(&c1array[c1], deltas, DELTA_RM); + } + if (error) + goto end; + } + + ARRIDX_FOREACH(&c2indexer, c2p) { + error = add_all_deltas(&c2array[*c2p], deltas, DELTA_ADD); + if (error) + goto end; + } + +end: arridx_cleanup(&c2indexer); + return error; +} + +static bool +find_addr_v4(struct v4_address *address, struct v4_addresses *array, + struct circular_indexer *indexer) +{ + array_index *i; + + ARRIDX_FOREACH(indexer, i) + if (prefix4_equals(&address->prefix, &array->array[*i].prefix)) + return true; + + return false; +} + +static bool +find_addr_v6(struct v6_address *address, struct v6_addresses *array, + struct circular_indexer *indexer) +{ + array_index *i; + + ARRIDX_FOREACH(indexer, i) + if (prefix6_equals(&address->prefix, &array->array[*i].prefix)) + return true; + + return false; +} + +#define HANDLE_ROAS_FN(name, array_type, node_type, field, find_fn, \ + add_one_fn, add_all_fn) \ + static int \ + name(struct roa *roa1, struct roa *roa2, struct deltas *deltas) \ + { \ + /* Notice that this is the same algorithm as */ \ + /* handle_delta_children(). Changes to one function */ \ + /* might need to cascade to the other. */ \ + \ + struct array_type *addrs1; \ + struct node_type *a1; /* address cursor for addrs1 */ \ + \ + struct array_type *addrs2; \ + array_index *a2p; /* counter for addrs2, pointer */ \ + \ + struct circular_indexer r2indexer; \ + int error = 0; \ + \ + if (roa1->as != roa2->as) { \ + error = add_all_fn(deltas, roa1, DELTA_RM); \ + if (error) \ + return error; \ + return add_all_fn(deltas, roa2, DELTA_ADD); \ + } \ + \ + addrs1 = &roa1->field; \ + addrs2 = &roa2->field; \ + arridx_init(&r2indexer, addrs2->len); \ + \ + ARRAYLIST_FOREACH(addrs1, a1) { \ + if (find_fn(a1, addrs2, &r2indexer)) \ + error = arridx_remove(&r2indexer); \ + else \ + error = add_one_fn(deltas, \ + roa1->as, a1, DELTA_RM); \ + if (error) \ + goto end; \ + } \ + \ + ARRIDX_FOREACH(&r2indexer, a2p) { \ + error = add_one_fn(deltas, roa2->as, \ + &addrs2->array[*a2p], DELTA_ADD); \ + if (error) \ + goto end; \ + } \ + \ + end: arridx_cleanup(&r2indexer); \ + return error; \ + } + +HANDLE_ROAS_FN(handle_roas_v4, v4_addresses, v4_address, addrs4, find_addr_v4, + deltas_add_roa_v4, add_all_roas_v4) +HANDLE_ROAS_FN(handle_roas_v6, v6_addresses, v6_address, addrs6, find_addr_v6, + deltas_add_roa_v6, add_all_roas_v6) + +static int +compute_deltas_node(struct node *n1, struct node *n2, struct deltas *deltas) +{ + int error; + + error = handle_delta_children(&n1->children, &n2->children, deltas); + if (error) + return error; + + error = handle_roas_v4(n1->roa, n2->roa, deltas); + if (error) + return error; + + return handle_roas_v6(n1->roa, n2->roa, deltas); +} + +int +compute_deltas(struct roa_tree *t1, struct roa_tree *t2, struct deltas **result) +{ + struct deltas *deltas; + int error; + + assert(t1->root != NULL); + assert(t2->root != NULL); + + error = deltas_create(&deltas); + if (error) + return error; + + error = compute_deltas_node(t1->root, t2->root, deltas); + if (error) { + deltas_destroy(deltas); + return error; + } + + *result = deltas; + return 0; +} diff --git a/src/rtr/db/roa_tree.h b/src/rtr/db/roa_tree.h new file mode 100644 index 00000000..b80c6bcb --- /dev/null +++ b/src/rtr/db/roa_tree.h @@ -0,0 +1,30 @@ +#ifndef SRC_ROA_TREE_H_ +#define SRC_ROA_TREE_H_ + +#include "address.h" +#include "object/name.h" +#include "rtr/db/delta.h" +#include "rtr/db/vrp.h" + +struct roa_tree; + +/* Constructor */ +struct roa_tree *roa_tree_create(void); +/* Reference counting */ +void roa_tree_get(struct roa_tree *); +void roa_tree_put(struct roa_tree *); + +int roa_tree_foreach_roa(struct roa_tree *, vrp_foreach_cb, void *); + +/* TODO rename to tree handler or whatever */ +int forthandler_reset(struct roa_tree *); +int forthandler_go_down(struct roa_tree *, struct rfc5280_name *); +int forthandler_go_up(struct roa_tree *); +int forthandler_handle_roa_v4(struct roa_tree *, uint32_t, + struct ipv4_prefix const *, uint8_t); +int forthandler_handle_roa_v6(struct roa_tree *, uint32_t, + struct ipv6_prefix const *, uint8_t); + +int compute_deltas(struct roa_tree *, struct roa_tree *, struct deltas **); + +#endif /* SRC_ROA_TREE_H_ */ diff --git a/src/rtr/db/vrp.h b/src/rtr/db/vrp.h new file mode 100644 index 00000000..5b868268 --- /dev/null +++ b/src/rtr/db/vrp.h @@ -0,0 +1,21 @@ +#ifndef SRC_RTR_DB_VRP_H_ +#define SRC_RTR_DB_VRP_H_ + +#define FLAG_WITHDRAWAL 0 +#define FLAG_ANNOUNCEMENT 1 + +struct vrp { + uint32_t asn; + union { + struct in_addr v4; + struct in6_addr v6; + } prefix; + uint8_t prefix_length; + uint8_t max_prefix_length; + uint8_t addr_fam; + uint8_t flags; +}; + +typedef int (*vrp_foreach_cb)(struct vrp *, void *); + +#endif /* SRC_RTR_DB_VRP_H_ */ diff --git a/src/rtr/db/vrps.c b/src/rtr/db/vrps.c new file mode 100644 index 00000000..b94c0767 --- /dev/null +++ b/src/rtr/db/vrps.c @@ -0,0 +1,234 @@ +#include "vrps.h" + +#include +#include +#include "common.h" +#include "data_structure/array_list.h" + +/* + * Storage of VRPs (term taken from RFC 6811 "Validated ROA Payload") and + * Serials that contain such VRPs + */ + +#define START_SERIAL 0 + +struct delta { + uint32_t serial; + struct deltas *deltas; +}; + +ARRAY_LIST(deltas_db, struct delta) + +struct state { + struct roa_tree *base; /** All the current valid ROAs */ + struct deltas_db deltas; /** ROA changes to @base over time */ + + uint32_t current_serial; + uint16_t v0_session_id; + uint16_t v1_session_id; + time_t last_modified_date; +} state; + +/* Read and Write locks */ +static sem_t rlock, wlock; + +/* Readers counter */ +static unsigned int rcounter; + +static void +delta_destroy(struct delta *delta) +{ + deltas_destroy(delta->deltas); +} + +int +vrps_init(void) +{ + int error; + + state.base = NULL; + + error = deltas_db_init(&state.deltas); + if (error) + return error; + + /* + * Use the same start serial, the session ID will avoid + * "desynchronization" (more at RFC 6810 'Glossary' and + * 'Fields of a PDU') + */ + state.current_serial = START_SERIAL; + + /* Get the bits that'll fit in session_id */ + state.v0_session_id = time(NULL) & 0xFFFF; + /* Minus 1 to prevent same ID */ + /* + * TODO Assigning an int (and potentially negative) to a uint16_t. + * Is this legal? + */ + state.v1_session_id = state.v0_session_id - 1; + + sem_init(&rlock, 0, 1); + sem_init(&wlock, 0, 1); + rcounter = 0; + + return 0; +} + +void +vrps_destroy(void) +{ + sem_wait(&wlock); + roa_tree_put(state.base); + deltas_db_cleanup(&state.deltas, delta_destroy); + sem_post(&wlock); + + sem_destroy(&wlock); + sem_destroy(&rlock); +} + +/* + * @new_deltas can be NULL, @new_tree cannot. + */ +int +vrps_update(struct roa_tree *new_tree, struct deltas *new_deltas) +{ + struct delta new_delta; + int error; + + sem_wait(&wlock); + + if (new_deltas != NULL) { + new_delta.serial = state.current_serial + 1; + new_delta.deltas = new_deltas; + error = deltas_db_add(&state.deltas, &new_delta); + if (error) { + sem_post(&wlock); + return error; + } + } + + if (state.base != NULL) + roa_tree_put(state.base); + state.base = new_tree; + roa_tree_get(new_tree); + state.current_serial++; + + sem_post(&wlock); + return 0; +} + +/* + * Get a status to know the difference between the delta with serial SERIAL and + * the last delta at DB. + * + * If SERIAL is received as NULL, and there's data at DB then the status will + * be DIFF_AVAILABLE. + */ +enum delta_status +deltas_db_status(uint32_t *serial) +{ + struct delta *delta; + enum delta_status result; + + read_lock(&rlock, &wlock, &rcounter); + if (state.base == NULL) { + result = DS_NO_DATA_AVAILABLE; + goto end; + } + + /* No serial to match, and there's data at DB */ + if (serial == NULL) { + result = DS_DIFF_AVAILABLE; + goto end; + } + + /* Is the last version? */ + if (*serial == state.current_serial) { + result = DS_NO_DIFF; + goto end; + } + + /* Get the delta corresponding to the serial */ + ARRAYLIST_FOREACH(&state.deltas, delta) + if (delta->serial == *serial) { + result = DS_DIFF_AVAILABLE; + goto end; + } + + /* No match yet, release lock */ + read_unlock(&rlock, &wlock, &rcounter); + + /* The first serial isn't at deltas */ + if (*serial == START_SERIAL) + return DS_DIFF_AVAILABLE; + + /* Reached end, diff can't be determined */ + return DS_DIFF_UNDETERMINED; +end: + read_unlock(&rlock, &wlock, &rcounter); + return result; +} + +int +vrps_foreach_base_roa(vrp_foreach_cb cb, void *arg) +{ + int error; + + read_lock(&rlock, &wlock, &rcounter); + error = roa_tree_foreach_roa(state.base, cb, arg); + read_unlock(&rlock, &wlock, &rcounter); + + return error; +} + +int +vrps_foreach_delta_roa(uint32_t from, uint32_t to, vrp_foreach_cb cb, void *arg) +{ + struct delta *d; + bool from_found; + int error; + + from_found = false; + error = 0; + + read_lock(&rlock, &wlock, &rcounter); + + ARRAYLIST_FOREACH(&state.deltas, d) { + if (!from_found) { + if (d->serial >= from) + from_found = true; + } else { + if (d->serial > to) + break; + error = deltas_foreach(d->deltas, cb, arg); + if (error) + break; + } + } + + read_unlock(&rlock, &wlock, &rcounter); + + return error; +} + +uint32_t +get_last_serial_number(void) +{ + uint32_t serial; + + read_lock(&rlock, &wlock, &rcounter); + serial = state.current_serial - 1; + read_unlock(&rlock, &wlock, &rcounter); + + return serial; +} + +uint16_t +get_current_session_id(uint8_t rtr_version) +{ + /* Semaphore isn't needed since this value is set at initialization */ + if (rtr_version == 1) + return state.v1_session_id; + return state.v0_session_id; +} diff --git a/src/rtr/db/vrps.h b/src/rtr/db/vrps.h new file mode 100644 index 00000000..40cbb6b3 --- /dev/null +++ b/src/rtr/db/vrps.h @@ -0,0 +1,32 @@ +#ifndef SRC_VRPS_H_ +#define SRC_VRPS_H_ + +#include +#include +#include "rtr/db/delta.h" +#include "rtr/db/roa_tree.h" + +enum delta_status { + /** There's no data at the DB */ + DS_NO_DATA_AVAILABLE, + /** The diff can't be determined */ + DS_DIFF_UNDETERMINED, + /** There's no difference */ + DS_NO_DIFF, + /** There are diffs between SERIAL and the last DB serial */ + DS_DIFF_AVAILABLE, +}; + +int vrps_init(void); +void vrps_destroy(void); + +int vrps_update(struct roa_tree *, struct deltas *); +enum delta_status deltas_db_status(uint32_t *); + +int vrps_foreach_base_roa(vrp_foreach_cb, void *); +int vrps_foreach_delta_roa(uint32_t, uint32_t, vrp_foreach_cb, void *); + +uint32_t get_last_serial_number(void); +uint16_t get_current_session_id(uint8_t); + +#endif /* SRC_VRPS_H_ */ diff --git a/src/rtr/pdu.h b/src/rtr/pdu.h index 91071c58..a0f1252d 100644 --- a/src/rtr/pdu.h +++ b/src/rtr/pdu.h @@ -93,10 +93,8 @@ struct pdu_metadata { void (*destructor)(void *); }; -__BEGIN_DECLS int pdu_load(int, void **, struct pdu_metadata const **, uint8_t *); struct pdu_metadata const *pdu_get_metadata(uint8_t); struct pdu_header *pdu_get_header(void *); -__END_DECLS #endif /* RTR_PDU_H_ */ diff --git a/src/rtr/pdu_handler.c b/src/rtr/pdu_handler.c index c8ea494a..6a5313e9 100644 --- a/src/rtr/pdu_handler.c +++ b/src/rtr/pdu_handler.c @@ -8,7 +8,7 @@ #include "err_pdu.h" #include "pdu.h" #include "pdu_sender.h" -#include "vrps.h" +#include "rtr/db/vrps.h" static int warn_unexpected_pdu(int fd, void *pdu, char const *pdu_name) @@ -27,7 +27,8 @@ handle_serial_notify_pdu(int fd, void *pdu) } static int -send_commmon_exchange(struct sender_common *common) +send_commmon_exchange(struct sender_common *common, + int (*pdu_sender)(struct sender_common *)) { int error; @@ -37,7 +38,7 @@ send_commmon_exchange(struct sender_common *common) return error; /* Send Payload PDUs */ - error = send_payload_pdus(common); + error = pdu_sender(common); if (error) return error; @@ -45,12 +46,20 @@ send_commmon_exchange(struct sender_common *common) return send_end_of_data_pdu(common); } +/* + * TODO The semaphoring is bonkers. The code keeps locking, storing a value, + * unlocking, locking again, and using the old value. + * It doesn't look like it's a problem for now, but eventually will be, when old + * delta forgetting is implemented. + * I'm going to defer this because it shouldn't be done during the merge. + */ int handle_serial_query_pdu(int fd, void *pdu) { struct serial_query_pdu *received = pdu; struct sender_common common; - int error, updates; + int error; + enum delta_status updates; uint32_t current_serial; uint16_t session_id; uint8_t version; @@ -74,26 +83,26 @@ handle_serial_query_pdu(int fd, void *pdu) updates = deltas_db_status(common.start_serial); switch (updates) { - case NO_DATA_AVAILABLE: + case DS_NO_DATA_AVAILABLE: /* https://tools.ietf.org/html/rfc8210#section-8.4 */ return err_pdu_send(fd, version, ERR_PDU_NO_DATA_AVAILABLE, NULL, NULL); - case DIFF_UNDETERMINED: + case DS_DIFF_UNDETERMINED: /* https://tools.ietf.org/html/rfc8210#section-8.3 */ return send_cache_reset_pdu(&common); - case DIFF_AVAILABLE: + case DS_DIFF_AVAILABLE: /* https://tools.ietf.org/html/rfc8210#section-8.2 */ - return send_commmon_exchange(&common); - case NO_DIFF: + return send_commmon_exchange(&common, send_pdus_delta); + case DS_NO_DIFF: /* Typical exchange with no Payloads */ error = send_cache_response_pdu(&common); if (error) return error; return send_end_of_data_pdu(&common); - default: - warnx("Reached 'unreachable' code"); - return -EINVAL; } + + warnx("Reached 'unreachable' code"); + return -EINVAL; } int @@ -104,7 +113,7 @@ handle_reset_query_pdu(int fd, void *pdu) uint32_t current_serial; uint16_t session_id; uint8_t version; - int updates; + enum delta_status updates; version = received->header.protocol_version; session_id = get_current_session_id(version); @@ -114,17 +123,20 @@ handle_reset_query_pdu(int fd, void *pdu) updates = deltas_db_status(NULL); switch (updates) { - case NO_DATA_AVAILABLE: + case DS_NO_DATA_AVAILABLE: /* https://tools.ietf.org/html/rfc8210#section-8.4 */ return err_pdu_send(fd, version, ERR_PDU_NO_DATA_AVAILABLE, NULL, NULL); - case DIFF_AVAILABLE: + case DS_DIFF_AVAILABLE: /* https://tools.ietf.org/html/rfc8210#section-8.1 */ - return send_commmon_exchange(&common); - default: - warnx("Reached 'unreachable' code"); - return -EINVAL; + return send_commmon_exchange(&common, send_pdus_base); + case DS_DIFF_UNDETERMINED: + case DS_NO_DIFF: + break; } + + warnx("Reached 'unreachable' code"); + return -EINVAL; } int diff --git a/src/rtr/pdu_sender.c b/src/rtr/pdu_sender.c index b1c79e40..87baf586 100644 --- a/src/rtr/pdu_sender.c +++ b/src/rtr/pdu_sender.c @@ -8,8 +8,8 @@ #include #include "config.h" -#include "vrps.h" #include "rtr/pdu_serializer.h" +#include "rtr/db/vrps.h" /* Header length field is always 64 bits long */ #define HEADER_LENGTH 8 @@ -166,7 +166,7 @@ send_ipv4_prefix_pdu(struct sender_common *common, struct vrp *vrp) pdu.prefix_length = vrp->prefix_length; pdu.max_length = vrp->max_prefix_length; pdu.zero = 0; - pdu.ipv4_prefix = vrp->prefix.ipv4; + pdu.ipv4_prefix = vrp->prefix.v4; pdu.asn = vrp->asn; pdu.header.length = length_ipvx_prefix_pdu(true); @@ -189,7 +189,7 @@ send_ipv6_prefix_pdu(struct sender_common *common, struct vrp *vrp) pdu.prefix_length = vrp->prefix_length; pdu.max_length = vrp->max_prefix_length; pdu.zero = 0; - pdu.ipv6_prefix = vrp->prefix.ipv6; + pdu.ipv6_prefix = vrp->prefix.v6; pdu.asn = vrp->asn; pdu.header.length = length_ipvx_prefix_pdu(false); @@ -199,37 +199,29 @@ send_ipv6_prefix_pdu(struct sender_common *common, struct vrp *vrp) } int -send_payload_pdus(struct sender_common *common) +send_prefix_pdu(struct vrp *vrp, void *arg) { - struct vrp *vrps, *ptr; - unsigned int len; - int error; - - vrps = malloc(sizeof(struct vrp)); - if (vrps == NULL) { - warn("Couldn't allocate VRPs to send PDUs"); - return -ENOMEM; - } - len = get_vrps_delta(common->start_serial, common->end_serial, &vrps); - if (len == 0) - goto end; - - for (ptr = vrps; (ptr - vrps) < len; ptr++) { - if (ptr->addr_fam == AF_INET) - error = send_ipv4_prefix_pdu(common, ptr); - else if (ptr->addr_fam == AF_INET6) - error = send_ipv6_prefix_pdu(common, ptr); - else - error = -EINVAL; - - if (error) { - free(vrps); - return error; - } + switch (vrp->addr_fam) { + case AF_INET: + return send_ipv4_prefix_pdu(arg, vrp); + case AF_INET6: + return send_ipv6_prefix_pdu(arg, vrp); } -end: - free(vrps); - return 0; + + return -EINVAL; +} + +int +send_pdus_base(struct sender_common *common) +{ + return vrps_foreach_base_roa(send_prefix_pdu, common); +} + +int +send_pdus_delta(struct sender_common *common) +{ + return vrps_foreach_delta_roa(*common->start_serial, + *common->end_serial, send_prefix_pdu, common); } int diff --git a/src/rtr/pdu_sender.h b/src/rtr/pdu_sender.h index 43792a21..94b6f196 100644 --- a/src/rtr/pdu_sender.h +++ b/src/rtr/pdu_sender.h @@ -17,7 +17,8 @@ void init_sender_common(struct sender_common *, int, uint8_t, uint16_t *, int send_serial_notify_pdu(struct sender_common *); int send_cache_reset_pdu(struct sender_common *); int send_cache_response_pdu(struct sender_common *); -int send_payload_pdus(struct sender_common *); +int send_pdus_base(struct sender_common *); +int send_pdus_delta(struct sender_common *); int send_end_of_data_pdu(struct sender_common *); int send_error_report_pdu(int, uint8_t, uint16_t, struct pdu_header *, char const *); diff --git a/src/rtr/pdu_serializer.h b/src/rtr/pdu_serializer.h index e536ac5d..9c23dce2 100644 --- a/src/rtr/pdu_serializer.h +++ b/src/rtr/pdu_serializer.h @@ -11,7 +11,6 @@ struct data_buffer { char *data; }; -__BEGIN_DECLS void init_buffer(struct data_buffer *); void free_buffer(struct data_buffer *); @@ -22,6 +21,5 @@ size_t serialize_ipv6_prefix_pdu(struct ipv6_prefix_pdu *, char *); size_t serialize_end_of_data_pdu(struct end_of_data_pdu *, char *); size_t serialize_cache_reset_pdu(struct cache_reset_pdu *, char *); size_t serialize_error_report_pdu(struct error_report_pdu *, char *); -__END_DECLS #endif /* SRC_RTR_PDU_SERIALIZER_H_ */ diff --git a/src/rtr/primitive_reader.h b/src/rtr/primitive_reader.h index 49790cfc..104b1239 100644 --- a/src/rtr/primitive_reader.h +++ b/src/rtr/primitive_reader.h @@ -7,13 +7,11 @@ typedef char rtr_char; -__BEGIN_DECLS int read_int8(int, uint8_t *); int read_int16(int, uint16_t *); int read_int32(int, uint32_t *); int read_in_addr(int, struct in_addr *); int read_in6_addr(int, struct in6_addr *); int read_string(int, rtr_char **); -__END_DECLS #endif /* RTR_PRIMITIVE_READER_H_ */ diff --git a/src/rtr/primitive_writer.h b/src/rtr/primitive_writer.h index a271a16e..8a34ce7d 100644 --- a/src/rtr/primitive_writer.h +++ b/src/rtr/primitive_writer.h @@ -3,12 +3,10 @@ #include -__BEGIN_DECLS char *write_int8(char *, uint8_t); char *write_int16(char *, uint16_t); char *write_int32(char *, uint32_t); char *write_in_addr(char *, struct in_addr); char *write_in6_addr(char *, struct in6_addr); -__END_DECLS #endif /* RTR_PRIMITIVE_WRITER_H_ */ diff --git a/src/rtr/rtr.h b/src/rtr/rtr.h index 0dfc1111..2d445c8d 100644 --- a/src/rtr/rtr.h +++ b/src/rtr/rtr.h @@ -3,9 +3,7 @@ #include "common.h" -__BEGIN_DECLS int rtr_listen(void); void rtr_cleanup(void); -__END_DECLS #endif /* RTR_RTR_H_ */ diff --git a/src/state.c b/src/state.c index 01cdaff5..ef0be179 100644 --- a/src/state.c +++ b/src/state.c @@ -3,10 +3,10 @@ #include #include #include -#include "array_list.h" #include "log.h" #include "str.h" #include "thread_var.h" +#include "data_structure/array_list.h" #include "object/certificate.h" struct serial_number { @@ -15,7 +15,7 @@ struct serial_number { }; struct subject_name { - struct rfc5280_name name; + struct rfc5280_name *name; char *file; /* File where this subject name was found. */ }; @@ -83,6 +83,8 @@ struct validation { */ char addr_buffer1[INET6_ADDRSTRLEN]; char addr_buffer2[INET6_ADDRSTRLEN]; + + struct validation_handler validation_handler; }; /* @@ -122,7 +124,8 @@ cb(int ok, X509_STORE_CTX *ctx) /** Creates a struct validation, puts it in thread local, and returns it. */ int -validation_prepare(struct validation **out, struct tal *tal) +validation_prepare(struct validation **out, struct tal *tal, + struct validation_handler *validation_handler) { struct validation *result; int error; @@ -153,6 +156,7 @@ validation_prepare(struct validation **out, struct tal *tal) SLIST_INIT(&result->certs); result->pubkey_state = PKS_UNTESTED; + result->validation_handler = *validation_handler; *out = result; return 0; @@ -174,7 +178,7 @@ serial_cleanup(struct serial_number *serial) static void subject_cleanup(struct subject_name *subject) { - x509_name_cleanup(&subject->name); + x509_name_put(subject->name); free(subject->file); } @@ -427,9 +431,6 @@ validation_store_serial_number(struct validation *state, BIGNUM *number) return error; } -/** - * This function will steal ownership of @subject on success. - */ int validation_store_subject(struct validation *state, struct rfc5280_name *subject) { @@ -481,38 +482,40 @@ validation_store_subject(struct validation *state, struct rfc5280_name *subject) * fixing. */ - /* Remember to free @subject if you return 0 but don't store it. */ - cert = SLIST_FIRST(&state->certs); - if (cert == NULL) { - x509_name_cleanup(subject); + if (cert == NULL) return 0; /* The TA lacks siblings, so subject is unique. */ - } /* See the large comment in validation_store_serial_number(). */ ARRAYLIST_FOREACH(&cert->subjects, cursor) { - if (x509_name_equals(&cursor->name, subject)) { + if (x509_name_equals(cursor->name, subject)) { + char const *serial = x509_name_serialNumber(subject); pr_warn("Subject name '%s%s%s' is not unique. (Also found in '%s'.)", - subject->commonName, - (subject->serialNumber != NULL) ? "/" : "", - (subject->serialNumber != NULL) - ? subject->serialNumber - : "", + x509_name_commonName(subject), + (serial != NULL) ? "/" : "", + (serial != NULL) ? serial : "", cursor->file); - x509_name_cleanup(subject); return 0; } } - duplicate.name = *subject; + duplicate.name = subject; + x509_name_get(subject); + error = get_current_file_name(&duplicate.file); if (error) - return error; + goto revert_name; error = subjects_add(&cert->subjects, &duplicate); if (error) - free(duplicate.file); + goto revert_file; + + return 0; +revert_file: + free(duplicate.file); +revert_name: + x509_name_put(subject); return error; } @@ -527,3 +530,9 @@ 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 index 5b1f45c1..d7d39517 100644 --- a/src/state.h +++ b/src/state.h @@ -3,12 +3,14 @@ #include #include "resource.h" +#include "validation_handler.h" #include "object/name.h" #include "object/tal.h" struct validation; -int validation_prepare(struct validation **, struct tal *); +int validation_prepare(struct validation **, struct tal *, + struct validation_handler *); void validation_destroy(struct validation *); struct tal *validation_tal(struct validation *); @@ -39,4 +41,6 @@ int validation_store_subject(struct validation *, struct rfc5280_name *); 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_ */ diff --git a/src/thread_var.c b/src/thread_var.c index d907d85d..b61b7cf1 100644 --- a/src/thread_var.c +++ b/src/thread_var.c @@ -127,18 +127,6 @@ fnstack_cleanup(void) fprintf(stderr, "pthread_setspecific() returned %d.", error); } -static struct filename_stack * -get_file_stack(void) -{ - struct filename_stack *files; - - files = pthread_getspecific(filenames_key); - if (files == NULL) - fprintf(stderr, "This thread lacks a files stack.\n"); - - return files; -} - /** * Call this function every time you're about to start processing a new file. * Any pr_err()s and friends will now include the new file name. @@ -151,7 +139,7 @@ fnstack_push(char const *file) struct filename_stack *files; char const **tmp; - files = get_file_stack(); + files = pthread_getspecific(filenames_key); if (files == NULL || files->filenames == NULL) return; @@ -184,7 +172,7 @@ fnstack_peek(void) { struct filename_stack *files; - files = get_file_stack(); + files = pthread_getspecific(filenames_key); if (files == NULL || files->filenames == NULL || files->len == 0) return NULL; @@ -197,7 +185,7 @@ fnstack_pop(void) { struct filename_stack *files; - files = get_file_stack(); + files = pthread_getspecific(filenames_key); if (files == NULL || files->filenames == NULL || files->len == 0) return; @@ -205,7 +193,7 @@ fnstack_pop(void) } static char const * -addr2str(int af, void *addr, char *(*buffer_cb)(struct validation *)) +addr2str(int af, void const *addr, char *(*buffer_cb)(struct validation *)) { struct validation *state; @@ -227,7 +215,7 @@ addr2str(int af, void *addr, char *(*buffer_cb)(struct validation *)) * The buffer is the same as v6addr2str()'s, so don't mix them either. */ char const * -v4addr2str(struct in_addr *addr) +v4addr2str(struct in_addr const *addr) { return addr2str(AF_INET, addr, validation_get_ip_buffer1); } @@ -236,7 +224,7 @@ v4addr2str(struct in_addr *addr) * Same as v4addr2str(), except a different buffer is used. */ char const * -v4addr2str2(struct in_addr *addr) +v4addr2str2(struct in_addr const *addr) { return addr2str(AF_INET, addr, validation_get_ip_buffer2); } @@ -245,7 +233,7 @@ v4addr2str2(struct in_addr *addr) * See v4addr2str(). */ char const * -v6addr2str(struct in6_addr *addr) +v6addr2str(struct in6_addr const *addr) { return addr2str(AF_INET6, addr, validation_get_ip_buffer1); } @@ -254,7 +242,7 @@ v6addr2str(struct in6_addr *addr) * See v4addr2str2(). */ char const * -v6addr2str2(struct in6_addr *addr) +v6addr2str2(struct in6_addr const *addr) { return addr2str(AF_INET6, addr, validation_get_ip_buffer2); } diff --git a/src/thread_var.h b/src/thread_var.h index 7dbe18e0..a56b8811 100644 --- a/src/thread_var.h +++ b/src/thread_var.h @@ -16,9 +16,9 @@ void fnstack_push_uri(struct rpki_uri const *); char const *fnstack_peek(void); void fnstack_pop(void); -char const *v4addr2str(struct in_addr *addr); -char const *v4addr2str2(struct in_addr *addr); -char const *v6addr2str(struct in6_addr *addr); -char const *v6addr2str2(struct in6_addr *addr); +char const *v4addr2str(struct in_addr const *addr); +char const *v4addr2str2(struct in_addr const *addr); +char const *v6addr2str(struct in6_addr const *addr); +char const *v6addr2str2(struct in6_addr const *addr); #endif /* SRC_THREAD_VAR_H_ */ diff --git a/src/updates_daemon.c b/src/updates_daemon.c index cd7d3c89..9c07a512 100644 --- a/src/updates_daemon.c +++ b/src/updates_daemon.c @@ -9,25 +9,112 @@ #include "config.h" #include "notify.h" #include "object/tal.h" +#include "rtr/db/vrps.h" static pthread_t thread; +static int +__reset(void *arg) +{ + return forthandler_reset(arg); +} + +static int +__traverse_down(struct rfc5280_name *subject_name, void *arg) +{ + return forthandler_go_down(arg, subject_name); +} + +static int +__traverse_up(void *arg) +{ + return forthandler_go_up(arg); +} + +int +__handle_roa_v4(uint32_t as, struct ipv4_prefix const *prefix, + uint8_t max_length, void *arg) +{ + return forthandler_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 forthandler_handle_roa_v6(arg, as, prefix, max_length); +} + static void * check_vrps_updates(void *param_void) { + struct validation_handler validation_handler; + struct roa_tree *old_tree; + struct deltas *deltas; int error; - bool updated; + + validation_handler.reset = __reset; + validation_handler.traverse_down = __traverse_down; + validation_handler.traverse_up = __traverse_up; + validation_handler.handle_roa_v4 = __handle_roa_v4; + validation_handler.handle_roa_v6 = __handle_roa_v6; + old_tree = NULL; do { - updated = false; - error = perform_standalone_validation(&updated); + validation_handler.arg = roa_tree_create(); + if (validation_handler.arg == NULL) { + pr_err("Memory allocation failed. Cannot validate. Sleeping..."); + goto sleep; + } + + error = perform_standalone_validation(&validation_handler); + if (error) { + roa_tree_put(validation_handler.arg); + pr_err("Validation failed (error code %d). Cannot udpate the ROA database. Sleeping...", + error); + goto sleep; + } + + if (old_tree == NULL) { + error = vrps_update(validation_handler.arg, NULL); + if (error) { + roa_tree_put(validation_handler.arg); + pr_err("Error code %d while trying to update the ROA database. Sleeping...", + error); + } else { + old_tree = validation_handler.arg; + } + goto sleep; + } + + error = compute_deltas(old_tree, validation_handler.arg, &deltas); if (error) { - warnx("Error '%d' while searching CSV updates, sleeping...", + roa_tree_put(validation_handler.arg); + pr_err("Something went wrong while trying to compute the deltas. (error code %d.) Cannot update the ROA database. Sleeping...", error); goto sleep; } - if (updated) - notify_clients(); + + if (deltas_is_empty(deltas)) { + roa_tree_put(validation_handler.arg); + deltas_destroy(deltas); + pr_debug("No changes. Sleeping..."); + goto sleep; + } + + error = vrps_update(validation_handler.arg, deltas); + if (error) { + roa_tree_put(validation_handler.arg); + deltas_destroy(deltas); + pr_err("Error code %d while trying to store the deltas in the database. Cannot update the ROA database. Sleeping...", + error); + goto sleep; + } + + old_tree = validation_handler.arg; + notify_clients(); + pr_debug("Database updated successfully. Sleeping..."); + sleep: sleep(config_get_vrps_check_interval()); } while (true); diff --git a/src/validation_handler.c b/src/validation_handler.c new file mode 100644 index 00000000..971af061 --- /dev/null +++ b/src/validation_handler.c @@ -0,0 +1,90 @@ +#include "validation_handler.h" + +#include +#include "log.h" +#include "thread_var.h" + +int +vhandler_reset(struct validation_handler *handler) +{ + return (handler->reset != NULL) ? handler->reset(handler->arg) : 0; +} + +static int +get_current_threads_handler(struct validation_handler const **result) +{ + struct validation *state; + struct validation_handler const *handler; + + state = state_retrieve(); + if (state == NULL) + return -EINVAL; + handler = validation_get_validation_handler(state); + if (handler == NULL) + return pr_crit("This thread lacks a validation handler."); + + *result = handler; + return 0; +} + +int +vhandler_traverse_down(struct rfc5280_name *subject_name) +{ + struct validation_handler const *handler; + int error; + + error = get_current_threads_handler(&handler); + if (error) + return error; + + return (handler->traverse_down != NULL) + ? handler->traverse_down(subject_name, handler->arg) + : 0; +} + +int +vhandler_traverse_up(void) +{ + struct validation_handler const *handler; + int error; + + error = get_current_threads_handler(&handler); + if (error) + return error; + + return (handler->traverse_up != NULL) + ? handler->traverse_up(handler->arg) + : 0; +} + +int +vhandler_handle_roa_v4(uint32_t as, struct ipv4_prefix const *prefix, + uint8_t max_length) +{ + struct validation_handler const *handler; + int error; + + error = get_current_threads_handler(&handler); + if (error) + return error; + + return (handler->handle_roa_v4 != NULL) + ? handler->handle_roa_v4(as, prefix, max_length, handler->arg) + : 0; +} + +int +vhandler_handle_roa_v6(uint32_t as, struct ipv6_prefix const *prefix, + uint8_t max_length) +{ + struct validation_handler const *handler; + int error; + + error = get_current_threads_handler(&handler); + if (error) + return error; + + return (handler->handle_roa_v6 != NULL) + ? handler->handle_roa_v6(as, prefix, max_length, handler->arg) + : 0; +} diff --git a/src/validation_handler.h b/src/validation_handler.h new file mode 100644 index 00000000..72ca4260 --- /dev/null +++ b/src/validation_handler.h @@ -0,0 +1,26 @@ +#ifndef SRC_VALIDATION_HANDLER_H_ +#define SRC_VALIDATION_HANDLER_H_ + +#include "address.h" +#include "object/name.h" + +struct validation_handler { + /* All of these can be NULL. */ + + int (*reset)(void *); + int (*traverse_down)(struct rfc5280_name *, void *); + int (*traverse_up)(void *); + 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 *); + void *arg; +}; + +int vhandler_reset(struct validation_handler *); +int vhandler_traverse_down(struct rfc5280_name *); +int vhandler_traverse_up(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); + +#endif /* SRC_VALIDATION_HANDLER_H_ */ diff --git a/src/vrps.c b/src/vrps.c deleted file mode 100644 index b41a10a7..00000000 --- a/src/vrps.c +++ /dev/null @@ -1,461 +0,0 @@ -#include "vrps.h" - -#include -#include -#include "array_list.h" -#include "common.h" - -/* - * Storage of VRPs (term taken from RFC 6811 "Validated ROA Payload") and - * Serials that contain such VRPs - */ - -#define FLAG_WITHDRAWAL 0 -#define FLAG_ANNOUNCEMENT 1 -#define START_SERIAL 0 - -ARRAY_LIST(vrps, struct vrp) - -struct delta { - uint32_t serial; - struct vrps vrps; -}; - -ARRAY_LIST(deltasdb, struct delta) - -struct state { - /** The current valid ROAs, freshly loaded from the file */ - struct delta base_db; - /** ROA changes over time */ - struct deltasdb deltas_db; - uint32_t current_serial; - uint16_t v0_session_id; - uint16_t v1_session_id; - time_t last_modified_date; -} state; - -/* Read and Write locks */ -sem_t rlock, wlock; - -/* Readers counter */ -unsigned int rcounter; - -static int -delta_init(struct delta *delta) -{ - return vrps_init(&delta->vrps); -} - -static void -vrp_destroy(struct vrp *vrp) -{ - /* Didn't allocate something, so do nothing */ -} - -static void -delta_destroy(struct delta *delta) -{ - vrps_cleanup(&delta->vrps, vrp_destroy); -} - -int -deltas_db_init(void) -{ - int error, shift; - - error = delta_init(&state.base_db); - if (error) - return error; - - error = deltasdb_init(&state.deltas_db); - if (error) { - delta_destroy(&state.base_db); - return error; - } - - /* - * Use the same start serial, the session ID will avoid - * "desynchronization" (more at RFC 6810 'Glossary' and - * 'Fields of a PDU') - */ - state.current_serial = START_SERIAL; - - /* Get the bits that'll fit in session_id */ - shift = sizeof(time_t) - sizeof(state.v0_session_id); - state.v0_session_id = (uint16_t)((time(NULL) << shift) >> shift); - /* Minus 1 to prevent same ID */ - state.v1_session_id = state.v0_session_id - 1; - - sem_init(&rlock, 0, 1); - sem_init(&wlock, 0, 1); - rcounter = 0; - - return 0; -} - -static void -init_vrp (struct vrp *vrp, uint32_t asn, uint8_t prefix_length, - uint8_t max_prefix_length) -{ - vrp->asn = asn; - vrp->prefix_length = prefix_length; - vrp->max_prefix_length = max_prefix_length; - /* Set as ANNOUNCEMENT by default */ - vrp->flags = FLAG_ANNOUNCEMENT; -} - -struct vrp -create_vrp4(uint32_t asn, struct in_addr ipv4_prefix, uint8_t prefix_length, - uint8_t max_prefix_length) -{ - struct vrp result; - - init_vrp(&result, asn, prefix_length, max_prefix_length); - result.prefix.ipv4 = ipv4_prefix; - result.addr_fam = AF_INET; - - return result; -} - -struct vrp -create_vrp6(uint32_t asn, struct in6_addr ipv6_prefix, uint8_t prefix_length, - uint8_t max_prefix_length) -{ - struct vrp result; - - init_vrp(&result, asn, prefix_length, max_prefix_length); - result.prefix.ipv6 = ipv6_prefix; - result.addr_fam = AF_INET6; - - return result; -} - -static bool -vrp_equal(struct vrp *left, struct vrp *right) -{ - return left->asn == right->asn - && left->addr_fam == right->addr_fam - && left->prefix_length == right->prefix_length - && left->max_prefix_length == right->max_prefix_length - && ((left->addr_fam == AF_INET - && left->prefix.ipv4.s_addr == right->prefix.ipv4.s_addr) - || (left->addr_fam == AF_INET6 - && IN6_ARE_ADDR_EQUAL(left->prefix.ipv6.s6_addr32, - right->prefix.ipv6.s6_addr32))); -} - -static struct vrp * -vrp_locate(struct vrps *base, struct vrp *vrp) -{ - struct vrp *cursor; - - ARRAYLIST_FOREACH(base, cursor) - if (vrp_equal(cursor, vrp)) - return cursor; - - return NULL; -} - -static bool -vrp_is_new(struct vrps *base, struct vrp *vrp) -{ - return vrp_locate(base, vrp) == NULL; -} - -static int -delta_add_vrp(struct delta *delta, struct vrp *vrp) -{ - return vrps_add(&delta->vrps, vrp); -} - -static int -delta_summary(struct delta *base_delta, struct delta *result) -{ - struct vrps *base, *search_list; - struct vrp *cursor; - int error; - - /* - * Note: Don't fix this function yet. - * I realize why you implemented it this way, and I'm trying to come up - * with a more efficient algorithm. - */ - - error = delta_init(result); - if (error) - return error; - - result->serial = base_delta->serial; - read_lock(&rlock, &wlock, &rcounter); - /* First check for announcements */ - base = &base_delta->vrps; - search_list = &state.base_db.vrps; - ARRAYLIST_FOREACH(base, cursor) - if (vrp_is_new(search_list, cursor)) { - cursor->flags = FLAG_ANNOUNCEMENT; - error = delta_add_vrp(result, cursor); - if (error) { - read_unlock(&rlock, &wlock, &rcounter); - return error; - } - } - - /* Now for withdrawals */ - base = &state.base_db.vrps; - search_list = &base_delta->vrps; - ARRAYLIST_FOREACH(base, cursor) - if (vrp_is_new(search_list, cursor)) { - cursor->flags = FLAG_WITHDRAWAL; - error = delta_add_vrp(result, cursor); - if (error) { - read_unlock(&rlock, &wlock, &rcounter); - return error; - } - } - - read_unlock(&rlock, &wlock, &rcounter); - return 0; -} - -static int -deltas_db_add_delta(struct delta delta) -{ - struct delta summary; - int result; - - result = 0; - read_lock(&rlock, &wlock, &rcounter); - delta.serial = state.current_serial; - read_unlock(&rlock, &wlock, &rcounter); - /* Store only updates */ - if (delta.serial != START_SERIAL) { - result = delta_summary(&delta, &summary); - if (result != 0) { - pr_err("Error summarizing new delta"); - return result; - } - sem_wait(&wlock); - result = deltasdb_add(&state.deltas_db, &summary); - sem_post(&wlock); - } - /* Don't set the base in case of error */ - if (result != 0) { - pr_err("Error persisting new delta"); - return result; - } - - sem_wait(&wlock); - free(state.base_db.vrps.array); - state.base_db = delta; - state.current_serial++; - sem_post(&wlock); - return result; -} - -static void -copy_vrps(struct vrp **dst, struct vrp *src, unsigned int len) -{ - struct vrp *tmp; - tmp = realloc(*dst, len * sizeof(struct vrp)); - if (tmp == NULL) { - pr_enomem(); - return; - } - *dst = tmp; - memcpy(*dst, src, len * sizeof(struct vrp)); -} - -int -deltas_db_create_delta(struct vrp *array, unsigned int len) -{ - struct delta new_delta; - int error; - - error = delta_init(&new_delta); - if (error) { - pr_err("New Delta couldn't be initialized"); - return error; - } - - copy_vrps(&new_delta.vrps.array, array, len); - new_delta.vrps.len = len; - new_delta.vrps.capacity = len * sizeof(struct vrp); - - error = deltas_db_add_delta(new_delta); - if (error) - return error; - - return 0; -} - -void -deltas_db_destroy(void) -{ - sem_wait(&wlock); - delta_destroy(&state.base_db); - deltasdb_cleanup(&state.deltas_db, delta_destroy); - sem_post(&wlock); - - sem_destroy(&wlock); - sem_destroy(&rlock); -} - -/* - * Get a status to know the difference between the delta with serial SERIAL and - * the last delta at DB. - * - * If SERIAL is received as NULL, and there's data at DB then the status will - * be DIFF_AVAILABLE. - * - * The possible return values are: - * NO_DATA_AVAILABLE -> There's no data at the DB - * DIFF_UNDETERMINED -> The diff can't be determined - * NO_DIFF -> There's no difference - * DIFF_AVAILABLE -> There are diffs between SERIAL and the last DB serial - */ -int -deltas_db_status(uint32_t *serial) -{ - struct delta *delta; - int result; - - read_lock(&rlock, &wlock, &rcounter); - if (state.base_db.vrps.len == 0) { - result = NO_DATA_AVAILABLE; - goto end; - } - - /* No serial to match, and there's data at DB */ - if (serial == NULL) { - result = DIFF_AVAILABLE; - goto end; - } - - /* Is the last version? */ - if (*serial == state.base_db.serial) { - result = NO_DIFF; - goto end; - } - - /* Get the delta corresponding to the serial */ - ARRAYLIST_FOREACH(&state.deltas_db, delta) - if (delta->serial == *serial) { - result = DIFF_AVAILABLE; - goto end; - } - - /* No match yet, release lock */ - read_unlock(&rlock, &wlock, &rcounter); - - /* The first serial isn't at deltas */ - if (*serial == START_SERIAL) - return DIFF_AVAILABLE; - - /* Reached end, diff can't be determined */ - return DIFF_UNDETERMINED; -end: - read_unlock(&rlock, &wlock, &rcounter); - return result; -} - -static void -add_vrps_filtered(struct vrps *dst, struct vrps *src) -{ - struct vrp *ptr; - for (ptr = src->array; (ptr - src->array) < src->len; ptr++) - if (vrp_is_new(dst, ptr)) - vrps_add(dst, ptr); -} - -/* - * Get the number of updates from serial START_SERIAL to END_SERIAL, set them - * at RESULT. - * - * Return 0 if no updates are available or couldn't be calculated with the - * received values. - */ -unsigned int -get_vrps_delta(uint32_t *start_serial, uint32_t *end_serial, - struct vrp **result) -{ - struct delta *delta1; - struct vrps summary; - unsigned int vrps_len; - - read_lock(&rlock, &wlock, &rcounter); - /* No data */ - if (state.base_db.vrps.len == 0) { - read_unlock(&rlock, &wlock, &rcounter); - return 0; - } - - /* NULL start? Send the last version, there's no need to iterate DB */ - if (start_serial == NULL) { - copy_vrps(result, state.base_db.vrps.array, - state.base_db.vrps.len); - vrps_len = state.base_db.vrps.len; - read_unlock(&rlock, &wlock, &rcounter); - return vrps_len; - } - - /* Apparently nothing to return */ - if (*start_serial >= *end_serial) { - read_unlock(&rlock, &wlock, &rcounter); - return 0; - } - - /* Get the delta corresponding to the serials */ - vrps_init(&summary); - ARRAYLIST_FOREACH(&state.deltas_db, delta1) { - if (delta1->serial > *start_serial) - add_vrps_filtered(&summary, &delta1->vrps); - if (delta1->serial == *end_serial) - break; - } - read_unlock(&rlock, &wlock, &rcounter); - - copy_vrps(result, summary.array, summary.len); - vrps_cleanup(&summary, vrp_destroy); - return summary.len; -} - -void -set_vrps_last_modified_date(time_t new_date) -{ - sem_wait(&wlock); - state.last_modified_date = new_date; - sem_post(&wlock); -} - -uint32_t -get_last_serial_number(void) -{ - uint32_t serial; - - read_lock(&rlock, &wlock, &rcounter); - serial = state.current_serial - 1; - read_unlock(&rlock, &wlock, &rcounter); - - return serial; -} - -uint16_t -get_current_session_id(uint8_t rtr_version) -{ - /* Semaphore isn't needed since this value is set at initialization */ - if (rtr_version == 1) - return state.v1_session_id; - return state.v0_session_id; -} - -time_t -get_vrps_last_modified_date(void) -{ - time_t date; - - read_lock(&rlock, &wlock, &rcounter); - date = state.last_modified_date; - read_unlock(&rlock, &wlock, &rcounter); - - return date; -} diff --git a/src/vrps.h b/src/vrps.h deleted file mode 100644 index 11f77d65..00000000 --- a/src/vrps.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef SRC_VRPS_H_ -#define SRC_VRPS_H_ - -#include -#include - -#define NO_DATA_AVAILABLE -2 -#define DIFF_UNDETERMINED -1 -#define NO_DIFF 0 -#define DIFF_AVAILABLE 1 - -struct vrp { - uint32_t asn; - union { - struct in_addr ipv4; - struct in6_addr ipv6; - } prefix; - uint8_t prefix_length; - uint8_t max_prefix_length; - uint8_t addr_fam; - uint8_t flags; -}; - -int deltas_db_init(void); - -struct vrp create_vrp4(uint32_t, struct in_addr, uint8_t, uint8_t); -struct vrp create_vrp6(uint32_t, struct in6_addr, uint8_t, uint8_t); - -int deltas_db_create_delta(struct vrp *, unsigned int); -int deltas_db_status(uint32_t *); - -unsigned int get_vrps_delta(uint32_t *, uint32_t *, struct vrp **); - -void deltas_db_destroy(void); -void set_vrps_last_modified_date(time_t); - -uint32_t get_last_serial_number(void); -uint16_t get_current_session_id(uint8_t); -time_t get_vrps_last_modified_date(void); - -#endif /* SRC_VRPS_H_ */ diff --git a/test/Makefile.am b/test/Makefile.am index b4d45663..c20497c3 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -20,12 +20,13 @@ BASIC_MODULES = ../src/log.c ../src/log.h BASIC_MODULES += impersonator.c check_PROGRAMS = address.test +check_PROGRAMS += circular_indexer.test check_PROGRAMS += vcard.test check_PROGRAMS += line_file.test check_PROGRAMS += rsync.test -check_PROGRAMS += tal.test -check_PROGRAMS += rtr/primitive_reader.test -check_PROGRAMS += rtr/pdu.test +#check_PROGRAMS += tal.test +#check_PROGRAMS += rtr/primitive_reader.test +#check_PROGRAMS += rtr/pdu.test TESTS = ${check_PROGRAMS} address_test_SOURCES = ${BASIC_MODULES} @@ -33,6 +34,11 @@ address_test_SOURCES += ../src/address.h address_test_SOURCES += address_test.c address_test_LDADD = ${MY_LDADD} +circular_indexer_test_SOURCES = ${BASIC_MODULES} +circular_indexer_test_SOURCES += ../src/data_structure/circular_indexer.c +circular_indexer_test_SOURCES += data_structure/circular_indexer_test.c +circular_indexer_test_LDADD = ${MY_LDADD} + line_file_test_SOURCES = ${BASIC_MODULES} line_file_test_SOURCES += ../src/file.c ../src/file.h line_file_test_SOURCES += ../src/line_file.c ../src/line_file.h @@ -45,27 +51,27 @@ rsync_test_SOURCES += ../src/uri.c ../src/uri.h rsync_test_SOURCES += rsync_test.c rsync_test_LDADD = ${MY_LDADD} -rtr_primitive_reader_test_SOURCES = ${BASIC_MODULES} -rtr_primitive_reader_test_SOURCES += rtr/primitive_reader_test.c -rtr_primitive_reader_test_SOURCES += rtr/stream.c -rtr_primitive_reader_test_LDADD = ${MY_LDADD} +#rtr_primitive_reader_test_SOURCES = ${BASIC_MODULES} +#rtr_primitive_reader_test_SOURCES += rtr/primitive_reader_test.c +#rtr_primitive_reader_test_SOURCES += rtr/stream.c +#rtr_primitive_reader_test_LDADD = ${MY_LDADD} -rtr_pdu_test_SOURCES = ${BASIC_MODULES} -rtr_pdu_test_SOURCES += rtr/pdu_test.c -rtr_pdu_test_SOURCES += rtr/stream.c -rtr_pdu_test_SOURCES += $(top_builddir)/src/rtr/primitive_reader.c -rtr_pdu_test_SOURCES += $(top_builddir)/src/rtr/pdu_handler.c -rtr_pdu_test_LDADD = ${MY_LDADD} +#rtr_pdu_test_SOURCES = ${BASIC_MODULES} +#rtr_pdu_test_SOURCES += rtr/pdu_test.c +#rtr_pdu_test_SOURCES += rtr/stream.c +#rtr_pdu_test_SOURCES += ../src/rtr/primitive_reader.c +#rtr_pdu_test_SOURCES += ../src/rtr/pdu_handler.c +#rtr_pdu_test_LDADD = ${MY_LDADD} -tal_test_SOURCES = ${BASIC_MODULES} -tal_test_SOURCES += ../src/file.c ../src/file.h -tal_test_SOURCES += ../src/crypto/base64.c ../src/crypto/base64.h -tal_test_SOURCES += ../src/random.c ../src/random.h -tal_test_SOURCES += ../src/str.c ../src/str.h -tal_test_SOURCES += ../src/uri.c ../src/uri.h -tal_test_SOURCES += ../src/line_file.c ../src/line_file.h -tal_test_SOURCES += tal_test.c -tal_test_LDADD = ${MY_LDADD} +#tal_test_SOURCES = ${BASIC_MODULES} +#tal_test_SOURCES += ../src/file.c ../src/file.h +#tal_test_SOURCES += ../src/crypto/base64.c ../src/crypto/base64.h +#tal_test_SOURCES += ../src/random.c ../src/random.h +#tal_test_SOURCES += ../src/str.c ../src/str.h +#tal_test_SOURCES += ../src/uri.c ../src/uri.h +#tal_test_SOURCES += ../src/line_file.c ../src/line_file.h +#tal_test_SOURCES += tal_test.c +#tal_test_LDADD = ${MY_LDADD} vcard_test_SOURCES = ${BASIC_MODULES} vcard_test_SOURCES += vcard_test.c diff --git a/test/address_test.c b/test/address_test.c index d829b08a..c31e8d7c 100644 --- a/test/address_test.c +++ b/test/address_test.c @@ -156,46 +156,15 @@ START_TEST(check_encoding6_test) } END_TEST -static void -test_get_address_from_string(char *text_prefix) -{ - struct ipv4_prefix prefix; - const char *result; - int error; - char buffer[INET_ADDRSTRLEN]; - - error = prefix4_decode(text_prefix, &prefix); - if (error) - return; - - result = addr2str4(&prefix.addr, buffer); - - ck_assert_str_eq(text_prefix, result); -} - -START_TEST(address_test_get_addr) -{ - char *text; - text = "198.248.146.0"; - - test_get_address_from_string(text); - -} -END_TEST - Suite *address_load_suite(void) { Suite *suite; TCase *core; - TCase *string; core = tcase_create("Core"); tcase_add_test(core, check_encoding4_test); tcase_add_test(core, check_encoding6_test); - string = tcase_create("Strings"); - tcase_add_test(string, address_test_get_addr); - suite = suite_create("Encoding checking"); suite_add_tcase(suite, core); return suite; diff --git a/test/data_structure/circular_indexer_test.c b/test/data_structure/circular_indexer_test.c new file mode 100644 index 00000000..6ad87d85 --- /dev/null +++ b/test/data_structure/circular_indexer_test.c @@ -0,0 +1,383 @@ +#include "data_structure/circular_indexer.h" + +#include +#include +#include + +/* + * These are macros so CHECK will be able to report proper lines on errors. + * Functions would ruin that. + */ + +static array_index *tmp; +#define assert_index(expected, actual) \ + tmp = actual; \ + ck_assert_ptr_ne(NULL, tmp); \ + ck_assert_int_eq(expected, *tmp); + +#define assert_next_is_null(indexer) \ + /* Twice, to make sure it stays consistent. */ \ + ck_assert_ptr_eq(NULL, arridx_next(indexer)); \ + ck_assert_ptr_eq(NULL, arridx_next(indexer)); + +#define assert_first_is_null(indexer) \ + ck_assert_ptr_eq(NULL, arridx_first(indexer)); \ + ck_assert_ptr_eq(NULL, arridx_first(indexer)); + +START_TEST(no_removes) +{ + struct circular_indexer indexer; + + arridx_init(&indexer, 4); + + /* Full traversal from 0 */ + assert_index(0, arridx_first(&indexer)); + assert_index(1, arridx_next(&indexer)); + assert_index(2, arridx_next(&indexer)); + assert_index(3, arridx_next(&indexer)); + assert_next_is_null(&indexer); + + assert_index(0, arridx_first(&indexer)); + assert_index(1, arridx_next(&indexer)); + assert_index(2, arridx_next(&indexer)); + + /* Full traversal from 3 */ + assert_index(3, arridx_first(&indexer)); + assert_index(0, arridx_next(&indexer)); + assert_index(1, arridx_next(&indexer)); + assert_index(2, arridx_next(&indexer)); + assert_next_is_null(&indexer); + + assert_index(3, arridx_first(&indexer)); + assert_index(0, arridx_next(&indexer)); + assert_index(1, arridx_next(&indexer)); + + /* Full traversal from 2 */ + assert_index(2, arridx_first(&indexer)); + assert_index(3, arridx_next(&indexer)); + assert_index(0, arridx_next(&indexer)); + assert_index(1, arridx_next(&indexer)); + assert_next_is_null(&indexer); + + assert_index(2, arridx_first(&indexer)); + assert_index(3, arridx_next(&indexer)); + assert_index(0, arridx_next(&indexer)); + + /* Full traversal from 1 */ + assert_index(1, arridx_first(&indexer)); + assert_index(2, arridx_next(&indexer)); + assert_index(3, arridx_next(&indexer)); + assert_index(0, arridx_next(&indexer)); + assert_next_is_null(&indexer); + + ck_assert_ptr_eq(NULL, indexer.indexes); + arridx_cleanup(&indexer); +} +END_TEST + +static void +test_traversal_with_removal(array_index *(*traverser)(struct circular_indexer *)) +{ + struct circular_indexer indexer; + + arridx_init(&indexer, 5); + + assert_index(0, arridx_first(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_index(1, traverser(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_index(2, traverser(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_index(3, traverser(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_index(4, traverser(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + + assert_next_is_null(&indexer); + assert_first_is_null(&indexer); + assert_next_is_null(&indexer); + + ck_assert_ptr_eq(NULL, indexer.indexes); + arridx_cleanup(&indexer); +} + +START_TEST(always_remove_first) +{ + test_traversal_with_removal(arridx_first); +} +END_TEST + +START_TEST(always_remove_next) +{ + test_traversal_with_removal(arridx_next); +} +END_TEST + +START_TEST(remove_only_top) +{ + /* This one is also unnecessary. */ + + struct circular_indexer indexer; + + arridx_init(&indexer, 5); + + /* 0 1 2 3 4 */ + assert_index(0, arridx_first(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_index(1, arridx_next(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_index(2, arridx_next(&indexer)); + assert_index(3, arridx_next(&indexer)); + assert_index(4, arridx_next(&indexer)); + assert_next_is_null(&indexer); + + /* 2 3 4 (just make sure the indexer was left in a consistent state) */ + assert_index(2, arridx_first(&indexer)); + assert_index(3, arridx_next(&indexer)); + assert_index(4, arridx_next(&indexer)); + assert_next_is_null(&indexer); + + /* 2 3 4 */ + assert_index(2, arridx_first(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_index(3, arridx_next(&indexer)); + assert_index(4, arridx_next(&indexer)); + assert_next_is_null(&indexer); + + /* 3 4 */ + assert_index(3, arridx_first(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_index(4, arridx_next(&indexer)); + assert_next_is_null(&indexer); + + /* 4 */ + assert_index(4, arridx_first(&indexer)); + assert_next_is_null(&indexer); + + /* 4 */ + assert_index(4, arridx_first(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_next_is_null(&indexer); + + /* */ + assert_first_is_null(&indexer); + assert_next_is_null(&indexer); + + ck_assert_ptr_eq(NULL, indexer.indexes); + arridx_cleanup(&indexer); +} +END_TEST + +START_TEST(remove_top_mid_iteration) +{ + struct circular_indexer indexer; + + arridx_init(&indexer, 4); + + /* 0 1 2 3 */ + assert_index(0, arridx_first(&indexer)); + assert_index(1, arridx_next(&indexer)); + assert_index(2, arridx_next(&indexer)); + + /* 3 0 1 2 */ + assert_index(3, arridx_first(&indexer)); + assert_index(0, arridx_next(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_index(1, arridx_next(&indexer)); + assert_index(2, arridx_next(&indexer)); + assert_next_is_null(&indexer); + + /* 3 1 2 */ + assert_index(3, arridx_first(&indexer)); + assert_index(1, arridx_next(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_index(2, arridx_next(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_next_is_null(&indexer); + + /* 3 */ + assert_index(3, arridx_first(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_next_is_null(&indexer); + + /* */ + assert_first_is_null(&indexer); + assert_next_is_null(&indexer); + + ck_assert_ptr_eq(NULL, indexer.indexes); + arridx_cleanup(&indexer); +} +END_TEST + +static void +traverse_mallocd_indexer_easy(array_index *(*traverser)(struct circular_indexer *)) +{ + struct circular_indexer indexer; + + arridx_init(&indexer, 4); + + /* (This iteration is mostly just intended to prepare the array) */ + /* 0 1 2 3 */ + assert_index(0, arridx_first(&indexer)); + assert_index(1, arridx_next(&indexer)); + + ck_assert_ptr_eq(NULL, indexer.indexes); + ck_assert_int_eq(0, arridx_remove(&indexer)); + ck_assert_ptr_ne(NULL, indexer.indexes); + + assert_index(2, arridx_next(&indexer)); + assert_index(3, arridx_next(&indexer)); + assert_next_is_null(&indexer); + + /* (This is the actual test) */ + /* 0 2 3 */ + assert_index(0, arridx_first(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_index(2, traverser(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_index(3, traverser(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_next_is_null(&indexer); + + /* */ + assert_first_is_null(&indexer); + assert_next_is_null(&indexer); + + arridx_cleanup(&indexer); +} + +START_TEST(malloc_always_remove_first_simple) +{ + traverse_mallocd_indexer_easy(arridx_first); +} +END_TEST + +START_TEST(malloc_always_remove_next_simple) +{ + traverse_mallocd_indexer_easy(arridx_next); +} +END_TEST + +/* + * This is the same as traverse_mallocd_indexer(), except it has the first, last + * and two contiguous elements pre-removed, cuz that's trickier. + */ +static void +traverse_mallocd_indexer_hard(array_index *(*traverser)(struct circular_indexer *)) +{ + struct circular_indexer indexer; + + arridx_init(&indexer, 8); + + /* -- Prepare the array -- */ + /* + * Despite being initialization, this actually manhandles the indexer + * quite a bit, which is good. + */ + /* 0 1 2 3 4 5 6 7 */ + assert_index(0, arridx_first(&indexer)); + assert_index(1, arridx_next(&indexer)); + assert_index(2, arridx_next(&indexer)); + + assert_index(3, arridx_next(&indexer)); + ck_assert_ptr_eq(NULL, indexer.indexes); + ck_assert_int_eq(0, arridx_remove(&indexer)); + ck_assert_ptr_ne(NULL, indexer.indexes); + + assert_index(4, arridx_next(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + + assert_index(5, arridx_next(&indexer)); + assert_index(6, arridx_next(&indexer)); + + assert_index(7, arridx_next(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + + assert_next_is_null(&indexer); + + /* 0 1 2 5 6 */ + assert_index(0, arridx_first(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + + /* -- Actual test -- */ + /* Let's do an innocent traversal first, just for shits and giggles. */ + /* 1 2 5 6 */ + assert_index(1, arridx_first(&indexer)); + assert_index(2, arridx_next(&indexer)); + assert_index(5, arridx_next(&indexer)); + assert_index(6, arridx_next(&indexer)); + assert_next_is_null(&indexer); + + /* Ok, begin. */ + /* 1 2 5 6 */ + assert_index(1, arridx_first(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_index(2, traverser(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_index(5, traverser(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_index(6, traverser(&indexer)); + ck_assert_int_eq(0, arridx_remove(&indexer)); + assert_next_is_null(&indexer); + + /* */ + assert_first_is_null(&indexer); + assert_next_is_null(&indexer); + + arridx_cleanup(&indexer); +} + +START_TEST(malloc_always_remove_first_complex) +{ + traverse_mallocd_indexer_hard(arridx_first); +} +END_TEST + +START_TEST(malloc_always_remove_next_complex) +{ + traverse_mallocd_indexer_hard(arridx_next); +} +END_TEST + +Suite *address_load_suite(void) +{ + Suite *suite; + TCase *malloc_no; + TCase *malloc_yes; + + /* Tests in which the indexer.indexes array is not allocated. */ + malloc_no = tcase_create("No malloc tests"); + tcase_add_test(malloc_no, no_removes); + tcase_add_test(malloc_no, always_remove_first); + tcase_add_test(malloc_no, always_remove_next); + tcase_add_test(malloc_no, remove_only_top); + tcase_add_test(malloc_no, remove_top_mid_iteration); + + /* Tests that involve the indexer.indexes array. */ + malloc_yes = tcase_create("malloc tests"); + tcase_add_test(malloc_yes, malloc_always_remove_first_simple); + tcase_add_test(malloc_yes, malloc_always_remove_next_simple); + tcase_add_test(malloc_yes, malloc_always_remove_first_complex); + tcase_add_test(malloc_yes, malloc_always_remove_next_complex); + + suite = suite_create("Circular indexer"); + suite_add_tcase(suite, malloc_no); + suite_add_tcase(suite, malloc_yes); + return suite; +} + +int main(void) +{ + Suite *suite; + SRunner *runner; + int tests_failed; + + suite = address_load_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; +} diff --git a/test/impersonator.c b/test/impersonator.c index d7c7c1d6..41cbbc21 100644 --- a/test/impersonator.c +++ b/test/impersonator.c @@ -64,7 +64,7 @@ config_get_rsync_program(void) } struct string_array const * -config_get_rsync_args(void) +config_get_rsync_args(bool is_ta) { static const struct string_array array = { 0 }; return &array; diff --git a/test/rtr/pdu_test.c b/test/rtr/pdu_test.c index 305db91d..1e4e11b8 100644 --- a/test/rtr/pdu_test.c +++ b/test/rtr/pdu_test.c @@ -26,7 +26,7 @@ init_pdu_header(struct pdu_header *header) { header->protocol_version = 0; header->pdu_type = 22; - header->reserved = 12345; + header->m.reserved = 12345; header->length = 0xFFAA9955; } @@ -35,7 +35,7 @@ assert_pdu_header(struct pdu_header *header) { ck_assert_uint_eq(header->protocol_version, 0); ck_assert_uint_eq(header->pdu_type, 22); - ck_assert_uint_eq(header->reserved, 12345); + ck_assert_uint_eq(header->m.reserved, 12345); ck_assert_uint_eq(header->length, 0xFFAA9955); } @@ -54,7 +54,7 @@ START_TEST(test_pdu_header_from_stream) ck_assert_uint_eq(header.protocol_version, 0); ck_assert_uint_eq(header.pdu_type, 1); - ck_assert_uint_eq(header.reserved, 0x0203); + ck_assert_uint_eq(header.m.reserved, 0x0203); ck_assert_uint_eq(header.length, 0x04050607); } END_TEST @@ -178,7 +178,7 @@ START_TEST(test_error_report_from_stream) sub_pdu = pdu->erroneous_pdu; ck_assert_uint_eq(sub_pdu->header.protocol_version, 1); ck_assert_uint_eq(sub_pdu->header.pdu_type, 0); - ck_assert_uint_eq(sub_pdu->header.reserved, 0x0203); + ck_assert_uint_eq(sub_pdu->header.m.reserved, 0x0203); ck_assert_uint_eq(sub_pdu->header.length, 12); ck_assert_uint_eq(sub_pdu->serial_number, 0x01020304); ck_assert_str_eq(pdu->error_message, "hello"); diff --git a/test/rtr/stream.h b/test/rtr/stream.h index 84f3212d..a3fc2c8f 100644 --- a/test/rtr/stream.h +++ b/test/rtr/stream.h @@ -3,9 +3,7 @@ #include -__BEGIN_DECLS int write_exact(int, unsigned char *, size_t); int buffer2fd(unsigned char *, size_t); -__END_DECLS #endif /* TEST_RTR_STREAM_H_ */