# Comment these out during releases.
# Also increase optimization I guess (-O0 -> -On)
-CFLAGS_DEBUG = -DDEBUG
+#CFLAGS_DEBUG = -DDEBUG
LDFLAGS_DEBUG = -rdynamic
bin_PROGRAMS = fort
fort_SOURCES += address.h address.c
fort_SOURCES += algorithm.h algorithm.c
-fort_SOURCES += array_list.h
fort_SOURCES += certificate_refs.h certificate_refs.c
+fort_SOURCES += console_handler.h console_handler.c
fort_SOURCES += common.h
fort_SOURCES += config.h config.c
fort_SOURCES += debug.h debug.c
fort_SOURCES += thread_var.h thread_var.c
fort_SOURCES += uri.h uri.c
fort_SOURCES += json_handler.h json_handler.c
+fort_SOURCES += validation_handler.h validation_handler.c
fort_SOURCES += rsync/rsync.h rsync/rsync.c
fort_SOURCES += config/uint.c config/uint.h
fort_SOURCES += config/uint32.c config/uint32.h
+fort_SOURCES += data_structure/array_list.h
+
fort_SOURCES += crypto/base64.h crypto/base64.c
fort_SOURCES += crypto/hash.h crypto/hash.c
+fort_SOURCES += data_structure/circular_indexer.h
+fort_SOURCES += data_structure/circular_indexer.c
+
fort_SOURCES += object/certificate.h object/certificate.c
fort_SOURCES += object/crl.h object/crl.c
fort_SOURCES += object/ghostbusters.h object/ghostbusters.c
fort_SOURCES += common.c common.h
fort_SOURCES += notify.c notify.h
fort_SOURCES += updates_daemon.c updates_daemon.h
-fort_SOURCES += vrps.c vrps.h
fort_SOURCES += rtr/err_pdu.c rtr/err_pdu.h
fort_SOURCES += rtr/pdu_handler.c rtr/pdu_handler.h
fort_SOURCES += rtr/primitive_writer.c rtr/primitive_writer.h
fort_SOURCES += rtr/rtr.c rtr/rtr.h
+fort_SOURCES += rtr/db/delta.c rtr/db/delta.h
+fort_SOURCES += rtr/db/roa_tree.c rtr/db/roa_tree.h
+fort_SOURCES += rtr/db/roa.c rtr/db/roa.h
+fort_SOURCES += rtr/db/vrps.c rtr/db/vrps.h
+
fort_CFLAGS = -Wall
# Feel free to temporarily remove this one if you're not using gcc 7.3.0.
-fort_CFLAGS += $(GCC_WARNS)
+#fort_CFLAGS += $(GCC_WARNS)
fort_CFLAGS += -std=gnu99 -O0 -g $(CFLAGS_DEBUG)
fort_LDFLAGS = $(LDFLAGS_DEBUG)
fort_LDADD = ${JANSSON_LIBS}
# I'm tired of scrolling up, but feel free to comment this out.
-#GCC_WARNS = -fmax-errors=1
+GCC_WARNS = -fmax-errors=1
# Please ready some arguments if you want to permanently remove some of these
# flags. I gave a quick read to the documentation of all of these warnings, and
# generally liked each of them.
-GCC_WARNS = -Wpedantic -pedantic-errors -Waddress -Walloc-zero -Walloca
+GCC_WARNS += -Wpedantic -pedantic-errors -Waddress -Walloc-zero -Walloca
GCC_WARNS += -Wno-aggressive-loop-optimizations -Warray-bounds=2 -Wbool-compare
GCC_WARNS += -Wbool-operation -Wno-builtin-declaration-mismatch -Wcast-align
GCC_WARNS += -Wcast-qual -Wchar-subscripts -Wchkp -Wclobbered -Wcomment
}
}
+bool
+prefix4_equals(struct ipv4_prefix const *a, struct ipv4_prefix const *b)
+{
+ return (a->addr.s_addr == b->addr.s_addr) && (a->len == b->len);
+}
+
+bool
+prefix6_equals(struct ipv6_prefix const *a, struct ipv6_prefix const *b)
+{
+ unsigned int i;
+
+ /*
+ * Not sure if I can use a memcmp() instead.
+ * I feel like in6_addr's union could cause padding in weird
+ * implementations.
+ */
+ for (i = 0; i < 16; i++)
+ if (a->addr.s6_addr[i] != b->addr.s6_addr[i])
+ return false;
+
+ return a->len == b->len;
+}
+
/**
* Translates an `IPAddress_t` to its equivalent `struct ipv4_prefix`.
*/
int
-prefix4_decode(IPAddress_t *str, struct ipv4_prefix *result)
+prefix4_decode(IPAddress_t const *str, struct ipv4_prefix *result)
{
int len;
* Translates an `IPAddress_t` to its equivalent `struct ipv6_prefix`.
*/
int
-prefix6_decode(IPAddress_t *str, struct ipv6_prefix *result)
+prefix6_decode(IPAddress_t const *str, struct ipv6_prefix *result)
{
struct in6_addr suffix;
int len;
* Translates an `IPAddressRange_t` to its equivalent `struct ipv4_range`.
*/
int
-range4_decode(IPAddressRange_t *input, struct ipv4_range *result)
+range4_decode(IPAddressRange_t const *input, struct ipv4_range *result)
{
struct ipv4_prefix prefix;
int error;
* Translates an `IPAddressRange_t` to its equivalent `struct ipv6_range`.
*/
int
-range6_decode(IPAddressRange_t *input, struct ipv6_range *result)
+range6_decode(IPAddressRange_t const *input, struct ipv6_range *result)
{
struct ipv6_prefix prefix;
int error;
uint32_t be32_suffix_mask(unsigned int);
void ipv6_suffix_mask(unsigned int, struct in6_addr *);
-int prefix4_decode(IPAddress_t *, struct ipv4_prefix *);
-int prefix6_decode(IPAddress_t *, struct ipv6_prefix *);
-int range4_decode(IPAddressRange_t *, struct ipv4_range *);
-int range6_decode(IPAddressRange_t *, struct ipv6_range *);
+bool prefix4_equals(struct ipv4_prefix const *, struct ipv4_prefix const *);
+bool prefix6_equals(struct ipv6_prefix const *, struct ipv6_prefix const *);
+
+int prefix4_decode(IPAddress_t const *, struct ipv4_prefix *);
+int prefix6_decode(IPAddress_t const *, struct ipv6_prefix *);
+int range4_decode(IPAddressRange_t const *, struct ipv4_range *);
+int range6_decode(IPAddressRange_t const *, struct ipv6_range *);
#endif /* SRC_ADDRESS_H_ */
-#include "signed_data.h"
+#include "asn1/signed_data.h"
#include <errno.h>
#include <libcmscodec/ContentType.h>
args->uri = uri;
args->crls = crls;
memset(&args->refs, 0, sizeof(args->refs));
+ args->subject_name = NULL;
return 0;
}
{
resources_destroy(args->res);
refs_cleanup(&args->refs);
+ if (args->subject_name != NULL)
+ x509_name_put(args->subject_name);
}
static int
error = certificate_validate_chain(cert, args->crls);
if (error)
goto end2;
- error = certificate_validate_rfc6487(cert, false);
+ error = certificate_validate_rfc6487(cert, &args->subject_name, false);
if (error)
goto end2;
error = certificate_validate_extensions_ee(cert, sid, &args->refs,
* recorded for future validation.
*/
struct certificate_refs refs;
+ /** Certificate's subject name field */
+ struct rfc5280_name *subject_name;
};
int signed_object_args_init(struct signed_object_args *,
#include "clients.h"
-#include "array_list.h"
#include "common.h"
+#include "data_structure/array_list.h"
#define SADDR_IN(addr) ((struct sockaddr_in *)addr)
#define SADDR_IN6(addr) ((struct sockaddr_in6 *)addr)
ARRAY_LIST(clientsdb, struct client)
-struct clientsdb clients_db;
+static struct clientsdb clients_db;
/* Read and Write locks */
sem_t rlock, wlock;
--- /dev/null
+#include "console_handler.h"
+
+#include "thread_var.h"
+#include "validation_handler.h"
+#include "object/tal.h"
+
+static int
+print_v4_roa(uint32_t as, struct ipv4_prefix const *prefix, uint8_t max_length,
+ void *arg)
+{
+ printf("AS%u,%s/%u,%u\n", as, v4addr2str(&prefix->addr), prefix->len,
+ max_length);
+ return 0;
+}
+
+static int
+print_v6_roa(uint32_t as, struct ipv6_prefix const *prefix, uint8_t max_length,
+ void *arg)
+{
+ printf("AS%u,%s/%u,%u\n", as, v6addr2str(&prefix->addr), prefix->len,
+ max_length);
+ return 0;
+}
+
+int
+validate_into_console(void)
+{
+ struct validation_handler handler;
+
+ handler.reset = NULL;
+ handler.traverse_down = NULL;
+ handler.traverse_up = NULL;
+ handler.handle_roa_v4 = print_v4_roa;
+ handler.handle_roa_v6 = print_v6_roa;
+ handler.arg = NULL;
+
+ return perform_standalone_validation(&handler);
+}
--- /dev/null
+#ifndef SRC_CONSOLE_HANDLER_H_
+#define SRC_CONSOLE_HANDLER_H_
+
+int validate_into_console(void);
+
+#endif /* SRC_CONSOLE_HANDLER_H_ */
-#ifndef SRC_ARRAY_LIST_H_
-#define SRC_ARRAY_LIST_H_
+#ifndef SRC_DATA_STRUCTURE_ARRAY_LIST_H_
+#define SRC_DATA_STRUCTURE_ARRAY_LIST_H_
#include <errno.h>
#include <stdlib.h>
#include "log.h"
+#include "data_structure/common.h"
-#define ARRAY_LIST(name, elem_type) \
+/* TODO sizes used to be unsigned ints. Check callers. */
+#define DEFINE_ARRAY_LIST_STRUCT(name, elem_type) \
struct name { \
/** Unidimensional array. */ \
elem_type *array; \
/** Number of elements in @array. */ \
- unsigned int len; \
+ size_t len; \
/** Actual allocated slots in @array. */ \
- unsigned int capacity; \
- }; \
- \
+ size_t capacity; \
+ }
+
+#define DEFINE_ARRAY_LIST_FUNCTIONS(name, elem_type) \
static int \
name##_init(struct name *list) \
{ \
list->capacity = 8; \
list->len = 0; \
+ /* TODO I need lazy initialization of this badly */ \
list->array = malloc(list->capacity \
* sizeof(elem_type)); \
return (list->array != NULL) ? 0 : pr_enomem(); \
static void \
name##_cleanup(struct name *list, void (*cb)(elem_type *)) \
{ \
- unsigned int i; \
- for (i = 0; i < list->len; i++) \
- cb(&list->array[i]); \
+ array_index i; \
+ /* TODO recently added this. Use it more */ \
+ if (cb != NULL) \
+ for (i = 0; i < list->len; i++) \
+ cb(&list->array[i]); \
free(list->array); \
} \
\
return 0; \
}
+#define ARRAY_LIST(name, elem_type) \
+ DEFINE_ARRAY_LIST_STRUCT(name, elem_type); \
+ DEFINE_ARRAY_LIST_FUNCTIONS(name, elem_type)
+
#define ARRAYLIST_FOREACH(list, cursor) for ( \
cursor = (list)->array; \
(cursor - ((typeof(cursor)) ((list)->array))) < (list)->len; \
cursor++ \
)
-#endif /* SRC_ARRAY_LIST_H_ */
+#endif /* SRC_DATA_STRUCTURE_ARRAY_LIST_H_ */
--- /dev/null
+#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;
+}
+
+void
+arridx_print(char const *prefix, struct circular_indexer *i)
+{
+ array_index o;
+ array_index p;
+
+ pr_info("%s:", prefix);
+ if (i->indexes != NULL) {
+ pr_info(" indexes:");
+ p = i->first;
+ for (o = 0; o < i->len; o++) {
+ pr_info(" %zu", p);
+ p = i->indexes[p].next;
+ }
+ }
+
+ pr_info(" first:%zu current:%zu top:%zu len:%zu allow:%d", i->first,
+ i->current, i->top, i->len, i->allow_another_lap);
+}
--- /dev/null
+#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 *);
+
+/* TODO remove me */
+void arridx_print(char const *, struct circular_indexer *);
+
+#endif /* SRC_DATA_STRUCTURE_CIRCULAR_INDEXER_H_ */
--- /dev/null
+#ifndef SRC_DATA_STRUCTURE_COMMON_H_
+#define SRC_DATA_STRUCTURE_COMMON_H_
+
+#include <stddef.h>
+
+typedef size_t array_index;
+
+#endif /* SRC_DATA_STRUCTURE_COMMON_H_ */
#include "clients.h"
#include "config.h"
+#include "console_handler.h"
#include "debug.h"
#include "extension.h"
#include "nid.h"
#include "thread_var.h"
-#include "vrps.h"
#include "rsync/rsync.h"
#include "rtr/rtr.h"
+#include "rtr/db/vrps.h"
static int
start_rtr_server(void)
{
int error;
- error = deltas_db_init();
+ error = vrps_init();
if (error)
goto end1;
rtr_cleanup(); /* TODO shouldn't this only happen on !error? */
clients_db_destroy();
-end2: deltas_db_destroy();
+end2: vrps_destroy();
end1: return error;
}
if (error)
goto revert_rsync;
- error = perform_standalone_validation(NULL);
- if (error)
- goto revert_rsync;
-
- if (config_get_server_address() != NULL)
- error = start_rtr_server();
- /* Otherwise, no server requested. */
+ error = (config_get_server_address() != NULL)
+ ? start_rtr_server()
+ : validate_into_console();
revert_rsync:
rsync_destroy();
#include "notify.h"
#include <err.h>
-#include "rtr/pdu_sender.h"
#include "clients.h"
-#include "vrps.h"
+#include "rtr/pdu_sender.h"
+#include "rtr/db/vrps.h"
static int
send_notify(int fd, uint8_t rtr_version)
#include "thread_var.h"
#include "asn1/decode.h"
#include "asn1/oid.h"
+#include "asn1/signed_data.h"
#include "crypto/hash.h"
#include "object/name.h"
#include "rsync/rsync.h"
+#include "rtr/db/roa_tree.h"
#include <sys/socket.h>
validate_issuer(X509 *cert, bool is_ta)
{
X509_NAME *issuer;
- struct rfc5280_name name;
+ struct rfc5280_name *name;
int error;
issuer = X509_get_issuer_name(cert);
error = x509_name_decode(issuer, "issuer", &name);
if (!error)
- x509_name_cleanup(&name);
+ x509_name_put(name);
return error;
}
static int
-validate_subject(X509 *cert)
+validate_subject(X509 *cert, struct rfc5280_name **subject_name)
{
struct validation *state;
- struct rfc5280_name name;
+ struct rfc5280_name *name;
int error;
state = state_retrieve();
if (error)
return error;
- error = validation_store_subject(state, &name);
- if (error)
- x509_name_cleanup(&name);
+ error = validation_store_subject(state, name);
+ if (error) {
+ x509_name_put(name);
+ return error;
+ }
- return error;
+ *subject_name = name; /* Transfer ownership */
+ return 0;
}
static int
}
int
-certificate_validate_rfc6487(X509 *cert, bool is_root)
+certificate_validate_rfc6487(X509 *cert, struct rfc5280_name **subject_name,
+ bool is_root)
{
int error;
* "An issuer SHOULD use a different subject name if the subject's
* key pair has changed" (it's a SHOULD, so [for now] avoid validation)
*/
- error = validate_subject(cert);
+ error = validate_subject(cert, subject_name);
if (error)
return error;
{
struct validation *state;
X509 *cert;
+ struct rfc5280_name *subject_name;
struct rpki_uri mft;
struct certificate_refs refs;
enum rpki_policy policy;
/* -- Validate the certificate (@cert) -- */
error = certificate_load(cert_uri, &cert);
if (error)
- goto end1;
+ goto revert_fnstack_and_debug;
if (!is_ta) {
error = certificate_validate_chain(cert, crls);
if (error)
- goto end2;
+ goto revert_cert;
}
- error = certificate_validate_rfc6487(cert, is_ta);
+ error = certificate_validate_rfc6487(cert, &subject_name, is_ta);
if (error)
- goto end2;
+ goto revert_cert;
error = is_ta
? certificate_validate_extensions_ta(cert, &mft, &policy)
: certificate_validate_extensions_ca(cert, &mft, &refs, &policy);
if (error)
- goto end2;
+ goto revert_subject_name;
error = refs_validate_ca(&refs, is_ta, rpp_parent);
if (error)
- goto end3;
+ goto revert_uri_and_refs;
/* -- Validate the manifest (@mft) pointed by the certificate -- */
error = validation_push_cert(state, cert_uri, cert, policy, is_ta);
if (error)
- goto end3;
+ goto revert_uri_and_refs;
error = handle_manifest(&mft, crls, &pp);
if (error)
- goto end4;
+ goto revert_cert_push;
/* -- Validate & traverse the RPP (@pp) described by the manifest -- */
+ error = vhandler_traverse_down(subject_name);
+ if (error)
+ goto revert_rpp;
+
error = rpp_traverse(pp);
+ if (error)
+ goto revert_rpp;
+
+ error = vhandler_traverse_up();
+revert_rpp:
rpp_destroy(pp);
-end4:
+revert_cert_push:
validation_pop_cert(state); /* Error code is useless. */
-end3:
+revert_uri_and_refs:
uri_cleanup(&mft);
refs_cleanup(&refs);
-end2:
+revert_subject_name:
+ x509_name_put(subject_name);
+revert_cert:
X509_free(cert);
-end1:
+revert_fnstack_and_debug:
fnstack_pop();
pr_debug_rm("}");
return error;
#include "resource.h"
#include "rpp.h"
#include "uri.h"
+#include "object/name.h"
int certificate_load(struct rpki_uri const *, X509 **);
* Validates RFC 6487 compliance.
* (Except extensions.)
*/
-int certificate_validate_rfc6487(X509 *, bool);
+int certificate_validate_rfc6487(X509 *, struct rfc5280_name **, bool);
int certificate_validate_signature(X509 *, ANY_t *coded, SignatureValue_t *);
#include "log.h"
#include "thread_var.h"
+/**
+ * It's an RFC5280 name, but from RFC 6487's perspective.
+ * Meaning, only commonName and serialNumbers are allowed, and the latter is
+ * optional.
+ */
+struct rfc5280_name {
+ char *commonName;
+ char *serialNumber;
+ /** Reference counter */
+ unsigned int references;
+};
+
static int
name2string(X509_NAME_ENTRY *name, char **_result)
{
int
x509_name_decode(X509_NAME *name, char const *what,
- struct rfc5280_name *result)
+ struct rfc5280_name **_result)
{
+ struct rfc5280_name *result;
int i;
X509_NAME_ENTRY *entry;
int nid;
int error;
+ result = malloc(sizeof(struct rfc5280_name));
+ if (result == NULL)
+ return pr_enomem();
+
result->commonName = NULL;
result->serialNumber = NULL;
+ result->references = 1;
for (i = 0; i < X509_NAME_entry_count(name); i++) {
entry = X509_NAME_get_entry(name, i);
goto fail;
}
+ *_result = result;
return 0;
fail:
- x509_name_cleanup(result);
+ x509_name_put(result);
return error;
}
void
-x509_name_cleanup(struct rfc5280_name *name)
+x509_name_get(struct rfc5280_name *name)
+{
+ name->references++;
+}
+
+void
+x509_name_put(struct rfc5280_name *name)
+{
+ name->references--;
+ if (name->references == 0) {
+ free(name->commonName);
+ free(name->serialNumber);
+ free(name);
+ }
+}
+
+char const *
+x509_name_commonName(struct rfc5280_name *name)
{
- free(name->commonName);
- free(name->serialNumber);
+ return name->commonName;
+}
+
+char const *
+x509_name_serialNumber(struct rfc5280_name *name)
+{
+ return name->serialNumber;
}
/**
{
struct validation *state;
X509 *parent;
- struct rfc5280_name parent_subject;
- struct rfc5280_name child_issuer;
+ struct rfc5280_name *parent_subject;
+ struct rfc5280_name *child_issuer;
int error;
/*
if (error)
goto end;
- if (!x509_name_equals(&parent_subject, &child_issuer)) {
+ if (!x509_name_equals(parent_subject, child_issuer)) {
+ char const *parent_serial;
+ char const *child_serial;
+
+ parent_serial = x509_name_serialNumber(parent_subject);
+ child_serial = x509_name_serialNumber(child_issuer);
+
error = pr_err("%s's issuer name ('%s%s%s') does not equal issuer certificate's name ('%s%s%s').",
container,
- parent_subject.commonName,
- (parent_subject.serialNumber != NULL) ? "/" : "",
- (parent_subject.serialNumber != NULL)
- ? parent_subject.serialNumber
- : "",
- child_issuer.commonName,
- (child_issuer.serialNumber != NULL) ? "/" : "",
- (child_issuer.serialNumber != NULL)
- ? child_issuer.serialNumber
- : "");
+ x509_name_commonName(parent_subject),
+ (parent_serial != NULL) ? "/" : "",
+ (parent_serial != NULL) ? parent_serial : "",
+ x509_name_commonName(child_issuer),
+ (child_serial != NULL) ? "/" : "",
+ (child_serial != NULL) ? child_serial : "");
}
- x509_name_cleanup(&child_issuer);
-end: x509_name_cleanup(&parent_subject);
+ x509_name_put(child_issuer);
+end: x509_name_put(parent_subject);
return error;
}
#include <stdbool.h>
#include <openssl/x509.h>
-/**
- * It's an RFC5280 name, but from RFC 6487's perspective.
- * Meaning, only commonName and serialNumbers are allowed, and the latter is
- * optional.
- */
-struct rfc5280_name {
- char *commonName;
- char *serialNumber;
-};
+struct rfc5280_name;
-int x509_name_decode(X509_NAME *, char const *, struct rfc5280_name *);
-void x509_name_cleanup(struct rfc5280_name *);
+/* Constructor */
+int x509_name_decode(X509_NAME *, char const *, struct rfc5280_name **);
+/* Reference counting */
+void x509_name_get(struct rfc5280_name *);
+void x509_name_put(struct rfc5280_name *);
+
+/* Getters */
+char const *x509_name_commonName(struct rfc5280_name *);
+char const *x509_name_serialNumber(struct rfc5280_name *);
bool x509_name_equals(struct rfc5280_name *, struct rfc5280_name *);
#include <errno.h>
#include <arpa/inet.h>
+#include <sys/socket.h>
#include <libcmscodec/RouteOriginAttestation.h>
#include "config.h"
#include "asn1/decode.h"
#include "asn1/oid.h"
#include "object/signed_object.h"
-
-#include <sys/socket.h>
+#include "rtr/db/roa_tree.h"
static int
roa_decode(OCTET_STRING_t *string, void *arg)
}
static int
-print_addr4(struct resources *parent, unsigned long asn,
+____handle_roa_v4(struct resources *parent, unsigned long asn,
struct ROAIPAddress *roa_addr)
{
struct ipv4_prefix prefix;
v4addr2str(&prefix.addr), prefix.len);
}
- /* TODO I think we're not validating asn boundaries */
- return roa_handle_v4(asn, &prefix, max_length);
-}
-
-int
-roa_handle_v4(uint32_t asn, struct ipv4_prefix *prefix, uint8_t max_length)
-{
- return -ENOTIMPLEMENTED;
+ return vhandler_handle_roa_v4(asn, &prefix, max_length);
}
static int
-print_addr6(struct resources *parent, unsigned long asn,
+____handle_roa_v6(struct resources *parent, unsigned long asn,
struct ROAIPAddress *roa_addr)
{
struct ipv6_prefix prefix;
v6addr2str(&prefix.addr), prefix.len);
}
- return roa_handle_v6(asn, &prefix, max_length);
-}
-
-int
-roa_handle_v6(uint32_t asn, struct ipv6_prefix *prefix, uint8_t max_length)
-{
- return -ENOTIMPLEMENTED;
+ return vhandler_handle_roa_v6(asn, &prefix, max_length);
}
static int
-print_addr(struct resources *parent, ASID_t *as_id, uint8_t family,
+____handle_roa(struct resources *parent, ASID_t *as_id, uint8_t family,
struct ROAIPAddress *roa_addr)
{
unsigned long asn;
return pr_err("ROA's AS ID couldn't be parsed as unsigned long");
}
+ if (asn > UINT32_MAX)
+ return pr_err("AS value (%lu) is out of range.", asn);
+
switch (family) {
case 1: /* IPv4 */
- return print_addr4(parent, asn, roa_addr);
+ return ____handle_roa_v4(parent, asn, roa_addr);
case 2: /* IPv6 */
- return print_addr6(parent, asn, roa_addr);
+ return ____handle_roa_v6(parent, asn, roa_addr);
}
return pr_err("Unknown family value: %u", family);
if (block->addresses.list.array == NULL)
return pr_err("ROA's address list array is NULL.");
for (a = 0; a < block->addresses.list.count; a++) {
- error = print_addr(parent, &roa->asID,
+ error = ____handle_roa(parent, &roa->asID,
block->addressFamily.buf[1],
block->addresses.list.array[a]);
if (error)
if (error)
goto end2;
+ error = vhandler_traverse_down(sobj_args.subject_name);
+ if (error)
+ goto end3;
+
error = __handle_roa(roa, sobj_args.res);
if (error)
goto end3;
+ error = vhandler_traverse_up();
+ if (error)
+ goto end3;
+
+ /* TODO why is this happening so late? */
error = refs_validate_ee(&sobj_args.refs, pp, sobj_args.uri);
end3:
int roa_traverse(struct rpki_uri const *, struct rpp *, STACK_OF(X509_CRL) *);
-int roa_handle_v4(uint32_t, struct ipv4_prefix *, uint8_t);
-int roa_handle_v6(uint32_t, struct ipv6_prefix *, uint8_t);
-
#endif /* SRC_OBJECT_ROA_H_ */
}
int
-foreach_uri(struct tal *tal, foreach_uri_cb cb)
+foreach_uri(struct tal *tal, foreach_uri_cb cb, void *arg)
{
struct rpki_uri uri;
unsigned int i;
if (error)
return error;
- error = cb(tal, &uri);
+ error = cb(tal, &uri, arg);
uri_cleanup(&uri);
if (error)
return error;
* have been extracted from a TAL.
*/
static int
-handle_tal_uri(struct tal *tal, struct rpki_uri const *uri)
+handle_tal_uri(struct tal *tal, struct rpki_uri const *uri, void *arg)
{
/*
* Because of the way the foreach iterates, this function must return
uri->global);
}
- error = validation_prepare(&state, tal);
+ error = validation_prepare(&state, tal, arg);
+ if (error)
+ return ENSURE_NEGATIVE(error);
+
+ error = vhandler_reset(arg);
if (error)
return ENSURE_NEGATIVE(error);
return error;
}
-/* TODO set @updated */
int
-perform_standalone_validation(bool *updated)
+perform_standalone_validation(struct validation_handler *handler)
{
struct tal *tal;
int error;
if (config_get_shuffle_tal_uris())
tal_shuffle_uris(tal);
- error = foreach_uri(tal, handle_tal_uri);
+ error = foreach_uri(tal, handle_tal_uri, handler);
if (error > 0)
error = 0;
else if (error == 0)
#include <stddef.h>
#include "uri.h"
+#include "validation_handler.h"
struct tal;
int tal_load(char const *, struct tal **);
void tal_destroy(struct tal *);
-typedef int (*foreach_uri_cb)(struct tal *, struct rpki_uri const *);
-int foreach_uri(struct tal *, foreach_uri_cb);
+typedef int (*foreach_uri_cb)(struct tal *, struct rpki_uri const *, void *);
+int foreach_uri(struct tal *, foreach_uri_cb, void *);
void tal_shuffle_uris(struct tal *);
char const *tal_get_file_name(struct tal *);
void tal_get_spki(struct tal *, unsigned char const **, size_t *);
-int perform_standalone_validation(bool *);
+int perform_standalone_validation(struct validation_handler *);
#endif /* TAL_OBJECT_H_ */
#include "rpp.h"
#include <stdlib.h>
-#include "array_list.h"
#include "log.h"
#include "thread_var.h"
#include "uri.h"
+#include "data_structure/array_list.h"
#include "object/certificate.h"
#include "object/crl.h"
#include "object/ghostbusters.h"
--- /dev/null
+#include "rtr/db/delta.h"
+
+#include "data_structure/array_list.h"
+
+struct delta_v4 {
+ uint32_t as;
+ struct ipv4_prefix prefix;
+ uint8_t max_length;
+};
+
+struct delta_v6 {
+ uint32_t as;
+ struct ipv6_prefix prefix;
+ uint8_t max_length;
+};
+
+ARRAY_LIST(deltas_v6, struct delta_v6)
+ARRAY_LIST(deltas_v4, struct delta_v4)
+
+struct deltas {
+ struct {
+ struct deltas_v4 adds;
+ struct deltas_v4 removes;
+ } v4;
+ struct {
+ struct deltas_v6 adds;
+ struct deltas_v6 removes;
+ } v6;
+};
+
+int
+deltas_create(struct deltas **_result)
+{
+ struct deltas *result;
+ int error;
+
+ result = malloc(sizeof(struct deltas));
+ if (result == NULL)
+ return pr_enomem();
+
+ error = deltas_v4_init(&result->v4.adds);
+ if (error)
+ goto revert_result;
+ error = deltas_v4_init(&result->v4.removes);
+ if (error)
+ goto revert_v4_adds;
+ error = deltas_v6_init(&result->v6.adds);
+ if (error)
+ goto revert_v4_removes;
+ error = deltas_v6_init(&result->v6.removes);
+ if (error)
+ goto revert_v6_adds;
+
+ *_result = result;
+ return 0;
+
+revert_v6_adds:
+ deltas_v6_cleanup(&result->v6.adds, NULL);
+revert_v4_removes:
+ deltas_v4_cleanup(&result->v4.removes, NULL);
+revert_v4_adds:
+ deltas_v4_cleanup(&result->v4.adds, NULL);
+revert_result:
+ free(result);
+ return error;
+}
+
+void
+deltas_destroy(struct deltas *deltas)
+{
+ deltas_v4_cleanup(&deltas->v4.adds, NULL);
+ deltas_v4_cleanup(&deltas->v4.removes, NULL);
+ deltas_v6_cleanup(&deltas->v6.adds, NULL);
+ deltas_v6_cleanup(&deltas->v6.removes, NULL);
+ free(deltas);
+}
+
+int
+deltas_add_roa_v4(struct deltas *deltas, uint32_t as, struct v4_address *addr,
+ enum delta_op op)
+{
+ struct delta_v4 delta = {
+ .as = as,
+ .prefix = addr->prefix,
+ .max_length = addr->max_length,
+ };
+
+ switch (op) {
+ case DELTA_ADD:
+ return deltas_v4_add(&deltas->v4.adds, &delta);
+ case DELTA_RM:
+ return deltas_v4_add(&deltas->v4.removes, &delta);
+ }
+
+ return pr_crit("Unknown delta operation: %u", op);
+}
+
+int
+deltas_add_roa_v6(struct deltas *deltas, uint32_t as, struct v6_address *addr,
+ enum delta_op op)
+{
+ struct delta_v6 delta = {
+ .as = as,
+ .prefix = addr->prefix,
+ .max_length = addr->max_length,
+ };
+
+ switch (op) {
+ case DELTA_ADD:
+ return deltas_v6_add(&deltas->v6.adds, &delta);
+ case DELTA_RM:
+ return deltas_v6_add(&deltas->v6.removes, &delta);
+ }
+
+ return pr_crit("Unknown delta operation: %u", op);
+}
+
+bool
+deltas_is_empty(struct deltas *deltas)
+{
+ return (deltas->v4.adds.len == 0)
+ && (deltas->v4.removes.len == 0)
+ && (deltas->v6.adds.len == 0)
+ && (deltas->v6.removes.len == 0);
+}
+
+static int
+__foreach_v4(struct deltas_v4 *array, vrp_foreach_cb cb, void *arg,
+ uint8_t flags)
+{
+ struct delta_v4 *d;
+ struct vrp vrp;
+ int error;
+
+ vrp.addr_fam = AF_INET;
+ vrp.flags = flags;
+
+ ARRAYLIST_FOREACH(array, d) {
+ vrp.asn = d->as;
+ vrp.prefix.v4 = d->prefix.addr;
+ vrp.prefix_length = d->prefix.len;
+ vrp.max_prefix_length = d->max_length;
+ error = cb(&vrp, arg);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+static int
+__foreach_v6(struct deltas_v6 *array, vrp_foreach_cb cb, void *arg,
+ uint8_t flags)
+{
+ struct delta_v6 *d;
+ struct vrp vrp;
+ int error;
+
+ vrp.addr_fam = AF_INET6;
+ vrp.flags = flags;
+
+ ARRAYLIST_FOREACH(array, d) {
+ vrp.asn = d->as;
+ vrp.prefix.v6 = d->prefix.addr;
+ vrp.prefix_length = d->prefix.len;
+ vrp.max_prefix_length = d->max_length;
+ error = cb(&vrp, arg);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+int
+deltas_foreach(struct deltas *deltas, vrp_foreach_cb cb, void *arg)
+{
+ int error;
+
+ error = __foreach_v4(&deltas->v4.adds, cb, arg, FLAG_ANNOUNCEMENT);
+ if (error)
+ return error;
+ error = __foreach_v4(&deltas->v4.removes, cb, arg, FLAG_WITHDRAWAL);
+ if (error)
+ return error;
+
+ error = __foreach_v6(&deltas->v6.adds, cb, arg, FLAG_ANNOUNCEMENT);
+ if (error)
+ return error;
+ return __foreach_v6(&deltas->v6.removes, cb, arg, FLAG_WITHDRAWAL);
+}
--- /dev/null
+#ifndef SRC_DELTA_H_
+#define SRC_DELTA_H_
+
+#include "rtr/db/roa.h"
+#include "rtr/db/vrp.h"
+
+enum delta_op {
+ DELTA_ADD,
+ DELTA_RM,
+};
+
+struct deltas;
+
+int deltas_create(struct deltas **);
+void deltas_destroy(struct deltas *);
+
+int deltas_add_roa_v4(struct deltas *, uint32_t, struct v4_address *,
+ enum delta_op);
+int deltas_add_roa_v6(struct deltas *, uint32_t, struct v6_address *,
+ enum delta_op);
+
+bool deltas_is_empty(struct deltas *);
+int deltas_foreach(struct deltas *, vrp_foreach_cb , void *);
+
+#endif /* SRC_DELTA_H_ */
--- /dev/null
+#include "rtr/db/roa.h"
+
+DEFINE_ARRAY_LIST_FUNCTIONS(v4_addresses, struct v4_address)
+DEFINE_ARRAY_LIST_FUNCTIONS(v6_addresses, struct v6_address)
+
+static void
+v4_address_destroy(struct v4_address *addr)
+{
+ free(addr);
+}
+
+static void
+v6_address_destroy(struct v6_address *addr)
+{
+ free(addr);
+}
+
+int
+roa_create(u_int32_t as, struct roa **_result)
+{
+ struct roa *result;
+ int error;
+
+ result = malloc(sizeof(struct roa));
+ if (result == NULL)
+ return pr_enomem();
+
+ result->as = as;
+ error = v4_addresses_init(&result->addrs4);
+ if (error)
+ goto revert_result;
+ error = v6_addresses_init(&result->addrs6);
+ if (error)
+ goto revert_addrs4;
+
+ *_result = result;
+ return 0;
+
+revert_addrs4:
+ v4_addresses_cleanup(&result->addrs4, v4_address_destroy);
+revert_result:
+ free(result);
+ return error;
+}
+
+void
+roa_destroy(struct roa *roa)
+{
+ v4_addresses_cleanup(&roa->addrs4, v4_address_destroy);
+ v6_addresses_cleanup(&roa->addrs6, v6_address_destroy);
+}
+
+int
+roa_add_v4(struct roa *roa, uint32_t as, struct ipv4_prefix const *prefix,
+ uint8_t max_length)
+{
+ struct v4_address addr;
+
+ if (roa->as != as) {
+ return pr_err("ROA has more than one ASN. (%u and %u)",
+ roa->as, as);
+ }
+
+ addr.prefix = *prefix;
+ addr.max_length = max_length;
+ return v4_addresses_add(&roa->addrs4, &addr);
+}
+
+int
+roa_add_v6(struct roa *roa, uint32_t as, struct ipv6_prefix const *prefix,
+ uint8_t max_length)
+{
+ struct v6_address addr;
+
+ if (roa->as != as) {
+ return pr_err("ROA has more than one ASN. (%u and %u)",
+ roa->as, as);
+ }
+
+ addr.prefix = *prefix;
+ addr.max_length = max_length;
+ return v6_addresses_add(&roa->addrs6, &addr);
+}
--- /dev/null
+#ifndef SRC_RTR_DB_ROA_H_
+#define SRC_RTR_DB_ROA_H_
+
+#include "address.h"
+#include "data_structure/array_list.h"
+
+struct v4_address {
+ struct ipv4_prefix prefix;
+ uint8_t max_length;
+};
+
+struct v6_address {
+ struct ipv6_prefix prefix;
+ uint8_t max_length;
+};
+
+DEFINE_ARRAY_LIST_STRUCT(v4_addresses, struct v4_address);
+DEFINE_ARRAY_LIST_STRUCT(v6_addresses, struct v6_address);
+
+struct roa {
+ uint32_t as;
+ struct v4_addresses addrs4;
+ struct v6_addresses addrs6;
+};
+
+int roa_create(u_int32_t, struct roa **);
+void roa_destroy(struct roa *);
+
+int roa_add_v4(struct roa *, uint32_t, struct ipv4_prefix const *, uint8_t);
+int roa_add_v6(struct roa *, uint32_t, struct ipv6_prefix const *, uint8_t);
+
+#endif /* SRC_RTR_DB_ROA_H_ */
--- /dev/null
+#include "rtr/db/roa_tree.h"
+
+#include "common.h"
+#include "data_structure/array_list.h"
+#include "data_structure/circular_indexer.h"
+#include "rtr/db/roa.h"
+
+DEFINE_ARRAY_LIST_STRUCT(nodes, struct node);
+
+struct node {
+ struct rfc5280_name *subject_name;
+ struct node *parent;
+ /*
+ * BTW: There's nothing in this code stopping both children and roa from
+ * being not null, but it should never happen naturally.
+ */
+ struct nodes children;
+ struct roa *roa;
+};
+
+struct roa_tree {
+ struct node *root;
+ struct node *current;
+ unsigned int references;
+};
+
+DEFINE_ARRAY_LIST_FUNCTIONS(nodes, struct node)
+
+static struct node *
+node_create(struct rfc5280_name *subject_name, struct node *parent)
+{
+ struct node *node;
+
+ node = malloc(sizeof(struct node));
+ if (node == NULL)
+ return NULL;
+
+ node->subject_name = subject_name;
+ x509_name_get(subject_name);
+ node->parent = parent;
+ node->children.array = NULL;
+ node->roa = NULL;
+ return node;
+}
+
+static void
+node_destroy(struct node *node)
+{
+ x509_name_put(node->subject_name);
+ if (node->children.array != NULL)
+ nodes_cleanup(&node->children, node_destroy);
+ if (node->roa != NULL)
+ roa_destroy(node->roa);
+ free(node);
+}
+
+static int
+node_add_child(struct node *parent, struct rfc5280_name *subject_name)
+{
+ struct node *child;
+ int error;
+
+ if (parent->children.array == NULL) {
+ error = nodes_init(&parent->children);
+ if (error)
+ return error;
+ }
+
+ child = node_create(subject_name, parent);
+ if (child == NULL)
+ return pr_enomem();
+
+ error = nodes_add(&parent->children, child);
+ if (error)
+ node_destroy(child);
+
+ return error;
+}
+
+/**
+ * Performs lazy initialization if the root does not exist.
+ */
+static struct node *
+get_root(struct roa_tree *tree, struct rfc5280_name *subject_name)
+{
+ if (tree->root == NULL)
+ tree->root = node_create(subject_name, NULL);
+ return tree->root;
+}
+
+struct roa_tree *
+roa_tree_create(void)
+{
+ struct roa_tree *tree;
+
+ tree = malloc(sizeof(struct roa_tree));
+ if (tree == NULL)
+ return NULL;
+
+ tree->root = NULL;
+ tree->current = NULL;
+ tree->references = 1;
+ return tree;
+}
+
+static void
+roa_tree_cleanup(struct roa_tree *tree)
+{
+ if (tree->root != NULL) {
+ node_destroy(tree->root);
+ tree->root = NULL;
+ }
+ tree->current = NULL;
+}
+
+void
+roa_tree_get(struct roa_tree *tree)
+{
+ tree->references++;
+}
+
+void
+roa_tree_put(struct roa_tree *tree)
+{
+ tree->references--;
+ if (tree->references == 0) {
+ roa_tree_cleanup(tree);
+ free(tree);
+ }
+}
+
+static int
+__foreach_v4(struct roa *roa, vrp_foreach_cb cb, void *arg)
+{
+ struct v4_address *addr;
+ struct vrp vrp;
+ int error;
+
+ vrp.asn = roa->as;
+ vrp.addr_fam = AF_INET;
+ vrp.flags = FLAG_ANNOUNCEMENT;
+
+ ARRAYLIST_FOREACH(&roa->addrs4, addr) {
+ vrp.prefix.v4 = addr->prefix.addr;
+ vrp.prefix_length = addr->prefix.len;
+ vrp.max_prefix_length = addr->max_length;
+ error = cb(&vrp, arg);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+static int
+__foreach_v6(struct roa *roa, vrp_foreach_cb cb, void *arg)
+{
+ struct v6_address *addr;
+ struct vrp vrp;
+ int error;
+
+ vrp.asn = roa->as;
+ vrp.addr_fam = AF_INET6;
+ vrp.flags = FLAG_ANNOUNCEMENT;
+
+ ARRAYLIST_FOREACH(&roa->addrs6, addr) {
+ vrp.prefix.v6 = addr->prefix.addr;
+ vrp.prefix_length = addr->prefix.len;
+ vrp.max_prefix_length = addr->max_length;
+ error = cb(&vrp, arg);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+int
+__foreach(struct node *node, vrp_foreach_cb cb, void *arg)
+{
+ struct node *child;
+ int error;
+
+ ARRAYLIST_FOREACH(&node->children, child) {
+ error = __foreach(child, cb, arg);
+ if (error)
+ return error;
+ }
+
+ if (child->roa != NULL) {
+ error = __foreach_v4(child->roa, cb, arg);
+ if (error)
+ return error;
+ error = __foreach_v6(child->roa, cb, arg);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+int
+roa_tree_foreach_roa(struct roa_tree *tree, vrp_foreach_cb cb, void *arg)
+{
+ return (tree->root != NULL) ? __foreach(tree->root, cb, arg) : 0;
+}
+
+int
+forthandler_reset(struct roa_tree *tree)
+{
+ roa_tree_cleanup(tree);
+ return 0;
+}
+
+int
+forthandler_go_down(struct roa_tree *tree,
+ struct rfc5280_name *subject_name)
+{
+ if (tree->current != NULL)
+ return node_add_child(tree->current, subject_name);
+
+ tree->current = get_root(tree, subject_name);
+ return (tree->current != NULL) ? 0 : pr_enomem();
+}
+
+int
+forthandler_go_up(struct roa_tree *tree)
+{
+ if (tree->current == NULL)
+ return pr_crit("Bad tree traversal: Attempting to move to the parent of a root.");
+
+ tree->current = tree->current->parent;
+ return 0;
+}
+
+static int
+get_current_roa(struct roa_tree *tree, uint32_t asn, struct roa **result)
+{
+ struct roa *roa;
+ int error;
+
+ if (tree->current == NULL)
+ return pr_crit("Validator posted ROA during incorrect context.");
+
+ roa = tree->current->roa;
+ if (roa == NULL) {
+ error = roa_create(asn, &roa);
+ if (error)
+ return error;
+ tree->current->roa = roa;
+ }
+
+ *result = roa;
+ return 0;
+}
+
+int
+forthandler_handle_roa_v4(struct roa_tree *tree, uint32_t asn,
+ struct ipv4_prefix const *prefix4, uint8_t max_length)
+{
+ struct roa *roa;
+ int error;
+ error = get_current_roa(tree, asn, &roa);
+ return error ? error : roa_add_v4(roa, asn, prefix4, max_length);
+}
+
+int
+forthandler_handle_roa_v6(struct roa_tree *tree, uint32_t asn,
+ struct ipv6_prefix const *prefix6, uint8_t max_length)
+{
+ struct roa *roa;
+ int error;
+ error = get_current_roa(tree, asn, &roa);
+ return error ? error : roa_add_v6(roa, asn, prefix6, max_length);
+}
+
+static bool
+find_subject_name(struct circular_indexer *indexer,
+ struct rfc5280_name *subject_name, struct node *c2array,
+ array_index *result)
+{
+ array_index *i;
+
+ ARRIDX_FOREACH(indexer, i) {
+ if (x509_name_equals(subject_name, c2array[*i].subject_name)) {
+ *result = *i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int
+add_all_roas_v4(struct deltas *deltas, struct roa *roa, enum delta_op op)
+{
+ struct v4_address *addr;
+ int error;
+
+ ARRAYLIST_FOREACH(&roa->addrs4, addr) {
+ error = deltas_add_roa_v4(deltas, roa->as, addr, op);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+static int
+add_all_roas_v6(struct deltas *deltas, struct roa *roa, enum delta_op op)
+{
+ struct v6_address *addr;
+ int error;
+
+ ARRAYLIST_FOREACH(&roa->addrs6, addr) {
+ error = deltas_add_roa_v6(deltas, roa->as, addr, op);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+static int
+add_all_deltas(struct node *node, struct deltas *deltas, enum delta_op op)
+{
+ struct node *child;
+ int error;
+
+ ARRAYLIST_FOREACH(&node->children, child) {
+ error = add_all_deltas(child, deltas, op);
+ if (error)
+ return error;
+ }
+
+ if (child->roa != NULL) {
+ error = add_all_roas_v4(deltas, child->roa, op);
+ if (error)
+ return error;
+ error = add_all_roas_v6(deltas, child->roa, op);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+static int compute_deltas_node(struct node *, struct node *, struct deltas *);
+
+static int
+handle_delta_children(struct nodes *children1, struct nodes *children2,
+ struct deltas *deltas)
+{
+ /*
+ * Most of the time, the arrays will be identical.
+ * When they are not, most of the time the arrays will be mostly
+ * identical.
+ *
+ * We will try our hardest to traverse the arrays as sequentially as
+ * possible to exploit these facts.
+ *
+ * Notice that this is the same algorithm as HANDLE_ROAS_FN().
+ * Changes to one function might need to cascade to the other.
+ */
+
+ struct node *c1array;
+ array_index c1; /* counter for c1array */
+
+ struct node *c2array;
+ array_index c2; /* counter for c2array */
+ array_index *c2p; /* counter for c2array, pointer */
+
+ struct circular_indexer c2indexer;
+
+ int error = 0;
+
+ c1array = children1->array;
+ c2array = children2->array;
+ arridx_init(&c2indexer, children2->len);
+
+ for (c1 = 0; c1 < children1->len; c1++) {
+ if (find_subject_name(&c2indexer, c1array[c1].subject_name,
+ c2array, &c2)) {
+ error = compute_deltas_node(&c1array[c1], &c2array[c2],
+ deltas);
+ if (error)
+ goto end;
+
+ error = arridx_remove(&c2indexer);
+ } else {
+ error = add_all_deltas(&c1array[c1], deltas, DELTA_RM);
+ }
+ if (error)
+ goto end;
+ }
+
+ ARRIDX_FOREACH(&c2indexer, c2p) {
+ error = add_all_deltas(&c2array[*c2p], deltas, DELTA_ADD);
+ if (error)
+ goto end;
+ }
+
+end: arridx_cleanup(&c2indexer);
+ return error;
+}
+
+static bool
+find_addr_v4(struct v4_address *address, struct v4_addresses *array,
+ struct circular_indexer *indexer)
+{
+ array_index *i;
+
+ ARRIDX_FOREACH(indexer, i)
+ if (prefix4_equals(&address->prefix, &array->array[*i].prefix))
+ return true;
+
+ return false;
+}
+
+static bool
+find_addr_v6(struct v6_address *address, struct v6_addresses *array,
+ struct circular_indexer *indexer)
+{
+ array_index *i;
+
+ ARRIDX_FOREACH(indexer, i)
+ if (prefix6_equals(&address->prefix, &array->array[*i].prefix))
+ return true;
+
+ return false;
+}
+
+#define HANDLE_ROAS_FN(name, array_type, node_type, field, find_fn, \
+ add_one_fn, add_all_fn) \
+ static int \
+ name(struct roa *roa1, struct roa *roa2, struct deltas *deltas) \
+ { \
+ /* Notice that this is the same algorithm as */ \
+ /* handle_delta_children(). Changes to one function */ \
+ /* might need to cascade to the other. */ \
+ \
+ struct array_type *addrs1; \
+ struct node_type *a1; /* address cursor for addrs1 */ \
+ \
+ struct array_type *addrs2; \
+ array_index *a2p; /* counter for addrs2, pointer */ \
+ \
+ struct circular_indexer r2indexer; \
+ int error = 0; \
+ \
+ if (roa1->as != roa2->as) { \
+ error = add_all_fn(deltas, roa1, DELTA_RM); \
+ if (error) \
+ return error; \
+ return add_all_fn(deltas, roa2, DELTA_ADD); \
+ } \
+ \
+ addrs1 = &roa1->field; \
+ addrs2 = &roa2->field; \
+ arridx_init(&r2indexer, addrs2->len); \
+ \
+ ARRAYLIST_FOREACH(addrs1, a1) { \
+ if (find_fn(a1, addrs2, &r2indexer)) \
+ error = arridx_remove(&r2indexer); \
+ else \
+ error = add_one_fn(deltas, \
+ roa1->as, a1, DELTA_RM); \
+ if (error) \
+ goto end; \
+ } \
+ \
+ ARRIDX_FOREACH(&r2indexer, a2p) { \
+ error = add_one_fn(deltas, roa2->as, \
+ &addrs2->array[*a2p], DELTA_ADD); \
+ if (error) \
+ goto end; \
+ } \
+ \
+ end: arridx_cleanup(&r2indexer); \
+ return error; \
+ }
+
+HANDLE_ROAS_FN(handle_roas_v4, v4_addresses, v4_address, addrs4, find_addr_v4,
+ deltas_add_roa_v4, add_all_roas_v4)
+HANDLE_ROAS_FN(handle_roas_v6, v6_addresses, v6_address, addrs6, find_addr_v6,
+ deltas_add_roa_v6, add_all_roas_v6)
+
+static int
+compute_deltas_node(struct node *n1, struct node *n2, struct deltas *deltas)
+{
+ int error;
+
+ error = handle_delta_children(&n1->children, &n2->children, deltas);
+ if (error)
+ return error;
+
+ error = handle_roas_v4(n1->roa, n2->roa, deltas);
+ if (error)
+ return error;
+
+ return handle_roas_v6(n1->roa, n2->roa, deltas);
+}
+
+int
+compute_deltas(struct roa_tree *t1, struct roa_tree *t2, struct deltas **result)
+{
+ struct deltas *deltas;
+ int error;
+
+ assert(t1->root != NULL);
+ assert(t2->root != NULL);
+
+ error = deltas_create(&deltas);
+ if (error)
+ return error;
+
+ error = compute_deltas_node(t1->root, t2->root, deltas);
+ if (error) {
+ deltas_destroy(deltas);
+ return error;
+ }
+
+ *result = deltas;
+ return 0;
+}
--- /dev/null
+#ifndef SRC_ROA_TREE_H_
+#define SRC_ROA_TREE_H_
+
+#include "address.h"
+#include "object/name.h"
+#include "rtr/db/delta.h"
+#include "rtr/db/vrp.h"
+
+struct roa_tree;
+
+/* Constructor */
+struct roa_tree *roa_tree_create(void);
+/* Reference counting */
+void roa_tree_get(struct roa_tree *);
+void roa_tree_put(struct roa_tree *);
+
+int roa_tree_foreach_roa(struct roa_tree *, vrp_foreach_cb, void *);
+
+/* TODO rename to tree handler or whatever */
+int forthandler_reset(struct roa_tree *);
+int forthandler_go_down(struct roa_tree *, struct rfc5280_name *);
+int forthandler_go_up(struct roa_tree *);
+int forthandler_handle_roa_v4(struct roa_tree *, uint32_t,
+ struct ipv4_prefix const *, uint8_t);
+int forthandler_handle_roa_v6(struct roa_tree *, uint32_t,
+ struct ipv6_prefix const *, uint8_t);
+
+int compute_deltas(struct roa_tree *, struct roa_tree *, struct deltas **);
+
+#endif /* SRC_ROA_TREE_H_ */
--- /dev/null
+#ifndef SRC_RTR_DB_VRP_H_
+#define SRC_RTR_DB_VRP_H_
+
+#define FLAG_WITHDRAWAL 0
+#define FLAG_ANNOUNCEMENT 1
+
+struct vrp {
+ uint32_t asn;
+ union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ } prefix;
+ uint8_t prefix_length;
+ uint8_t max_prefix_length;
+ uint8_t addr_fam;
+ uint8_t flags;
+};
+
+typedef int (*vrp_foreach_cb)(struct vrp *, void *);
+
+#endif /* SRC_RTR_DB_VRP_H_ */
--- /dev/null
+#include "vrps.h"
+
+#include <stdbool.h>
+#include <string.h>
+#include "common.h"
+#include "data_structure/array_list.h"
+
+/*
+ * Storage of VRPs (term taken from RFC 6811 "Validated ROA Payload") and
+ * Serials that contain such VRPs
+ */
+
+#define START_SERIAL 0
+
+struct delta {
+ uint32_t serial;
+ struct deltas *deltas;
+};
+
+ARRAY_LIST(deltas_db, struct delta)
+
+struct state {
+ struct roa_tree *base; /** All the current valid ROAs */
+ struct deltas_db deltas; /** ROA changes to @base over time */
+
+ uint32_t current_serial;
+ uint16_t v0_session_id;
+ uint16_t v1_session_id;
+ time_t last_modified_date;
+} state;
+
+/* Read and Write locks */
+static sem_t rlock, wlock;
+
+/* Readers counter */
+static unsigned int rcounter;
+
+static void
+delta_destroy(struct delta *delta)
+{
+ deltas_destroy(delta->deltas);
+}
+
+int
+vrps_init(void)
+{
+ int error;
+
+ state.base = NULL;
+
+ error = deltas_db_init(&state.deltas);
+ if (error)
+ return error;
+
+ /*
+ * Use the same start serial, the session ID will avoid
+ * "desynchronization" (more at RFC 6810 'Glossary' and
+ * 'Fields of a PDU')
+ */
+ state.current_serial = START_SERIAL;
+
+ /* Get the bits that'll fit in session_id */
+ state.v0_session_id = time(NULL) & 0xFFFF;
+ /* Minus 1 to prevent same ID */
+ /*
+ * TODO Assigning an int (and potentially negative) to a uint16_t.
+ * Is this legal?
+ */
+ state.v1_session_id = state.v0_session_id - 1;
+
+ sem_init(&rlock, 0, 1);
+ sem_init(&wlock, 0, 1);
+ rcounter = 0;
+
+ return 0;
+}
+
+void
+vrps_destroy(void)
+{
+ sem_wait(&wlock);
+ roa_tree_put(state.base);
+ deltas_db_cleanup(&state.deltas, delta_destroy);
+ sem_post(&wlock);
+
+ sem_destroy(&wlock);
+ sem_destroy(&rlock);
+}
+
+/*
+ * @new_deltas can be NULL, @new_tree cannot.
+ */
+int
+vrps_update(struct roa_tree *new_tree, struct deltas *new_deltas)
+{
+ struct delta new_delta;
+ int error;
+
+ sem_wait(&wlock);
+
+ if (new_deltas != NULL) {
+ new_delta.serial = state.current_serial + 1;
+ new_delta.deltas = new_deltas;
+ error = deltas_db_add(&state.deltas, &new_delta);
+ if (error) {
+ sem_post(&wlock);
+ return error;
+ }
+ }
+
+ if (state.base != NULL)
+ roa_tree_put(state.base);
+ state.base = new_tree;
+ roa_tree_get(new_tree);
+ state.current_serial++;
+
+ sem_post(&wlock);
+ return 0;
+}
+
+/*
+ * Get a status to know the difference between the delta with serial SERIAL and
+ * the last delta at DB.
+ *
+ * If SERIAL is received as NULL, and there's data at DB then the status will
+ * be DIFF_AVAILABLE.
+ */
+enum delta_status
+deltas_db_status(uint32_t *serial)
+{
+ struct delta *delta;
+ enum delta_status result;
+
+ read_lock(&rlock, &wlock, &rcounter);
+ if (state.base == NULL) {
+ result = DS_NO_DATA_AVAILABLE;
+ goto end;
+ }
+
+ /* No serial to match, and there's data at DB */
+ if (serial == NULL) {
+ result = DS_DIFF_AVAILABLE;
+ goto end;
+ }
+
+ /* Is the last version? */
+ if (*serial == state.current_serial) {
+ result = DS_NO_DIFF;
+ goto end;
+ }
+
+ /* Get the delta corresponding to the serial */
+ ARRAYLIST_FOREACH(&state.deltas, delta)
+ if (delta->serial == *serial) {
+ result = DS_DIFF_AVAILABLE;
+ goto end;
+ }
+
+ /* No match yet, release lock */
+ read_unlock(&rlock, &wlock, &rcounter);
+
+ /* The first serial isn't at deltas */
+ if (*serial == START_SERIAL)
+ return DS_DIFF_AVAILABLE;
+
+ /* Reached end, diff can't be determined */
+ return DS_DIFF_UNDETERMINED;
+end:
+ read_unlock(&rlock, &wlock, &rcounter);
+ return result;
+}
+
+int
+vrps_foreach_base_roa(vrp_foreach_cb cb, void *arg)
+{
+ int error;
+
+ read_lock(&rlock, &wlock, &rcounter);
+ error = roa_tree_foreach_roa(state.base, cb, arg);
+ read_unlock(&rlock, &wlock, &rcounter);
+
+ return error;
+}
+
+int
+vrps_foreach_delta_roa(uint32_t from, uint32_t to, vrp_foreach_cb cb, void *arg)
+{
+ struct delta *d;
+ bool from_found;
+ int error;
+
+ from_found = false;
+ error = 0;
+
+ read_lock(&rlock, &wlock, &rcounter);
+
+ ARRAYLIST_FOREACH(&state.deltas, d) {
+ if (!from_found) {
+ if (d->serial >= from)
+ from_found = true;
+ } else {
+ if (d->serial > to)
+ break;
+ error = deltas_foreach(d->deltas, cb, arg);
+ if (error)
+ break;
+ }
+ }
+
+ read_unlock(&rlock, &wlock, &rcounter);
+
+ return error;
+}
+
+uint32_t
+get_last_serial_number(void)
+{
+ uint32_t serial;
+
+ read_lock(&rlock, &wlock, &rcounter);
+ serial = state.current_serial - 1;
+ read_unlock(&rlock, &wlock, &rcounter);
+
+ return serial;
+}
+
+uint16_t
+get_current_session_id(uint8_t rtr_version)
+{
+ /* Semaphore isn't needed since this value is set at initialization */
+ if (rtr_version == 1)
+ return state.v1_session_id;
+ return state.v0_session_id;
+}
--- /dev/null
+#ifndef SRC_VRPS_H_
+#define SRC_VRPS_H_
+
+#include <time.h>
+#include <netinet/ip.h>
+#include "rtr/db/delta.h"
+#include "rtr/db/roa_tree.h"
+
+enum delta_status {
+ /** There's no data at the DB */
+ DS_NO_DATA_AVAILABLE,
+ /** The diff can't be determined */
+ DS_DIFF_UNDETERMINED,
+ /** There's no difference */
+ DS_NO_DIFF,
+ /** There are diffs between SERIAL and the last DB serial */
+ DS_DIFF_AVAILABLE,
+};
+
+int vrps_init(void);
+void vrps_destroy(void);
+
+int vrps_update(struct roa_tree *, struct deltas *);
+enum delta_status deltas_db_status(uint32_t *);
+
+int vrps_foreach_base_roa(vrp_foreach_cb, void *);
+int vrps_foreach_delta_roa(uint32_t, uint32_t, vrp_foreach_cb, void *);
+
+uint32_t get_last_serial_number(void);
+uint16_t get_current_session_id(uint8_t);
+
+#endif /* SRC_VRPS_H_ */
void (*destructor)(void *);
};
-__BEGIN_DECLS
int pdu_load(int, void **, struct pdu_metadata const **, uint8_t *);
struct pdu_metadata const *pdu_get_metadata(uint8_t);
struct pdu_header *pdu_get_header(void *);
-__END_DECLS
#endif /* RTR_PDU_H_ */
#include "err_pdu.h"
#include "pdu.h"
#include "pdu_sender.h"
-#include "vrps.h"
+#include "rtr/db/vrps.h"
static int
warn_unexpected_pdu(int fd, void *pdu, char const *pdu_name)
}
static int
-send_commmon_exchange(struct sender_common *common)
+send_commmon_exchange(struct sender_common *common,
+ int (*pdu_sender)(struct sender_common *))
{
int error;
return error;
/* Send Payload PDUs */
- error = send_payload_pdus(common);
+ error = pdu_sender(common);
if (error)
return error;
return send_end_of_data_pdu(common);
}
+/*
+ * TODO The semaphoring is bonkers. The code keeps locking, storing a value,
+ * unlocking, locking again, and using the old value.
+ * It doesn't look like it's a problem for now, but eventually will be, when old
+ * delta forgetting is implemented.
+ * I'm going to defer this because it shouldn't be done during the merge.
+ */
int
handle_serial_query_pdu(int fd, void *pdu)
{
struct serial_query_pdu *received = pdu;
struct sender_common common;
- int error, updates;
+ int error;
+ enum delta_status updates;
uint32_t current_serial;
uint16_t session_id;
uint8_t version;
updates = deltas_db_status(common.start_serial);
switch (updates) {
- case NO_DATA_AVAILABLE:
+ case DS_NO_DATA_AVAILABLE:
/* https://tools.ietf.org/html/rfc8210#section-8.4 */
return err_pdu_send(fd, version, ERR_PDU_NO_DATA_AVAILABLE,
NULL, NULL);
- case DIFF_UNDETERMINED:
+ case DS_DIFF_UNDETERMINED:
/* https://tools.ietf.org/html/rfc8210#section-8.3 */
return send_cache_reset_pdu(&common);
- case DIFF_AVAILABLE:
+ case DS_DIFF_AVAILABLE:
/* https://tools.ietf.org/html/rfc8210#section-8.2 */
- return send_commmon_exchange(&common);
- case NO_DIFF:
+ return send_commmon_exchange(&common, send_pdus_delta);
+ case DS_NO_DIFF:
/* Typical exchange with no Payloads */
error = send_cache_response_pdu(&common);
if (error)
return error;
return send_end_of_data_pdu(&common);
- default:
- warnx("Reached 'unreachable' code");
- return -EINVAL;
}
+
+ warnx("Reached 'unreachable' code");
+ return -EINVAL;
}
int
uint32_t current_serial;
uint16_t session_id;
uint8_t version;
- int updates;
+ enum delta_status updates;
version = received->header.protocol_version;
session_id = get_current_session_id(version);
updates = deltas_db_status(NULL);
switch (updates) {
- case NO_DATA_AVAILABLE:
+ case DS_NO_DATA_AVAILABLE:
/* https://tools.ietf.org/html/rfc8210#section-8.4 */
return err_pdu_send(fd, version, ERR_PDU_NO_DATA_AVAILABLE,
NULL, NULL);
- case DIFF_AVAILABLE:
+ case DS_DIFF_AVAILABLE:
/* https://tools.ietf.org/html/rfc8210#section-8.1 */
- return send_commmon_exchange(&common);
- default:
- warnx("Reached 'unreachable' code");
- return -EINVAL;
+ return send_commmon_exchange(&common, send_pdus_base);
+ case DS_DIFF_UNDETERMINED:
+ case DS_NO_DIFF:
+ break;
}
+
+ warnx("Reached 'unreachable' code");
+ return -EINVAL;
}
int
#include <unistd.h>
#include "config.h"
-#include "vrps.h"
#include "rtr/pdu_serializer.h"
+#include "rtr/db/vrps.h"
/* Header length field is always 64 bits long */
#define HEADER_LENGTH 8
pdu.prefix_length = vrp->prefix_length;
pdu.max_length = vrp->max_prefix_length;
pdu.zero = 0;
- pdu.ipv4_prefix = vrp->prefix.ipv4;
+ pdu.ipv4_prefix = vrp->prefix.v4;
pdu.asn = vrp->asn;
pdu.header.length = length_ipvx_prefix_pdu(true);
pdu.prefix_length = vrp->prefix_length;
pdu.max_length = vrp->max_prefix_length;
pdu.zero = 0;
- pdu.ipv6_prefix = vrp->prefix.ipv6;
+ pdu.ipv6_prefix = vrp->prefix.v6;
pdu.asn = vrp->asn;
pdu.header.length = length_ipvx_prefix_pdu(false);
}
int
-send_payload_pdus(struct sender_common *common)
+send_prefix_pdu(struct vrp *vrp, void *arg)
{
- struct vrp *vrps, *ptr;
- unsigned int len;
- int error;
-
- vrps = malloc(sizeof(struct vrp));
- if (vrps == NULL) {
- warn("Couldn't allocate VRPs to send PDUs");
- return -ENOMEM;
- }
- len = get_vrps_delta(common->start_serial, common->end_serial, &vrps);
- if (len == 0)
- goto end;
-
- for (ptr = vrps; (ptr - vrps) < len; ptr++) {
- if (ptr->addr_fam == AF_INET)
- error = send_ipv4_prefix_pdu(common, ptr);
- else if (ptr->addr_fam == AF_INET6)
- error = send_ipv6_prefix_pdu(common, ptr);
- else
- error = -EINVAL;
-
- if (error) {
- free(vrps);
- return error;
- }
+ switch (vrp->addr_fam) {
+ case AF_INET:
+ return send_ipv4_prefix_pdu(arg, vrp);
+ case AF_INET6:
+ return send_ipv6_prefix_pdu(arg, vrp);
}
-end:
- free(vrps);
- return 0;
+
+ return -EINVAL;
+}
+
+int
+send_pdus_base(struct sender_common *common)
+{
+ return vrps_foreach_base_roa(send_prefix_pdu, common);
+}
+
+int
+send_pdus_delta(struct sender_common *common)
+{
+ return vrps_foreach_delta_roa(*common->start_serial,
+ *common->end_serial, send_prefix_pdu, common);
}
int
int send_serial_notify_pdu(struct sender_common *);
int send_cache_reset_pdu(struct sender_common *);
int send_cache_response_pdu(struct sender_common *);
-int send_payload_pdus(struct sender_common *);
+int send_pdus_base(struct sender_common *);
+int send_pdus_delta(struct sender_common *);
int send_end_of_data_pdu(struct sender_common *);
int send_error_report_pdu(int, uint8_t, uint16_t, struct pdu_header *,
char const *);
char *data;
};
-__BEGIN_DECLS
void init_buffer(struct data_buffer *);
void free_buffer(struct data_buffer *);
size_t serialize_end_of_data_pdu(struct end_of_data_pdu *, char *);
size_t serialize_cache_reset_pdu(struct cache_reset_pdu *, char *);
size_t serialize_error_report_pdu(struct error_report_pdu *, char *);
-__END_DECLS
#endif /* SRC_RTR_PDU_SERIALIZER_H_ */
typedef char rtr_char;
-__BEGIN_DECLS
int read_int8(int, uint8_t *);
int read_int16(int, uint16_t *);
int read_int32(int, uint32_t *);
int read_in_addr(int, struct in_addr *);
int read_in6_addr(int, struct in6_addr *);
int read_string(int, rtr_char **);
-__END_DECLS
#endif /* RTR_PRIMITIVE_READER_H_ */
#include <netinet/in.h>
-__BEGIN_DECLS
char *write_int8(char *, uint8_t);
char *write_int16(char *, uint16_t);
char *write_int32(char *, uint32_t);
char *write_in_addr(char *, struct in_addr);
char *write_in6_addr(char *, struct in6_addr);
-__END_DECLS
#endif /* RTR_PRIMITIVE_WRITER_H_ */
#include "common.h"
-__BEGIN_DECLS
int rtr_listen(void);
void rtr_cleanup(void);
-__END_DECLS
#endif /* RTR_RTR_H_ */
#include <sys/queue.h>
#include <errno.h>
#include <string.h>
-#include "array_list.h"
#include "log.h"
#include "str.h"
#include "thread_var.h"
+#include "data_structure/array_list.h"
#include "object/certificate.h"
struct serial_number {
};
struct subject_name {
- struct rfc5280_name name;
+ struct rfc5280_name *name;
char *file; /* File where this subject name was found. */
};
*/
char addr_buffer1[INET6_ADDRSTRLEN];
char addr_buffer2[INET6_ADDRSTRLEN];
+
+ struct validation_handler validation_handler;
};
/*
/** Creates a struct validation, puts it in thread local, and returns it. */
int
-validation_prepare(struct validation **out, struct tal *tal)
+validation_prepare(struct validation **out, struct tal *tal,
+ struct validation_handler *validation_handler)
{
struct validation *result;
int error;
SLIST_INIT(&result->certs);
result->pubkey_state = PKS_UNTESTED;
+ result->validation_handler = *validation_handler;
*out = result;
return 0;
static void
subject_cleanup(struct subject_name *subject)
{
- x509_name_cleanup(&subject->name);
+ x509_name_put(subject->name);
free(subject->file);
}
return error;
}
-/**
- * This function will steal ownership of @subject on success.
- */
int
validation_store_subject(struct validation *state, struct rfc5280_name *subject)
{
* fixing.
*/
- /* Remember to free @subject if you return 0 but don't store it. */
-
cert = SLIST_FIRST(&state->certs);
- if (cert == NULL) {
- x509_name_cleanup(subject);
+ if (cert == NULL)
return 0; /* The TA lacks siblings, so subject is unique. */
- }
/* See the large comment in validation_store_serial_number(). */
ARRAYLIST_FOREACH(&cert->subjects, cursor) {
- if (x509_name_equals(&cursor->name, subject)) {
+ if (x509_name_equals(cursor->name, subject)) {
+ char const *serial = x509_name_serialNumber(subject);
pr_warn("Subject name '%s%s%s' is not unique. (Also found in '%s'.)",
- subject->commonName,
- (subject->serialNumber != NULL) ? "/" : "",
- (subject->serialNumber != NULL)
- ? subject->serialNumber
- : "",
+ x509_name_commonName(subject),
+ (serial != NULL) ? "/" : "",
+ (serial != NULL) ? serial : "",
cursor->file);
- x509_name_cleanup(subject);
return 0;
}
}
- duplicate.name = *subject;
+ duplicate.name = subject;
+ x509_name_get(subject);
+
error = get_current_file_name(&duplicate.file);
if (error)
- return error;
+ goto revert_name;
error = subjects_add(&cert->subjects, &duplicate);
if (error)
- free(duplicate.file);
+ goto revert_file;
+
+ return 0;
+revert_file:
+ free(duplicate.file);
+revert_name:
+ x509_name_put(subject);
return error;
}
{
return state->addr_buffer2;
}
+
+struct validation_handler const *
+validation_get_validation_handler(struct validation *state)
+{
+ return &state->validation_handler;
+}
#include <openssl/x509.h>
#include "resource.h"
+#include "validation_handler.h"
#include "object/name.h"
#include "object/tal.h"
struct validation;
-int validation_prepare(struct validation **, struct tal *);
+int validation_prepare(struct validation **, struct tal *,
+ struct validation_handler *);
void validation_destroy(struct validation *);
struct tal *validation_tal(struct validation *);
char *validation_get_ip_buffer1(struct validation *);
char *validation_get_ip_buffer2(struct validation *);
+struct validation_handler const *validation_get_validation_handler(struct validation *);
+
#endif /* SRC_STATE_H_ */
fprintf(stderr, "pthread_setspecific() returned %d.", error);
}
-static struct filename_stack *
-get_file_stack(void)
-{
- struct filename_stack *files;
-
- files = pthread_getspecific(filenames_key);
- if (files == NULL)
- fprintf(stderr, "This thread lacks a files stack.\n");
-
- return files;
-}
-
/**
* Call this function every time you're about to start processing a new file.
* Any pr_err()s and friends will now include the new file name.
struct filename_stack *files;
char const **tmp;
- files = get_file_stack();
+ files = pthread_getspecific(filenames_key);
if (files == NULL || files->filenames == NULL)
return;
{
struct filename_stack *files;
- files = get_file_stack();
+ files = pthread_getspecific(filenames_key);
if (files == NULL || files->filenames == NULL || files->len == 0)
return NULL;
{
struct filename_stack *files;
- files = get_file_stack();
+ files = pthread_getspecific(filenames_key);
if (files == NULL || files->filenames == NULL || files->len == 0)
return;
}
static char const *
-addr2str(int af, void *addr, char *(*buffer_cb)(struct validation *))
+addr2str(int af, void const *addr, char *(*buffer_cb)(struct validation *))
{
struct validation *state;
* The buffer is the same as v6addr2str()'s, so don't mix them either.
*/
char const *
-v4addr2str(struct in_addr *addr)
+v4addr2str(struct in_addr const *addr)
{
return addr2str(AF_INET, addr, validation_get_ip_buffer1);
}
* Same as v4addr2str(), except a different buffer is used.
*/
char const *
-v4addr2str2(struct in_addr *addr)
+v4addr2str2(struct in_addr const *addr)
{
return addr2str(AF_INET, addr, validation_get_ip_buffer2);
}
* See v4addr2str().
*/
char const *
-v6addr2str(struct in6_addr *addr)
+v6addr2str(struct in6_addr const *addr)
{
return addr2str(AF_INET6, addr, validation_get_ip_buffer1);
}
* See v4addr2str2().
*/
char const *
-v6addr2str2(struct in6_addr *addr)
+v6addr2str2(struct in6_addr const *addr)
{
return addr2str(AF_INET6, addr, validation_get_ip_buffer2);
}
char const *fnstack_peek(void);
void fnstack_pop(void);
-char const *v4addr2str(struct in_addr *addr);
-char const *v4addr2str2(struct in_addr *addr);
-char const *v6addr2str(struct in6_addr *addr);
-char const *v6addr2str2(struct in6_addr *addr);
+char const *v4addr2str(struct in_addr const *addr);
+char const *v4addr2str2(struct in_addr const *addr);
+char const *v6addr2str(struct in6_addr const *addr);
+char const *v6addr2str2(struct in6_addr const *addr);
#endif /* SRC_THREAD_VAR_H_ */
#include "config.h"
#include "notify.h"
#include "object/tal.h"
+#include "rtr/db/vrps.h"
static pthread_t thread;
+static int
+__reset(void *arg)
+{
+ return forthandler_reset(arg);
+}
+
+static int
+__traverse_down(struct rfc5280_name *subject_name, void *arg)
+{
+ return forthandler_go_down(arg, subject_name);
+}
+
+static int
+__traverse_up(void *arg)
+{
+ return forthandler_go_up(arg);
+}
+
+int
+__handle_roa_v4(uint32_t as, struct ipv4_prefix const *prefix,
+ uint8_t max_length, void *arg)
+{
+ return forthandler_handle_roa_v4(arg, as, prefix, max_length);
+}
+
+int
+__handle_roa_v6(uint32_t as, struct ipv6_prefix const * prefix,
+ uint8_t max_length, void *arg)
+{
+ return forthandler_handle_roa_v6(arg, as, prefix, max_length);
+}
+
static void *
check_vrps_updates(void *param_void)
{
+ struct validation_handler validation_handler;
+ struct roa_tree *old_tree;
+ struct deltas *deltas;
int error;
- bool updated;
+
+ validation_handler.reset = __reset;
+ validation_handler.traverse_down = __traverse_down;
+ validation_handler.traverse_up = __traverse_up;
+ validation_handler.handle_roa_v4 = __handle_roa_v4;
+ validation_handler.handle_roa_v6 = __handle_roa_v6;
+ old_tree = NULL;
do {
- updated = false;
- error = perform_standalone_validation(&updated);
+ validation_handler.arg = roa_tree_create();
+ if (validation_handler.arg == NULL) {
+ pr_err("Memory allocation failed. Cannot validate. Sleeping...");
+ goto sleep;
+ }
+
+ error = perform_standalone_validation(&validation_handler);
+ if (error) {
+ roa_tree_put(validation_handler.arg);
+ pr_err("Validation failed (error code %d). Cannot udpate the ROA database. Sleeping...",
+ error);
+ goto sleep;
+ }
+
+ if (old_tree == NULL) {
+ error = vrps_update(validation_handler.arg, NULL);
+ if (error) {
+ roa_tree_put(validation_handler.arg);
+ pr_err("Error code %d while trying to update the ROA database. Sleeping...",
+ error);
+ } else {
+ old_tree = validation_handler.arg;
+ }
+ goto sleep;
+ }
+
+ error = compute_deltas(old_tree, validation_handler.arg, &deltas);
if (error) {
- warnx("Error '%d' while searching CSV updates, sleeping...",
+ roa_tree_put(validation_handler.arg);
+ pr_err("Something went wrong while trying to compute the deltas. (error code %d.) Cannot update the ROA database. Sleeping...",
error);
goto sleep;
}
- if (updated)
- notify_clients();
+
+ if (deltas_is_empty(deltas)) {
+ roa_tree_put(validation_handler.arg);
+ deltas_destroy(deltas);
+ pr_debug("No changes. Sleeping...");
+ goto sleep;
+ }
+
+ error = vrps_update(validation_handler.arg, deltas);
+ if (error) {
+ roa_tree_put(validation_handler.arg);
+ deltas_destroy(deltas);
+ pr_err("Error code %d while trying to store the deltas in the database. Cannot update the ROA database. Sleeping...",
+ error);
+ goto sleep;
+ }
+
+ old_tree = validation_handler.arg;
+ notify_clients();
+ pr_debug("Database updated successfully. Sleeping...");
+
sleep:
sleep(config_get_vrps_check_interval());
} while (true);
--- /dev/null
+#include "validation_handler.h"
+
+#include <errno.h>
+#include "log.h"
+#include "thread_var.h"
+
+int
+vhandler_reset(struct validation_handler *handler)
+{
+ return (handler->reset != NULL) ? handler->reset(handler->arg) : 0;
+}
+
+static int
+get_current_threads_handler(struct validation_handler const **result)
+{
+ struct validation *state;
+ struct validation_handler const *handler;
+
+ state = state_retrieve();
+ if (state == NULL)
+ return -EINVAL;
+ handler = validation_get_validation_handler(state);
+ if (handler == NULL)
+ return pr_crit("This thread lacks a validation handler.");
+
+ *result = handler;
+ return 0;
+}
+
+int
+vhandler_traverse_down(struct rfc5280_name *subject_name)
+{
+ struct validation_handler const *handler;
+ int error;
+
+ error = get_current_threads_handler(&handler);
+ if (error)
+ return error;
+
+ return (handler->traverse_down != NULL)
+ ? handler->traverse_down(subject_name, handler->arg)
+ : 0;
+}
+
+int
+vhandler_traverse_up(void)
+{
+ struct validation_handler const *handler;
+ int error;
+
+ error = get_current_threads_handler(&handler);
+ if (error)
+ return error;
+
+ return (handler->traverse_up != NULL)
+ ? handler->traverse_up(handler->arg)
+ : 0;
+}
+
+int
+vhandler_handle_roa_v4(uint32_t as, struct ipv4_prefix const *prefix,
+ uint8_t max_length)
+{
+ struct validation_handler const *handler;
+ int error;
+
+ error = get_current_threads_handler(&handler);
+ if (error)
+ return error;
+
+ return (handler->handle_roa_v4 != NULL)
+ ? handler->handle_roa_v4(as, prefix, max_length, handler->arg)
+ : 0;
+}
+
+int
+vhandler_handle_roa_v6(uint32_t as, struct ipv6_prefix const *prefix,
+ uint8_t max_length)
+{
+ struct validation_handler const *handler;
+ int error;
+
+ error = get_current_threads_handler(&handler);
+ if (error)
+ return error;
+
+ return (handler->handle_roa_v6 != NULL)
+ ? handler->handle_roa_v6(as, prefix, max_length, handler->arg)
+ : 0;
+}
--- /dev/null
+#ifndef SRC_VALIDATION_HANDLER_H_
+#define SRC_VALIDATION_HANDLER_H_
+
+#include "address.h"
+#include "object/name.h"
+
+struct validation_handler {
+ /* All of these can be NULL. */
+
+ int (*reset)(void *);
+ int (*traverse_down)(struct rfc5280_name *, void *);
+ int (*traverse_up)(void *);
+ int (*handle_roa_v4)(uint32_t, struct ipv4_prefix const *, uint8_t,
+ void *);
+ int (*handle_roa_v6)(uint32_t, struct ipv6_prefix const *, uint8_t,
+ void *);
+ void *arg;
+};
+
+int vhandler_reset(struct validation_handler *);
+int vhandler_traverse_down(struct rfc5280_name *);
+int vhandler_traverse_up(void);
+int vhandler_handle_roa_v4(uint32_t, struct ipv4_prefix const *, uint8_t);
+int vhandler_handle_roa_v6(uint32_t, struct ipv6_prefix const *, uint8_t);
+
+#endif /* SRC_VALIDATION_HANDLER_H_ */
+++ /dev/null
-#include "vrps.h"
-
-#include <stdbool.h>
-#include <string.h>
-#include "array_list.h"
-#include "common.h"
-
-/*
- * Storage of VRPs (term taken from RFC 6811 "Validated ROA Payload") and
- * Serials that contain such VRPs
- */
-
-#define FLAG_WITHDRAWAL 0
-#define FLAG_ANNOUNCEMENT 1
-#define START_SERIAL 0
-
-ARRAY_LIST(vrps, struct vrp)
-
-struct delta {
- uint32_t serial;
- struct vrps vrps;
-};
-
-ARRAY_LIST(deltasdb, struct delta)
-
-struct state {
- /** The current valid ROAs, freshly loaded from the file */
- struct delta base_db;
- /** ROA changes over time */
- struct deltasdb deltas_db;
- uint32_t current_serial;
- uint16_t v0_session_id;
- uint16_t v1_session_id;
- time_t last_modified_date;
-} state;
-
-/* Read and Write locks */
-sem_t rlock, wlock;
-
-/* Readers counter */
-unsigned int rcounter;
-
-static int
-delta_init(struct delta *delta)
-{
- return vrps_init(&delta->vrps);
-}
-
-static void
-vrp_destroy(struct vrp *vrp)
-{
- /* Didn't allocate something, so do nothing */
-}
-
-static void
-delta_destroy(struct delta *delta)
-{
- vrps_cleanup(&delta->vrps, vrp_destroy);
-}
-
-int
-deltas_db_init(void)
-{
- int error, shift;
-
- error = delta_init(&state.base_db);
- if (error)
- return error;
-
- error = deltasdb_init(&state.deltas_db);
- if (error) {
- delta_destroy(&state.base_db);
- return error;
- }
-
- /*
- * Use the same start serial, the session ID will avoid
- * "desynchronization" (more at RFC 6810 'Glossary' and
- * 'Fields of a PDU')
- */
- state.current_serial = START_SERIAL;
-
- /* Get the bits that'll fit in session_id */
- shift = sizeof(time_t) - sizeof(state.v0_session_id);
- state.v0_session_id = (uint16_t)((time(NULL) << shift) >> shift);
- /* Minus 1 to prevent same ID */
- state.v1_session_id = state.v0_session_id - 1;
-
- sem_init(&rlock, 0, 1);
- sem_init(&wlock, 0, 1);
- rcounter = 0;
-
- return 0;
-}
-
-static void
-init_vrp (struct vrp *vrp, uint32_t asn, uint8_t prefix_length,
- uint8_t max_prefix_length)
-{
- vrp->asn = asn;
- vrp->prefix_length = prefix_length;
- vrp->max_prefix_length = max_prefix_length;
- /* Set as ANNOUNCEMENT by default */
- vrp->flags = FLAG_ANNOUNCEMENT;
-}
-
-struct vrp
-create_vrp4(uint32_t asn, struct in_addr ipv4_prefix, uint8_t prefix_length,
- uint8_t max_prefix_length)
-{
- struct vrp result;
-
- init_vrp(&result, asn, prefix_length, max_prefix_length);
- result.prefix.ipv4 = ipv4_prefix;
- result.addr_fam = AF_INET;
-
- return result;
-}
-
-struct vrp
-create_vrp6(uint32_t asn, struct in6_addr ipv6_prefix, uint8_t prefix_length,
- uint8_t max_prefix_length)
-{
- struct vrp result;
-
- init_vrp(&result, asn, prefix_length, max_prefix_length);
- result.prefix.ipv6 = ipv6_prefix;
- result.addr_fam = AF_INET6;
-
- return result;
-}
-
-static bool
-vrp_equal(struct vrp *left, struct vrp *right)
-{
- return left->asn == right->asn
- && left->addr_fam == right->addr_fam
- && left->prefix_length == right->prefix_length
- && left->max_prefix_length == right->max_prefix_length
- && ((left->addr_fam == AF_INET
- && left->prefix.ipv4.s_addr == right->prefix.ipv4.s_addr)
- || (left->addr_fam == AF_INET6
- && IN6_ARE_ADDR_EQUAL(left->prefix.ipv6.s6_addr32,
- right->prefix.ipv6.s6_addr32)));
-}
-
-static struct vrp *
-vrp_locate(struct vrps *base, struct vrp *vrp)
-{
- struct vrp *cursor;
-
- ARRAYLIST_FOREACH(base, cursor)
- if (vrp_equal(cursor, vrp))
- return cursor;
-
- return NULL;
-}
-
-static bool
-vrp_is_new(struct vrps *base, struct vrp *vrp)
-{
- return vrp_locate(base, vrp) == NULL;
-}
-
-static int
-delta_add_vrp(struct delta *delta, struct vrp *vrp)
-{
- return vrps_add(&delta->vrps, vrp);
-}
-
-static int
-delta_summary(struct delta *base_delta, struct delta *result)
-{
- struct vrps *base, *search_list;
- struct vrp *cursor;
- int error;
-
- /*
- * Note: Don't fix this function yet.
- * I realize why you implemented it this way, and I'm trying to come up
- * with a more efficient algorithm.
- */
-
- error = delta_init(result);
- if (error)
- return error;
-
- result->serial = base_delta->serial;
- read_lock(&rlock, &wlock, &rcounter);
- /* First check for announcements */
- base = &base_delta->vrps;
- search_list = &state.base_db.vrps;
- ARRAYLIST_FOREACH(base, cursor)
- if (vrp_is_new(search_list, cursor)) {
- cursor->flags = FLAG_ANNOUNCEMENT;
- error = delta_add_vrp(result, cursor);
- if (error) {
- read_unlock(&rlock, &wlock, &rcounter);
- return error;
- }
- }
-
- /* Now for withdrawals */
- base = &state.base_db.vrps;
- search_list = &base_delta->vrps;
- ARRAYLIST_FOREACH(base, cursor)
- if (vrp_is_new(search_list, cursor)) {
- cursor->flags = FLAG_WITHDRAWAL;
- error = delta_add_vrp(result, cursor);
- if (error) {
- read_unlock(&rlock, &wlock, &rcounter);
- return error;
- }
- }
-
- read_unlock(&rlock, &wlock, &rcounter);
- return 0;
-}
-
-static int
-deltas_db_add_delta(struct delta delta)
-{
- struct delta summary;
- int result;
-
- result = 0;
- read_lock(&rlock, &wlock, &rcounter);
- delta.serial = state.current_serial;
- read_unlock(&rlock, &wlock, &rcounter);
- /* Store only updates */
- if (delta.serial != START_SERIAL) {
- result = delta_summary(&delta, &summary);
- if (result != 0) {
- pr_err("Error summarizing new delta");
- return result;
- }
- sem_wait(&wlock);
- result = deltasdb_add(&state.deltas_db, &summary);
- sem_post(&wlock);
- }
- /* Don't set the base in case of error */
- if (result != 0) {
- pr_err("Error persisting new delta");
- return result;
- }
-
- sem_wait(&wlock);
- free(state.base_db.vrps.array);
- state.base_db = delta;
- state.current_serial++;
- sem_post(&wlock);
- return result;
-}
-
-static void
-copy_vrps(struct vrp **dst, struct vrp *src, unsigned int len)
-{
- struct vrp *tmp;
- tmp = realloc(*dst, len * sizeof(struct vrp));
- if (tmp == NULL) {
- pr_enomem();
- return;
- }
- *dst = tmp;
- memcpy(*dst, src, len * sizeof(struct vrp));
-}
-
-int
-deltas_db_create_delta(struct vrp *array, unsigned int len)
-{
- struct delta new_delta;
- int error;
-
- error = delta_init(&new_delta);
- if (error) {
- pr_err("New Delta couldn't be initialized");
- return error;
- }
-
- copy_vrps(&new_delta.vrps.array, array, len);
- new_delta.vrps.len = len;
- new_delta.vrps.capacity = len * sizeof(struct vrp);
-
- error = deltas_db_add_delta(new_delta);
- if (error)
- return error;
-
- return 0;
-}
-
-void
-deltas_db_destroy(void)
-{
- sem_wait(&wlock);
- delta_destroy(&state.base_db);
- deltasdb_cleanup(&state.deltas_db, delta_destroy);
- sem_post(&wlock);
-
- sem_destroy(&wlock);
- sem_destroy(&rlock);
-}
-
-/*
- * Get a status to know the difference between the delta with serial SERIAL and
- * the last delta at DB.
- *
- * If SERIAL is received as NULL, and there's data at DB then the status will
- * be DIFF_AVAILABLE.
- *
- * The possible return values are:
- * NO_DATA_AVAILABLE -> There's no data at the DB
- * DIFF_UNDETERMINED -> The diff can't be determined
- * NO_DIFF -> There's no difference
- * DIFF_AVAILABLE -> There are diffs between SERIAL and the last DB serial
- */
-int
-deltas_db_status(uint32_t *serial)
-{
- struct delta *delta;
- int result;
-
- read_lock(&rlock, &wlock, &rcounter);
- if (state.base_db.vrps.len == 0) {
- result = NO_DATA_AVAILABLE;
- goto end;
- }
-
- /* No serial to match, and there's data at DB */
- if (serial == NULL) {
- result = DIFF_AVAILABLE;
- goto end;
- }
-
- /* Is the last version? */
- if (*serial == state.base_db.serial) {
- result = NO_DIFF;
- goto end;
- }
-
- /* Get the delta corresponding to the serial */
- ARRAYLIST_FOREACH(&state.deltas_db, delta)
- if (delta->serial == *serial) {
- result = DIFF_AVAILABLE;
- goto end;
- }
-
- /* No match yet, release lock */
- read_unlock(&rlock, &wlock, &rcounter);
-
- /* The first serial isn't at deltas */
- if (*serial == START_SERIAL)
- return DIFF_AVAILABLE;
-
- /* Reached end, diff can't be determined */
- return DIFF_UNDETERMINED;
-end:
- read_unlock(&rlock, &wlock, &rcounter);
- return result;
-}
-
-static void
-add_vrps_filtered(struct vrps *dst, struct vrps *src)
-{
- struct vrp *ptr;
- for (ptr = src->array; (ptr - src->array) < src->len; ptr++)
- if (vrp_is_new(dst, ptr))
- vrps_add(dst, ptr);
-}
-
-/*
- * Get the number of updates from serial START_SERIAL to END_SERIAL, set them
- * at RESULT.
- *
- * Return 0 if no updates are available or couldn't be calculated with the
- * received values.
- */
-unsigned int
-get_vrps_delta(uint32_t *start_serial, uint32_t *end_serial,
- struct vrp **result)
-{
- struct delta *delta1;
- struct vrps summary;
- unsigned int vrps_len;
-
- read_lock(&rlock, &wlock, &rcounter);
- /* No data */
- if (state.base_db.vrps.len == 0) {
- read_unlock(&rlock, &wlock, &rcounter);
- return 0;
- }
-
- /* NULL start? Send the last version, there's no need to iterate DB */
- if (start_serial == NULL) {
- copy_vrps(result, state.base_db.vrps.array,
- state.base_db.vrps.len);
- vrps_len = state.base_db.vrps.len;
- read_unlock(&rlock, &wlock, &rcounter);
- return vrps_len;
- }
-
- /* Apparently nothing to return */
- if (*start_serial >= *end_serial) {
- read_unlock(&rlock, &wlock, &rcounter);
- return 0;
- }
-
- /* Get the delta corresponding to the serials */
- vrps_init(&summary);
- ARRAYLIST_FOREACH(&state.deltas_db, delta1) {
- if (delta1->serial > *start_serial)
- add_vrps_filtered(&summary, &delta1->vrps);
- if (delta1->serial == *end_serial)
- break;
- }
- read_unlock(&rlock, &wlock, &rcounter);
-
- copy_vrps(result, summary.array, summary.len);
- vrps_cleanup(&summary, vrp_destroy);
- return summary.len;
-}
-
-void
-set_vrps_last_modified_date(time_t new_date)
-{
- sem_wait(&wlock);
- state.last_modified_date = new_date;
- sem_post(&wlock);
-}
-
-uint32_t
-get_last_serial_number(void)
-{
- uint32_t serial;
-
- read_lock(&rlock, &wlock, &rcounter);
- serial = state.current_serial - 1;
- read_unlock(&rlock, &wlock, &rcounter);
-
- return serial;
-}
-
-uint16_t
-get_current_session_id(uint8_t rtr_version)
-{
- /* Semaphore isn't needed since this value is set at initialization */
- if (rtr_version == 1)
- return state.v1_session_id;
- return state.v0_session_id;
-}
-
-time_t
-get_vrps_last_modified_date(void)
-{
- time_t date;
-
- read_lock(&rlock, &wlock, &rcounter);
- date = state.last_modified_date;
- read_unlock(&rlock, &wlock, &rcounter);
-
- return date;
-}
+++ /dev/null
-#ifndef SRC_VRPS_H_
-#define SRC_VRPS_H_
-
-#include <time.h>
-#include <netinet/ip.h>
-
-#define NO_DATA_AVAILABLE -2
-#define DIFF_UNDETERMINED -1
-#define NO_DIFF 0
-#define DIFF_AVAILABLE 1
-
-struct vrp {
- uint32_t asn;
- union {
- struct in_addr ipv4;
- struct in6_addr ipv6;
- } prefix;
- uint8_t prefix_length;
- uint8_t max_prefix_length;
- uint8_t addr_fam;
- uint8_t flags;
-};
-
-int deltas_db_init(void);
-
-struct vrp create_vrp4(uint32_t, struct in_addr, uint8_t, uint8_t);
-struct vrp create_vrp6(uint32_t, struct in6_addr, uint8_t, uint8_t);
-
-int deltas_db_create_delta(struct vrp *, unsigned int);
-int deltas_db_status(uint32_t *);
-
-unsigned int get_vrps_delta(uint32_t *, uint32_t *, struct vrp **);
-
-void deltas_db_destroy(void);
-void set_vrps_last_modified_date(time_t);
-
-uint32_t get_last_serial_number(void);
-uint16_t get_current_session_id(uint8_t);
-time_t get_vrps_last_modified_date(void);
-
-#endif /* SRC_VRPS_H_ */
BASIC_MODULES += impersonator.c
check_PROGRAMS = address.test
+check_PROGRAMS += circular_indexer.test
check_PROGRAMS += vcard.test
check_PROGRAMS += line_file.test
check_PROGRAMS += rsync.test
-check_PROGRAMS += tal.test
-check_PROGRAMS += rtr/primitive_reader.test
-check_PROGRAMS += rtr/pdu.test
+#check_PROGRAMS += tal.test
+#check_PROGRAMS += rtr/primitive_reader.test
+#check_PROGRAMS += rtr/pdu.test
TESTS = ${check_PROGRAMS}
address_test_SOURCES = ${BASIC_MODULES}
address_test_SOURCES += address_test.c
address_test_LDADD = ${MY_LDADD}
+circular_indexer_test_SOURCES = ${BASIC_MODULES}
+circular_indexer_test_SOURCES += ../src/data_structure/circular_indexer.c
+circular_indexer_test_SOURCES += data_structure/circular_indexer_test.c
+circular_indexer_test_LDADD = ${MY_LDADD}
+
line_file_test_SOURCES = ${BASIC_MODULES}
line_file_test_SOURCES += ../src/file.c ../src/file.h
line_file_test_SOURCES += ../src/line_file.c ../src/line_file.h
rsync_test_SOURCES += rsync_test.c
rsync_test_LDADD = ${MY_LDADD}
-rtr_primitive_reader_test_SOURCES = ${BASIC_MODULES}
-rtr_primitive_reader_test_SOURCES += rtr/primitive_reader_test.c
-rtr_primitive_reader_test_SOURCES += rtr/stream.c
-rtr_primitive_reader_test_LDADD = ${MY_LDADD}
+#rtr_primitive_reader_test_SOURCES = ${BASIC_MODULES}
+#rtr_primitive_reader_test_SOURCES += rtr/primitive_reader_test.c
+#rtr_primitive_reader_test_SOURCES += rtr/stream.c
+#rtr_primitive_reader_test_LDADD = ${MY_LDADD}
-rtr_pdu_test_SOURCES = ${BASIC_MODULES}
-rtr_pdu_test_SOURCES += rtr/pdu_test.c
-rtr_pdu_test_SOURCES += rtr/stream.c
-rtr_pdu_test_SOURCES += $(top_builddir)/src/rtr/primitive_reader.c
-rtr_pdu_test_SOURCES += $(top_builddir)/src/rtr/pdu_handler.c
-rtr_pdu_test_LDADD = ${MY_LDADD}
+#rtr_pdu_test_SOURCES = ${BASIC_MODULES}
+#rtr_pdu_test_SOURCES += rtr/pdu_test.c
+#rtr_pdu_test_SOURCES += rtr/stream.c
+#rtr_pdu_test_SOURCES += ../src/rtr/primitive_reader.c
+#rtr_pdu_test_SOURCES += ../src/rtr/pdu_handler.c
+#rtr_pdu_test_LDADD = ${MY_LDADD}
-tal_test_SOURCES = ${BASIC_MODULES}
-tal_test_SOURCES += ../src/file.c ../src/file.h
-tal_test_SOURCES += ../src/crypto/base64.c ../src/crypto/base64.h
-tal_test_SOURCES += ../src/random.c ../src/random.h
-tal_test_SOURCES += ../src/str.c ../src/str.h
-tal_test_SOURCES += ../src/uri.c ../src/uri.h
-tal_test_SOURCES += ../src/line_file.c ../src/line_file.h
-tal_test_SOURCES += tal_test.c
-tal_test_LDADD = ${MY_LDADD}
+#tal_test_SOURCES = ${BASIC_MODULES}
+#tal_test_SOURCES += ../src/file.c ../src/file.h
+#tal_test_SOURCES += ../src/crypto/base64.c ../src/crypto/base64.h
+#tal_test_SOURCES += ../src/random.c ../src/random.h
+#tal_test_SOURCES += ../src/str.c ../src/str.h
+#tal_test_SOURCES += ../src/uri.c ../src/uri.h
+#tal_test_SOURCES += ../src/line_file.c ../src/line_file.h
+#tal_test_SOURCES += tal_test.c
+#tal_test_LDADD = ${MY_LDADD}
vcard_test_SOURCES = ${BASIC_MODULES}
vcard_test_SOURCES += vcard_test.c
}
END_TEST
-static void
-test_get_address_from_string(char *text_prefix)
-{
- struct ipv4_prefix prefix;
- const char *result;
- int error;
- char buffer[INET_ADDRSTRLEN];
-
- error = prefix4_decode(text_prefix, &prefix);
- if (error)
- return;
-
- result = addr2str4(&prefix.addr, buffer);
-
- ck_assert_str_eq(text_prefix, result);
-}
-
-START_TEST(address_test_get_addr)
-{
- char *text;
- text = "198.248.146.0";
-
- test_get_address_from_string(text);
-
-}
-END_TEST
-
Suite *address_load_suite(void)
{
Suite *suite;
TCase *core;
- TCase *string;
core = tcase_create("Core");
tcase_add_test(core, check_encoding4_test);
tcase_add_test(core, check_encoding6_test);
- string = tcase_create("Strings");
- tcase_add_test(string, address_test_get_addr);
-
suite = suite_create("Encoding checking");
suite_add_tcase(suite, core);
return suite;
--- /dev/null
+#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;
+}
}
struct string_array const *
-config_get_rsync_args(void)
+config_get_rsync_args(bool is_ta)
{
static const struct string_array array = { 0 };
return &array;
{
header->protocol_version = 0;
header->pdu_type = 22;
- header->reserved = 12345;
+ header->m.reserved = 12345;
header->length = 0xFFAA9955;
}
{
ck_assert_uint_eq(header->protocol_version, 0);
ck_assert_uint_eq(header->pdu_type, 22);
- ck_assert_uint_eq(header->reserved, 12345);
+ ck_assert_uint_eq(header->m.reserved, 12345);
ck_assert_uint_eq(header->length, 0xFFAA9955);
}
ck_assert_uint_eq(header.protocol_version, 0);
ck_assert_uint_eq(header.pdu_type, 1);
- ck_assert_uint_eq(header.reserved, 0x0203);
+ ck_assert_uint_eq(header.m.reserved, 0x0203);
ck_assert_uint_eq(header.length, 0x04050607);
}
END_TEST
sub_pdu = pdu->erroneous_pdu;
ck_assert_uint_eq(sub_pdu->header.protocol_version, 1);
ck_assert_uint_eq(sub_pdu->header.pdu_type, 0);
- ck_assert_uint_eq(sub_pdu->header.reserved, 0x0203);
+ ck_assert_uint_eq(sub_pdu->header.m.reserved, 0x0203);
ck_assert_uint_eq(sub_pdu->header.length, 12);
ck_assert_uint_eq(sub_pdu->serial_number, 0x01020304);
ck_assert_str_eq(pdu->error_message, "hello");
#include <stddef.h>
-__BEGIN_DECLS
int write_exact(int, unsigned char *, size_t);
int buffer2fd(unsigned char *, size_t);
-__END_DECLS
#endif /* TEST_RTR_STREAM_H_ */