From: Alberto Leiva Popper Date: Tue, 30 Apr 2019 19:56:38 +0000 (-0500) Subject: Change the ROA table from a tree to a hash table X-Git-Tag: v0.0.2~45 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c044b9d2fb8fe22273729ecd666139372334a99f;p=thirdparty%2FFORT-validator.git Change the ROA table from a tree to a hash table Previously, I had missed this requirement: The cache server MUST ensure that it has told the router client to have one and only one IPvX PDU for a unique {Prefix, Len, Max-Len, ASN} at any one point in time. Should the router client receive an IPvX PDU with a {Prefix, Len, Max-Len, ASN} identical to one it already has active, it SHOULD raise a Duplicate Announcement Received error. It literally changes everything. --- diff --git a/src/Makefile.am b/src/Makefile.am index b4a95d4b..c7ce4cdf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -51,8 +51,9 @@ 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 += data_structure/array_list.h +fort_SOURCES += data_structure/common.h +fort_SOURCES += data_structure/uthash.h fort_SOURCES += object/certificate.h object/certificate.c fort_SOURCES += object/crl.h object/crl.c @@ -88,7 +89,7 @@ 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_table.c rtr/db/roa_table.h fort_SOURCES += rtr/db/roa.c rtr/db/roa.h fort_SOURCES += rtr/db/vrps.c rtr/db/vrps.h diff --git a/src/data_structure/circular_indexer.c b/src/data_structure/circular_indexer.c deleted file mode 100644 index d405137a..00000000 --- a/src/data_structure/circular_indexer.c +++ /dev/null @@ -1,144 +0,0 @@ -#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; -} diff --git a/src/data_structure/circular_indexer.h b/src/data_structure/circular_indexer.h deleted file mode 100644 index b79db979..00000000 --- a/src/data_structure/circular_indexer.h +++ /dev/null @@ -1,105 +0,0 @@ -#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 *); - -#endif /* SRC_DATA_STRUCTURE_CIRCULAR_INDEXER_H_ */ diff --git a/src/object/certificate.c b/src/object/certificate.c index 0d728eff..7416244f 100644 --- a/src/object/certificate.c +++ b/src/object/certificate.c @@ -19,8 +19,6 @@ #include "crypto/hash.h" #include "object/name.h" #include "rsync/rsync.h" -#include "rtr/db/roa_tree.h" - #include /* Just to prevent some line breaking. */ diff --git a/src/object/roa.c b/src/object/roa.c index 58d3783f..98362ad7 100644 --- a/src/object/roa.c +++ b/src/object/roa.c @@ -11,7 +11,6 @@ #include "asn1/decode.h" #include "asn1/oid.h" #include "object/signed_object.h" -#include "rtr/db/roa_tree.h" static int roa_decode(OCTET_STRING_t *string, void *arg) diff --git a/src/rtr/db/roa_table.c b/src/rtr/db/roa_table.c new file mode 100644 index 00000000..b166b20d --- /dev/null +++ b/src/rtr/db/roa_table.c @@ -0,0 +1,219 @@ +#include "rtr/db/roa_table.h" + +#include "data_structure/uthash.h" + +struct hashable_roa { + /* + * TODO (whatever) flags is not useful here. + * Maybe separate struct vrp into two structures: One that doesn't + * contain flags, and one that contains the other. + */ + struct vrp data; + UT_hash_handle hh; +}; + +struct roa_table { + struct hashable_roa *roas; + unsigned int references; +}; + +struct roa_table * +roa_table_create(void) +{ + struct roa_table *table; + + table = malloc(sizeof(struct roa_table)); + if (table == NULL) + return NULL; + + table->roas = NULL; + table->references = 1; + return table; +} + +static void +roa_table_cleanup(struct roa_table *table) +{ + struct hashable_roa *node; + struct hashable_roa *tmp; + + HASH_ITER(hh, table->roas, node, tmp) { + HASH_DEL(table->roas, node); + free(node); + } +} + +void +roa_table_get(struct roa_table *table) +{ + table->references++; +} + +void +roa_table_put(struct roa_table *table) +{ + table->references--; + if (table->references == 0) { + roa_table_cleanup(table); + free(table); + } +} + +int +roa_table_foreach_roa(struct roa_table *table, vrp_foreach_cb cb, void *arg) +{ + struct hashable_roa *node; + int error; + + for (node = table->roas; node != NULL; node = node->hh.next) { + error = cb(&node->data, arg); + if (error) + return error; + } + + return 0; +} + +int +rtrhandler_reset(struct roa_table *table) +{ + roa_table_cleanup(table); + return 0; +} + +static struct hashable_roa * +create_roa(uint32_t asn, uint8_t max_length) +{ + struct hashable_roa *roa; + + roa = malloc(sizeof(struct hashable_roa)); + if (roa == NULL) + return NULL; + /* Needed by uthash */ + memset(roa, 0, sizeof(struct hashable_roa)); + + roa->data.asn = asn; + roa->data.max_prefix_length = max_length; + roa->data.flags = FLAG_ANNOUNCEMENT; + + return roa; +} + +static int +add_roa(struct roa_table *table, struct hashable_roa *new) +{ + struct hashable_roa *old; + + HASH_REPLACE(hh, table->roas, data, sizeof(new->data), new, old); + if (old != NULL) + free(old); + + return 0; +} + +int +rtrhandler_handle_roa_v4(struct roa_table *table, uint32_t asn, + struct ipv4_prefix const *prefix4, uint8_t max_length) +{ + struct hashable_roa *roa; + + roa = create_roa(asn, max_length); + if (roa == NULL) + return pr_enomem(); + roa->data.prefix.v4 = prefix4->addr; + roa->data.prefix_length = prefix4->len; + roa->data.addr_fam = AF_INET; + + return add_roa(table, roa); +} + +int +rtrhandler_handle_roa_v6(struct roa_table *table, uint32_t asn, + struct ipv6_prefix const *prefix6, uint8_t max_length) +{ + struct hashable_roa *roa; + + roa = create_roa(asn, max_length); + if (roa == NULL) + return pr_enomem(); + roa->data.prefix.v6 = prefix6->addr; + roa->data.prefix_length = prefix6->len; + roa->data.addr_fam = AF_INET6; + + return add_roa(table, roa); +} + +static int +add_delta(struct deltas *deltas, struct hashable_roa *roa, enum delta_op op) +{ + union { + struct v4_address v4; + struct v6_address v6; + } addr; + + switch (roa->data.addr_fam) { + case AF_INET: + addr.v4.prefix.addr = roa->data.prefix.v4; + addr.v4.prefix.len = roa->data.prefix_length; + addr.v4.max_length = roa->data.max_prefix_length; + return deltas_add_roa_v4(deltas, roa->data.asn, &addr.v4, op); + case AF_INET6: + addr.v6.prefix.addr = roa->data.prefix.v6; + addr.v6.prefix.len = roa->data.prefix_length; + addr.v6.max_length = roa->data.max_prefix_length; + return deltas_add_roa_v6(deltas, roa->data.asn, &addr.v6, op); + } + + return pr_crit("Unknown address family: %d", roa->data.addr_fam); +} + +/* + * Copies `@roas1 - roas2` into @deltas. + * + * (Places the ROAs that exist in @roas1 but not in @roas2 in @deltas.) + */ +static int +add_deltas(struct hashable_roa *roas1, struct hashable_roa *roas2, + struct deltas *deltas, enum delta_op op) +{ + struct hashable_roa *n1; /* A node from @roas1 */ + struct hashable_roa *n2; /* A node from @roas2 */ + int error; + + for (n1 = roas1; n1 != NULL; n1 = n1->hh.next) { + HASH_FIND(hh, roas2, &n1->data, sizeof(n1->data), n2); + if (n2 == NULL) { + error = add_delta(deltas, n1, op); + if (error) + return error; + } + } + + return 0; +} + +int +compute_deltas(struct roa_table *old, struct roa_table *new, + struct deltas **result) +{ + struct deltas *deltas; + int error; + + error = deltas_create(&deltas); + if (error) + return error; + + error = add_deltas(new->roas, old->roas, deltas, DELTA_ADD); + if (error) + goto fail; + error = add_deltas(old->roas, new->roas, deltas, DELTA_RM); + if (error) + goto fail; + + *result = deltas; + return 0; + +fail: + deltas_destroy(deltas); + return error; +} diff --git a/src/rtr/db/roa_table.h b/src/rtr/db/roa_table.h new file mode 100644 index 00000000..f9a3122d --- /dev/null +++ b/src/rtr/db/roa_table.h @@ -0,0 +1,25 @@ +#ifndef SRC_ROA_TABLE_H_ +#define SRC_ROA_TABLE_H_ + +#include "rtr/db/delta.h" +#include "rtr/db/vrp.h" + +struct roa_table; + +/* Constructor */ +struct roa_table *roa_table_create(void); +/* Reference counting */ +void roa_table_get(struct roa_table *); +void roa_table_put(struct roa_table *); + +int roa_table_foreach_roa(struct roa_table *, vrp_foreach_cb, void *); + +int rtrhandler_reset(struct roa_table *); +int rtrhandler_handle_roa_v4(struct roa_table *, uint32_t, + struct ipv4_prefix const *, uint8_t); +int rtrhandler_handle_roa_v6(struct roa_table *, uint32_t, + struct ipv6_prefix const *, uint8_t); + +int compute_deltas(struct roa_table *, struct roa_table *, struct deltas **); + +#endif /* SRC_ROA_TABLE_H_ */ diff --git a/src/rtr/db/roa_tree.c b/src/rtr/db/roa_tree.c deleted file mode 100644 index d29c01c7..00000000 --- a/src/rtr/db/roa_tree.c +++ /dev/null @@ -1,539 +0,0 @@ -#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 void -node_init(struct node *node, struct rfc5280_name *subject_name, - struct node *parent) -{ - node->subject_name = subject_name; - x509_name_get(subject_name); - node->parent = parent; - nodes_init(&node->children); - node->roa = NULL; -} - -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_init(node, subject_name, parent); - return node; -} - -static void -node_cleanup(struct node *node) -{ - if (node->subject_name != NULL) - x509_name_put(node->subject_name); - nodes_cleanup(&node->children, node_cleanup); - if (node->roa != NULL) - roa_destroy(node->roa); -} - -static int -node_add_child(struct node *parent, struct rfc5280_name *subject_name) -{ - struct node child; - int error; - - node_init(&child, subject_name, parent); - - error = nodes_add(&parent->children, &child); - if (error) - node_cleanup(&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_cleanup(tree->root); - free(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 (node->roa != NULL) { - error = __foreach_v4(node->roa, cb, arg); - if (error) - return error; - error = __foreach_v6(node->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; -} - -struct node * -get_last_node(struct nodes *nodes) -{ - if (nodes->array == NULL || nodes->len == 0) - return NULL; - - return &nodes->array[nodes->len - 1]; -} - -int -forthandler_go_down(struct roa_tree *tree, - struct rfc5280_name *subject_name) -{ - int error; - if (tree->current != NULL) { - error = node_add_child(tree->current, subject_name); - if (error) - return error; - tree->current = get_last_node(&tree->current->children); - return 0; - } - - 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) - 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 *c1node; - - struct node *c2array; - array_index c2; /* counter for c2array */ - array_index *c2p; /* counter for c2array, pointer */ - - struct circular_indexer c2indexer; - - int error = 0; - - c2array = children2->array; - arridx_init(&c2indexer, children2->len); - - ARRAYLIST_FOREACH(children1, c1node) { - if (find_subject_name(&c2indexer, c1node->subject_name, - c2array, &c2)) { - error = compute_deltas_node(c1node, &c2array[c2], - deltas); - if (error) - goto end; - - error = arridx_remove(&c2indexer); - } else { - error = add_all_deltas(c1node, 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; - - /** TODO I still need to validate that this is ok */ - if (n1->roa == NULL || n2->roa == NULL) - return 0; - - 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 deleted file mode 100644 index b6b37087..00000000 --- a/src/rtr/db/roa_tree.h +++ /dev/null @@ -1,30 +0,0 @@ -#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 (urgent) 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 index 5b868268..9a520eca 100644 --- a/src/rtr/db/vrp.h +++ b/src/rtr/db/vrp.h @@ -6,6 +6,11 @@ struct vrp { uint32_t asn; + /* + * TODO (whatever) convert to ipv*_prefix? (from address.h) + * Most of the time, @prefix and @prefix_length are copied from or into + * ipv*_prefixes. + */ union { struct in_addr v4; struct in6_addr v6; diff --git a/src/rtr/db/vrps.c b/src/rtr/db/vrps.c index 8dd21735..b5b753ed 100644 --- a/src/rtr/db/vrps.c +++ b/src/rtr/db/vrps.c @@ -21,7 +21,7 @@ struct delta { ARRAY_LIST(deltas_db, struct delta) struct state { - struct roa_tree *base; /** All the current valid ROAs */ + struct roa_table *base; /** All the current valid ROAs */ struct deltas_db deltas; /** ROA changes to @base over time */ uint32_t current_serial; @@ -74,7 +74,7 @@ vrps_init(void) void vrps_destroy(void) { - roa_tree_put(state.base); + roa_table_put(state.base); deltas_db_cleanup(&state.deltas, delta_destroy); pthread_rwlock_destroy(&lock); /* Nothing to do with error code */ } @@ -83,7 +83,7 @@ vrps_destroy(void) * @new_deltas can be NULL, @new_tree cannot. */ int -vrps_update(struct roa_tree *new_tree, struct deltas *new_deltas) +vrps_update(struct roa_table *new_roas, struct deltas *new_deltas) { struct delta new_delta; int error = 0; @@ -99,9 +99,9 @@ vrps_update(struct roa_tree *new_tree, struct deltas *new_deltas) } if (state.base != NULL) - roa_tree_put(state.base); - state.base = new_tree; - roa_tree_get(new_tree); + roa_table_put(state.base); + state.base = new_roas; + roa_table_get(new_roas); state.current_serial++; end: @@ -179,7 +179,7 @@ vrps_foreach_base_roa(vrp_foreach_cb cb, void *arg) if (error) return error; - error = roa_tree_foreach_roa(state.base, cb, arg); + error = roa_table_foreach_roa(state.base, cb, arg); rwlock_unlock(&lock); diff --git a/src/rtr/db/vrps.h b/src/rtr/db/vrps.h index 7d3be17e..77fe1223 100644 --- a/src/rtr/db/vrps.h +++ b/src/rtr/db/vrps.h @@ -3,8 +3,9 @@ #include #include + #include "rtr/db/delta.h" -#include "rtr/db/roa_tree.h" +#include "rtr/db/roa_table.h" enum delta_status { /** There's no data at the DB */ @@ -20,7 +21,7 @@ enum delta_status { int vrps_init(void); void vrps_destroy(void); -int vrps_update(struct roa_tree *, struct deltas *); +int vrps_update(struct roa_table *, struct deltas *); int deltas_db_status(uint32_t *, enum delta_status *); int vrps_foreach_base_roa(vrp_foreach_cb, void *); diff --git a/src/updates_daemon.c b/src/updates_daemon.c index 422d3e90..2dea6b40 100644 --- a/src/updates_daemon.c +++ b/src/updates_daemon.c @@ -15,52 +15,40 @@ 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); + return rtrhandler_reset(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); + return rtrhandler_handle_roa_v4(arg, as, prefix, max_length); } int __handle_roa_v6(uint32_t as, struct ipv6_prefix const * prefix, uint8_t max_length, void *arg) { - return forthandler_handle_roa_v6(arg, as, prefix, max_length); + return rtrhandler_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 roa_table *old_roas; struct deltas *deltas; int error; validation_handler.reset = __reset; - validation_handler.traverse_down = __traverse_down; - validation_handler.traverse_up = __traverse_up; + validation_handler.traverse_down = NULL; + validation_handler.traverse_up = NULL; validation_handler.handle_roa_v4 = __handle_roa_v4; validation_handler.handle_roa_v6 = __handle_roa_v6; - old_tree = NULL; + old_roas = NULL; do { - validation_handler.arg = roa_tree_create(); + validation_handler.arg = roa_table_create(); if (validation_handler.arg == NULL) { pr_err("Memory allocation failed. Cannot validate. Sleeping..."); goto sleep; @@ -68,34 +56,34 @@ check_vrps_updates(void *param_void) error = perform_standalone_validation(&validation_handler); if (error) { - roa_tree_put(validation_handler.arg); + roa_table_put(validation_handler.arg); pr_err("Validation failed (error code %d). Cannot udpate the ROA database. Sleeping...", error); goto sleep; } - if (old_tree == NULL) { + if (old_roas == NULL) { error = vrps_update(validation_handler.arg, NULL); if (error) { - roa_tree_put(validation_handler.arg); + roa_table_put(validation_handler.arg); pr_err("Error code %d while trying to update the ROA database. Sleeping...", error); } else { - old_tree = validation_handler.arg; + old_roas = validation_handler.arg; } goto sleep; } - error = compute_deltas(old_tree, validation_handler.arg, &deltas); + error = compute_deltas(old_roas, validation_handler.arg, &deltas); if (error) { - roa_tree_put(validation_handler.arg); + roa_table_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 (deltas_is_empty(deltas)) { - roa_tree_put(validation_handler.arg); + roa_table_put(validation_handler.arg); deltas_destroy(deltas); pr_debug("No changes. Sleeping..."); goto sleep; @@ -103,14 +91,14 @@ check_vrps_updates(void *param_void) error = vrps_update(validation_handler.arg, deltas); if (error) { - roa_tree_put(validation_handler.arg); + roa_table_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; + old_roas = validation_handler.arg; error = notify_clients(); if (error) pr_debug("Could not notify clients of the new VRPs. (Error code %d.) Sleeping...", diff --git a/test/Makefile.am b/test/Makefile.am index bd7ecf7e..6cb82fb6 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -21,10 +21,10 @@ BASIC_MODULES = ../src/log.c ../src/log.h BASIC_MODULES += impersonator.c check_PROGRAMS = address.test -check_PROGRAMS += circular_indexer.test check_PROGRAMS += clients.test check_PROGRAMS += line_file.test check_PROGRAMS += rsync.test +check_PROGRAMS += roa_table.test #check_PROGRAMS += tal.test check_PROGRAMS += vcard.test #check_PROGRAMS += rtr/pdu.test @@ -36,11 +36,6 @@ 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} - clients_test_SOURCES = ${BASIC_MODULES} clients_test_SOURCES += ../src/clients.c clients_test_SOURCES += ../src/common.c @@ -53,6 +48,12 @@ line_file_test_SOURCES += ../src/line_file.c ../src/line_file.h line_file_test_SOURCES += line_file_test.c line_file_test_LDADD = ${MY_LDADD} +roa_table_test_SOURCES = ${BASIC_MODULES} +roa_table_test_SOURCES += ../src/rtr/db/delta.c +roa_table_test_SOURCES += ../src/rtr/db/roa_table.c +roa_table_test_SOURCES += rtr/db/roa_table_test.c +roa_table_test_LDADD = ${MY_LDADD} + rsync_test_SOURCES = ${BASIC_MODULES} rsync_test_SOURCES += ../src/str.c ../src/str.h rsync_test_SOURCES += ../src/uri.c ../src/uri.h diff --git a/test/data_structure/circular_indexer_test.c b/test/data_structure/circular_indexer_test.c deleted file mode 100644 index 6ad87d85..00000000 --- a/test/data_structure/circular_indexer_test.c +++ /dev/null @@ -1,383 +0,0 @@ -#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/rtr/db/roa_table_test.c b/test/rtr/db/roa_table_test.c new file mode 100644 index 00000000..6b3850f8 --- /dev/null +++ b/test/rtr/db/roa_table_test.c @@ -0,0 +1,190 @@ +#include +#include +#include "thread_var.h" +#include "rtr/db/roa_table.h" + +#define ADDR1 htonl(0xC0000201) /* 192.0.2.1 */ +#define ADDR2 htonl(0xC0000202) /* 192.0.2.2 */ + +#define TOTAL_ROAS 10 +static bool roas_found[TOTAL_ROAS]; +static unsigned int total_found; + +static bool +vrp_equals_v4(struct vrp *vrp, uint8_t as, uint32_t addr, uint8_t prefix_len, + uint8_t max_prefix_len) +{ + return (AF_INET == vrp->addr_fam) + && (as == vrp->asn) + && (addr == vrp->prefix.v4.s_addr) + && (prefix_len == vrp->prefix_length) + && (max_prefix_len == vrp->max_prefix_length); +} + +static bool +vrp_equals_v6(struct vrp *vrp, uint8_t as, uint32_t addr, uint8_t prefix_len, + uint8_t max_prefix_len) +{ + return (AF_INET6 == vrp->addr_fam) + && (as == vrp->asn) + && (htonl(0x20010DB8) == vrp->prefix.v6.s6_addr32[0]) + && (0 == vrp->prefix.v6.s6_addr32[1]) + && (0 == vrp->prefix.v6.s6_addr32[2]) + && (htonl(addr) == vrp->prefix.v6.s6_addr32[3]) + && (prefix_len == vrp->prefix_length) + && (max_prefix_len == vrp->max_prefix_length); +} + +static int +update_found(array_index index) +{ + ck_assert_int_eq(false, roas_found[index]); + roas_found[index] = true; + total_found++; + return 0; +} + +static int +foreach_cb(struct vrp *vrp, void *arg) +{ + char const *str; + + if (vrp_equals_v4(vrp, 10, ADDR1, 24, 32)) + return update_found(0); + if (vrp_equals_v4(vrp, 11, ADDR1, 24, 32)) + return update_found(1); + if (vrp_equals_v4(vrp, 10, ADDR2, 24, 32)) + return update_found(2); + if (vrp_equals_v4(vrp, 10, ADDR1, 25, 32)) + return update_found(3); + if (vrp_equals_v4(vrp, 10, ADDR1, 24, 30)) + return update_found(4); + + if (vrp_equals_v6(vrp, 10, 1, 120, 128)) + return update_found(5); + if (vrp_equals_v6(vrp, 11, 1, 120, 128)) + return update_found(6); + if (vrp_equals_v6(vrp, 10, 2, 120, 128)) + return update_found(7); + if (vrp_equals_v6(vrp, 10, 1, 121, 128)) + return update_found(8); + if (vrp_equals_v6(vrp, 10, 1, 120, 127)) + return update_found(9); + + switch (vrp->addr_fam) { + case AF_INET: + str = v4addr2str(&vrp->prefix.v4); + break; + case AF_INET6: + str = v6addr2str(&vrp->prefix.v6); + break; + default: + ck_abort_msg("Unknown address family: %u", vrp->addr_fam); + } + + ck_abort_msg("Foreach is looping over unknown VRP %u/%s/%u/%u.", + vrp->asn, str, vrp->prefix_length, vrp->max_prefix_length); +} + +START_TEST(test_basic) +{ + struct ipv4_prefix prefix4; + struct ipv6_prefix prefix6; + struct roa_table *table; + array_index i; + + table = roa_table_create(); + ck_assert_ptr_ne(NULL, table); + + prefix4.addr.s_addr = ADDR1; + prefix4.len = 24; + prefix6.addr.s6_addr32[0] = htonl(0x20010DB8); + prefix6.addr.s6_addr32[1] = 0; + prefix6.addr.s6_addr32[2] = 0; + prefix6.addr.s6_addr32[3] = htonl(0x00000001); + prefix6.len = 120; + + /* Duplicates should be transparently not re-added. */ + + ck_assert_int_eq(0, rtrhandler_handle_roa_v4(table, 10, &prefix4, 32)); + ck_assert_int_eq(0, rtrhandler_handle_roa_v4(table, 10, &prefix4, 32)); + + /* Change the AS slightly */ + ck_assert_int_eq(0, rtrhandler_handle_roa_v4(table, 11, &prefix4, 32)); + ck_assert_int_eq(0, rtrhandler_handle_roa_v4(table, 11, &prefix4, 32)); + + /* Change the prefix slightly */ + prefix4.addr.s_addr = ADDR2; + ck_assert_int_eq(0, rtrhandler_handle_roa_v4(table, 10, &prefix4, 32)); + ck_assert_int_eq(0, rtrhandler_handle_roa_v4(table, 10, &prefix4, 32)); + + prefix4.addr.s_addr = ADDR1; + prefix4.len = 25; + ck_assert_int_eq(0, rtrhandler_handle_roa_v4(table, 10, &prefix4, 32)); + ck_assert_int_eq(0, rtrhandler_handle_roa_v4(table, 10, &prefix4, 32)); + + /* Change the max prefix length slightly */ + prefix4.len = 24; + ck_assert_int_eq(0, rtrhandler_handle_roa_v4(table, 10, &prefix4, 30)); + ck_assert_int_eq(0, rtrhandler_handle_roa_v4(table, 10, &prefix4, 30)); + + /* IPv6 */ + ck_assert_int_eq(0, rtrhandler_handle_roa_v6(table, 10, &prefix6, 128)); + ck_assert_int_eq(0, rtrhandler_handle_roa_v6(table, 10, &prefix6, 128)); + + ck_assert_int_eq(0, rtrhandler_handle_roa_v6(table, 11, &prefix6, 128)); + ck_assert_int_eq(0, rtrhandler_handle_roa_v6(table, 11, &prefix6, 128)); + + prefix6.addr.s6_addr32[3] = htonl(0x00000002); + ck_assert_int_eq(0, rtrhandler_handle_roa_v6(table, 10, &prefix6, 128)); + ck_assert_int_eq(0, rtrhandler_handle_roa_v6(table, 10, &prefix6, 128)); + + prefix6.addr.s6_addr32[3] = htonl(0x00000001); + prefix6.len = 121; + ck_assert_int_eq(0, rtrhandler_handle_roa_v6(table, 10, &prefix6, 128)); + ck_assert_int_eq(0, rtrhandler_handle_roa_v6(table, 10, &prefix6, 128)); + + prefix6.len = 120; + ck_assert_int_eq(0, rtrhandler_handle_roa_v6(table, 10, &prefix6, 127)); + ck_assert_int_eq(0, rtrhandler_handle_roa_v6(table, 10, &prefix6, 127)); + + /* Check table contents */ + memset(roas_found, 0, sizeof(roas_found)); + total_found = 0; + ck_assert_int_eq(0, roa_table_foreach_roa(table, foreach_cb, NULL)); + ck_assert_int_eq(TOTAL_ROAS, total_found); + for (i = 0; i < TOTAL_ROAS; i++) + ck_assert_int_eq(true, roas_found[i]); + + roa_table_put(table); +} +END_TEST + +Suite *pdu_suite(void) +{ + Suite *suite; + TCase *core; + + core = tcase_create("Core"); + tcase_add_test(core, test_basic); + + suite = suite_create("ROA Table"); + suite_add_tcase(suite, core); + return suite; +} + +int main(void) +{ + Suite *suite; + SRunner *runner; + int tests_failed; + + suite = pdu_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; +}