]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Change the ROA table from a tree to a hash table
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 30 Apr 2019 19:56:38 +0000 (14:56 -0500)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 30 Apr 2019 19:56:38 +0000 (14:56 -0500)
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.

16 files changed:
src/Makefile.am
src/data_structure/circular_indexer.c [deleted file]
src/data_structure/circular_indexer.h [deleted file]
src/object/certificate.c
src/object/roa.c
src/rtr/db/roa_table.c [new file with mode: 0644]
src/rtr/db/roa_table.h [new file with mode: 0644]
src/rtr/db/roa_tree.c [deleted file]
src/rtr/db/roa_tree.h [deleted file]
src/rtr/db/vrp.h
src/rtr/db/vrps.c
src/rtr/db/vrps.h
src/updates_daemon.c
test/Makefile.am
test/data_structure/circular_indexer_test.c [deleted file]
test/rtr/db/roa_table_test.c [new file with mode: 0644]

index b4a95d4b2ccda5d7af3d046ffd7bfe1bc8f6b464..c7ce4cdfec0dde827929e1be36e8fe1f27774023 100644 (file)
@@ -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 (file)
index d405137..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-#include "data_structure/circular_indexer.h"
-
-#include <stdlib.h>
-#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 (file)
index b79db97..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-#ifndef SRC_DATA_STRUCTURE_CIRCULAR_INDEXER_H_
-#define SRC_DATA_STRUCTURE_CIRCULAR_INDEXER_H_
-
-#include <stdbool.h>
-#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_ */
index 0d728eff4ca41436f17a70eaea2f4ebeef9a3322..7416244fbaf6986af9bdb9dd8e4833ef779f1943 100644 (file)
@@ -19,8 +19,6 @@
 #include "crypto/hash.h"
 #include "object/name.h"
 #include "rsync/rsync.h"
-#include "rtr/db/roa_tree.h"
-
 #include <sys/socket.h>
 
 /* Just to prevent some line breaking. */
index 58d3783f3edc59b4c842dec45c772271cc96974f..98362ad7900ed9941369d4b3487a4360b7852f57 100644 (file)
@@ -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 (file)
index 0000000..b166b20
--- /dev/null
@@ -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 (file)
index 0000000..f9a3122
--- /dev/null
@@ -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 (file)
index d29c01c..0000000
+++ /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 (file)
index b6b3708..0000000
+++ /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_ */
index 5b868268f816999551e9dbbf2050ad4eba16bf3c..9a520eca266ae4d464d16cca93501c9d14173b32 100644 (file)
@@ -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;
index 8dd217358c6dbb525d7992c4326ac96fa9ab6d8f..b5b753ed0d0eb271c9ad8b6a84d96a02f7e2138d 100644 (file)
@@ -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);
 
index 7d3be17e2c4619e7047e83d77649f6b59c42a589..77fe1223d58c219e787181078e57a53a41307491 100644 (file)
@@ -3,8 +3,9 @@
 
 #include <time.h>
 #include <netinet/ip.h>
+
 #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 *);
index 422d3e90cd624b03a616a2ffb6cf65b25f70eec9..2dea6b4097572e8f317638206d9386d39ab259c2 100644 (file)
@@ -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...",
index bd7ecf7eec2d6b7f41b2329244b9f93d987469c0..6cb82fb6377739ab7e1117969a26b7d99a5785b1 100644 (file)
@@ -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 (file)
index 6ad87d8..0000000
+++ /dev/null
@@ -1,383 +0,0 @@
-#include "data_structure/circular_indexer.h"
-
-#include <check.h>
-#include <errno.h>
-#include <stdlib.h>
-
-/*
- * 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 (file)
index 0000000..6b3850f
--- /dev/null
@@ -0,0 +1,190 @@
+#include <check.h>
+#include <stdlib.h>
+#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;
+}