fort_SOURCES += address.h address.c
fort_SOURCES += algorithm.h algorithm.c
fort_SOURCES += certificate_refs.h certificate_refs.c
+fort_SOURCES += clients.c clients.h
+fort_SOURCES += common.c common.h
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 += extension.h extension.c
fort_SOURCES += file.h file.c
+fort_SOURCES += json_parser.c json_parser.h
fort_SOURCES += line_file.h line_file.c
fort_SOURCES += log.h log.c
fort_SOURCES += nid.h nid.c
+fort_SOURCES += notify.c notify.h
fort_SOURCES += random.h random.c
fort_SOURCES += resource.h resource.c
fort_SOURCES += rpp.h rpp.c
+fort_SOURCES += slurm_db.c slurm_db.h
+fort_SOURCES += slurm_loader.c slurm_loader.h
+fort_SOURCES += slurm_parser.c slurm_parser.h
fort_SOURCES += sorted_array.h sorted_array.c
fort_SOURCES += state.h state.c
fort_SOURCES += str.h str.c
fort_SOURCES += thread_var.h thread_var.c
+fort_SOURCES += updates_daemon.c updates_daemon.h
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 += asn1/content_info.h asn1/content_info.c
fort_SOURCES += asn1/decode.h asn1/decode.c
fort_SOURCES += asn1/oid.h asn1/oid.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 += resource/ip6.h resource/ip6.c
fort_SOURCES += resource/asn.h resource/asn.c
-fort_SOURCES += array_list.h
-fort_SOURCES += clients.c clients.h
-fort_SOURCES += common.c common.h
-fort_SOURCES += json_parser.c json_parser.h
-fort_SOURCES += notify.c notify.h
-fort_SOURCES += slurm_db.c slurm_db.h
-fort_SOURCES += slurm_loader.c slurm_loader.h
-fort_SOURCES += slurm_parser.c slurm_parser.h
-fort_SOURCES += updates_daemon.c updates_daemon.h
+fort_SOURCES += rsync/rsync.h rsync/rsync.c
fort_SOURCES += rtr/err_pdu.c rtr/err_pdu.h
fort_SOURCES += rtr/pdu_handler.c rtr/pdu_handler.h
return inet_ntop(AF_INET6, addr, buffer, INET6_ADDRSTRLEN);
}
-static int const
+static int
str2addr4(const char *addr, struct in_addr *dst)
{
if (!inet_pton(AF_INET, addr, dst))
return 0;
}
-static int const
+static int
str2addr6(const char *addr, struct in6_addr *dst)
{
if (!inet_pton(AF_INET6, addr, dst))
return -EINVAL;
}
/* An underflow or overflow will be considered here */
- if (len < 0 || max_value < len)
- return pr_err("Prefix length (%ld) is out of range (0-%d).",
+ if (max_value < len)
+ return pr_err("Prefix length (%lu) is out of range (0-%u).",
len, max_value);
*dst = (uint8_t) len;
/** Read/write lock, which protects @table and its inhabitants. */
static pthread_rwlock_t lock;
/** Serial number from which deltas must be stored */
-static uint32_t min_serial;
+static serial_t min_serial;
int
clients_db_init(void)
}
void
-clients_update_serial(int fd, uint32_t serial)
+clients_update_serial(int fd, serial_t serial)
{
struct hashable_client *cur_client;
rwlock_unlock(&lock);
}
-uint32_t
+serial_t
clients_get_min_serial(void)
{
struct hashable_client *current, *ptr;
- uint32_t result;
+ serial_t result;
rwlock_write_lock(&lock);
if (HASH_COUNT(table) == 0)
#define SRC_CLIENTS_H_
#include <arpa/inet.h>
+#include "rtr/db/vrp.h"
#define ERTR_VERSION_MISMATCH 8754983
};
in_port_t sin_port;
uint8_t rtr_version;
- uint32_t serial_number;
+ serial_t serial_number;
};
int clients_db_init(void);
int clients_add(int, struct sockaddr_storage *, uint8_t);
-void clients_update_serial(int, uint32_t);
+void clients_update_serial(int, serial_t);
void clients_forget(int);
typedef int (*clients_foreach_cb)(struct client const *, void *);
int clients_foreach(clients_foreach_cb, void *);
-uint32_t clients_get_min_serial(void);
+serial_t clients_get_min_serial(void);
void clients_db_destroy(void);
size_t capacity; \
}
-#define DEFINE_ARRAY_LIST_FUNCTIONS(name, elem_type) \
- static void \
+#define DECLARE_ARRAY_LIST_FUNCTIONS(name, elem_type) \
+ void name##_init(struct name *); \
+ void name##_cleanup(struct name *, void (*cb)(elem_type *)); \
+ int name##_add(struct name *list, elem_type *elem);
+
+#define DEFINE_ARRAY_LIST_FUNCTIONS(name, elem_type, modifiers) \
+ modifiers void \
name##_init(struct name *list) \
{ \
list->array = NULL; \
list->capacity = 0; \
} \
\
- static void \
+ modifiers void \
name##_cleanup(struct name *list, void (*cb)(elem_type *)) \
{ \
array_index i; \
} \
\
/* Will store a shallow copy, not @elem */ \
- static int \
+ modifiers int \
name##_add(struct name *list, elem_type *elem) \
{ \
elem_type *tmp; \
#define ARRAY_LIST(name, elem_type) \
DEFINE_ARRAY_LIST_STRUCT(name, elem_type); \
- DEFINE_ARRAY_LIST_FUNCTIONS(name, elem_type)
+ DEFINE_ARRAY_LIST_FUNCTIONS(name, elem_type, )
/* c = cursor */
#define ARRAYLIST_FOREACH(list, c) for ( \
send_notify(struct client const *client, void *arg)
{
struct sender_common common;
- uint32_t *serial = arg;
- uint16_t session_id;
+ serial_t *serial = arg;
int error;
/* Send Serial Notify PDU */
- session_id = get_current_session_id(client->rtr_version);
- init_sender_common(&common, client->fd, client->rtr_version,
- &session_id, serial, NULL);
- error = send_serial_notify_pdu(&common);
+ init_sender_common(&common, client->fd, client->rtr_version);
+ error = send_serial_notify_pdu(&common, *serial);
- /* Error? Log it */
+ /* Error? Log it... */
if (error)
- pr_warn("Error sending notify PDU to client");
+ pr_warn("Error code %d sending notify PDU to client.", error);
- return 0; /* Do not interrupt notify to other clients */
+ return 0; /* ...but do not interrupt notify to other clients */
}
int
notify_clients(void)
{
- uint32_t serial;
+ serial_t serial;
int error;
error = get_last_serial_number(&serial);
struct deltas_v6 adds;
struct deltas_v6 removes;
} v6;
+
+ /* TODO (now) atomic */
+ unsigned int references;
};
int
deltas_v4_init(&result->v4.removes);
deltas_v6_init(&result->v6.adds);
deltas_v6_init(&result->v6.removes);
+ result->references = 1;
*_result = result;
return 0;
}
void
-deltas_destroy(struct deltas *deltas)
+deltas_get(struct deltas *deltas)
+{
+ deltas->references++;
+}
+
+void
+deltas_put(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);
+ deltas->references--;
+ if (deltas->references == 0) {
+ 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)
+ int op)
{
struct delta_v4 delta = {
.as = as,
};
switch (op) {
- case DELTA_ADD:
+ case FLAG_ANNOUNCEMENT:
return deltas_v4_add(&deltas->v4.adds, &delta);
- case DELTA_RM:
+ case FLAG_WITHDRAWAL:
return deltas_v4_add(&deltas->v4.removes, &delta);
}
- return pr_crit("Unknown delta operation: %u", op);
+ return pr_crit("Unknown delta operation: %d", op);
}
int
deltas_add_roa_v6(struct deltas *deltas, uint32_t as, struct v6_address *addr,
- enum delta_op op)
+ int op)
{
struct delta_v6 delta = {
.as = as,
};
switch (op) {
- case DELTA_ADD:
+ case FLAG_ANNOUNCEMENT:
return deltas_v6_add(&deltas->v6.adds, &delta);
- case DELTA_RM:
+ case FLAG_WITHDRAWAL:
return deltas_v6_add(&deltas->v6.removes, &delta);
}
- return pr_crit("Unknown delta operation: %u", op);
+ return pr_crit("Unknown delta operation: %d", op);
}
bool
}
static int
-__foreach_v4(struct deltas_v4 *array, vrp_foreach_cb cb, void *arg,
- uint8_t flags)
+__foreach_v4(struct deltas_v4 *array, delta_foreach_cb cb, void *arg,
+ serial_t serial, uint8_t flags)
{
+ struct delta delta;
struct delta_v4 *d;
- struct vrp vrp;
int error;
- vrp.addr_fam = AF_INET;
- vrp.flags = flags;
+ delta.serial = serial;
+ delta.vrp.addr_fam = AF_INET;
+ delta.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);
+ delta.vrp.asn = d->as;
+ delta.vrp.prefix.v4 = d->prefix.addr;
+ delta.vrp.prefix_length = d->prefix.len;
+ delta.vrp.max_prefix_length = d->max_length;
+ error = cb(&delta, arg);
if (error)
return error;
}
}
static int
-__foreach_v6(struct deltas_v6 *array, vrp_foreach_cb cb, void *arg,
- uint8_t flags)
+__foreach_v6(struct deltas_v6 *array, delta_foreach_cb cb, void *arg,
+ serial_t serial, uint8_t flags)
{
+ struct delta delta;
struct delta_v6 *d;
- struct vrp vrp;
int error;
- vrp.addr_fam = AF_INET6;
- vrp.flags = flags;
+ delta.serial = serial;
+ delta.vrp.addr_fam = AF_INET6;
+ delta.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);
+ delta.vrp.asn = d->as;
+ delta.vrp.prefix.v6 = d->prefix.addr;
+ delta.vrp.prefix_length = d->prefix.len;
+ delta.vrp.max_prefix_length = d->max_length;
+ error = cb(&delta, arg);
if (error)
return error;
}
}
int
-deltas_foreach(struct deltas *deltas, vrp_foreach_cb cb, void *arg)
+deltas_foreach(serial_t serial, struct deltas *deltas, delta_foreach_cb cb,
+ void *arg)
{
int error;
- error = __foreach_v4(&deltas->v4.adds, cb, arg, FLAG_ANNOUNCEMENT);
+ error = __foreach_v4(&deltas->v4.adds, cb, arg, serial,
+ FLAG_ANNOUNCEMENT);
if (error)
return error;
- error = __foreach_v4(&deltas->v4.removes, cb, arg, FLAG_WITHDRAWAL);
+
+ error = __foreach_v4(&deltas->v4.removes, cb, arg, serial,
+ FLAG_WITHDRAWAL);
if (error)
return error;
- error = __foreach_v6(&deltas->v6.adds, cb, arg, FLAG_ANNOUNCEMENT);
+ error = __foreach_v6(&deltas->v6.adds, cb, arg, serial,
+ FLAG_ANNOUNCEMENT);
if (error)
return error;
- return __foreach_v6(&deltas->v6.removes, cb, arg, FLAG_WITHDRAWAL);
+
+ return __foreach_v6(&deltas->v6.removes, cb, arg, serial,
+ FLAG_WITHDRAWAL);
}
#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 *);
+void deltas_get(struct deltas *);
+void deltas_put(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);
+int deltas_add_roa_v4(struct deltas *, uint32_t, struct v4_address *, int);
+int deltas_add_roa_v6(struct deltas *, uint32_t, struct v6_address *, int);
bool deltas_is_empty(struct deltas *);
-int deltas_foreach(struct deltas *, vrp_foreach_cb , void *);
+int deltas_foreach(serial_t, struct deltas *, delta_foreach_cb , void *);
#endif /* SRC_DELTA_H_ */
#include "rtr/db/roa.h"
-DEFINE_ARRAY_LIST_FUNCTIONS(v4_addresses, struct v4_address)
-DEFINE_ARRAY_LIST_FUNCTIONS(v6_addresses, struct v6_address)
+DEFINE_ARRAY_LIST_FUNCTIONS(v4_addresses, struct v4_address, static)
+DEFINE_ARRAY_LIST_FUNCTIONS(v6_addresses, struct v6_address, static)
int
roa_create(uint32_t as, struct roa **_result)
roa->data.asn = asn;
roa->data.max_prefix_length = max_length;
- roa->data.flags = FLAG_ANNOUNCEMENT;
return roa;
}
}
static int
-add_delta(struct deltas *deltas, struct hashable_roa *roa, enum delta_op op)
+add_delta(struct deltas *deltas, struct hashable_roa *roa, int op)
{
union {
struct v4_address v4;
*/
static int
add_deltas(struct hashable_roa *roas1, struct hashable_roa *roas2,
- struct deltas *deltas, enum delta_op op)
+ struct deltas *deltas, int op)
{
struct hashable_roa *n1; /* A node from @roas1 */
struct hashable_roa *n2; /* A node from @roas2 */
if (error)
return error;
- error = add_deltas(new->roas, old->roas, deltas, DELTA_ADD);
+ error = add_deltas(new->roas, old->roas, deltas, FLAG_ANNOUNCEMENT);
if (error)
goto fail;
- error = add_deltas(old->roas, new->roas, deltas, DELTA_RM);
+ error = add_deltas(old->roas, new->roas, deltas, FLAG_WITHDRAWAL);
if (error)
goto fail;
return 0;
fail:
- deltas_destroy(deltas);
+ deltas_put(deltas);
return error;
}
#define FLAG_WITHDRAWAL 0
#define FLAG_ANNOUNCEMENT 1
+typedef uint32_t serial_t;
+
struct vrp {
uint32_t asn;
/*
uint8_t prefix_length;
uint8_t max_prefix_length;
uint8_t addr_fam;
- uint8_t flags;
+ /* uint8_t flags; */ /* TODO (now) commit remove */
+};
+
+struct delta {
+ serial_t serial;
+ struct vrp vrp;
+ uint8_t flags;
};
-typedef int (*vrp_foreach_cb)(struct vrp *, void *);
+typedef int (*vrp_foreach_cb)(struct vrp const *, void *);
+typedef int (*delta_foreach_cb)(struct delta const *, void *);
#endif /* SRC_RTR_DB_VRP_H_ */
#include "vrps.h"
#include <pthread.h>
-#include <stdbool.h>
-#include <string.h>
+#include <time.h>
#include "clients.h"
#include "common.h"
-#include "validation_handler.h"
-#include "data_structure/array_list.h"
#include "object/tal.h"
#include "rtr/db/roa_table.h"
#define START_SERIAL 0
-struct delta {
- uint32_t serial;
- struct deltas *deltas;
-};
-
-ARRAY_LIST(deltas_db, struct delta)
+DEFINE_ARRAY_LIST_FUNCTIONS(deltas_db, struct delta_group, )
struct state {
/**
/** ROA changes to @base over time. */
struct deltas_db deltas;
- uint32_t current_serial;
+ /*
+ * TODO (whatever) Maybe rename this. It's not really the current
+ * serial; it's the next one.
+ */
+ serial_t current_serial;
uint16_t v0_session_id;
uint16_t v1_session_id;
time_t last_modified_date;
/** Read/write lock, which protects @state and its inhabitants. */
static pthread_rwlock_t lock;
-static void
-delta_destroy(struct delta *delta)
+void
+deltagroup_cleanup(struct delta_group *group)
{
- deltas_destroy(delta->deltas);
+ deltas_put(group->deltas);
}
int
int error;
state.base = NULL;
-
deltas_db_init(&state.deltas);
/*
* '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 */
error = pthread_rwlock_init(&lock, NULL);
if (error) {
- deltas_db_cleanup(&state.deltas, delta_destroy);
+ deltas_db_cleanup(&state.deltas, deltagroup_cleanup);
return pr_errno(error, "pthread_rwlock_init() errored");
}
{
if (state.base != NULL)
roa_table_destroy(state.base);
- deltas_db_cleanup(&state.deltas, delta_destroy);
+ deltas_db_cleanup(&state.deltas, deltagroup_cleanup);
pthread_rwlock_destroy(&lock); /* Nothing to do with error code */
}
return 0;
}
-
-
/**
* Reallocate the array of @db starting at @start, the length and capacity are
* calculated according to the new start.
*/
static void
-resize_deltas_db(struct deltas_db *db, struct delta *start)
+resize_deltas_db(struct deltas_db *db, struct delta_group *start)
{
- struct delta *tmp;
+ struct delta_group *tmp;
db->len -= (start - db->array);
while (db->len < db->capacity / 2)
db->capacity /= 2;
- tmp = malloc(sizeof(struct delta) * db->capacity);
+ tmp = malloc(sizeof(struct delta_group) * db->capacity);
if (tmp == NULL) {
pr_enomem();
return;
}
- memcpy(tmp, start, db->len * sizeof(struct delta));
+ memcpy(tmp, start, db->len * sizeof(struct delta_group));
free(db->array);
db->array = tmp;
}
static void
vrps_purge(void)
{
- struct delta *d;
+ struct delta_group *group;
uint32_t min_serial;
min_serial = clients_get_min_serial();
/** Assume is ordered by serial, so get the new initial pointer */
- ARRAYLIST_FOREACH(&state.deltas, d)
- if (d->serial >= min_serial)
+ ARRAYLIST_FOREACH(&state.deltas, group)
+ if (group->serial >= min_serial)
break;
/** Is the first element or reached end, nothing to purge */
- if (d == state.deltas.array ||
- (d - state.deltas.array) == state.deltas.len)
+ if (group == state.deltas.array ||
+ (group - state.deltas.array) == state.deltas.len)
return;
- resize_deltas_db(&state.deltas, d);
+ resize_deltas_db(&state.deltas, group);
}
int
struct roa_table *old_base;
struct roa_table *new_base;
struct deltas *deltas; /* Deltas in raw form */
- struct delta deltas_node; /* Deltas in database node form */
+ struct delta_group deltas_node; /* Deltas in database node form */
int error;
*changed = false;
/** Remove unnecessary deltas */
vrps_purge();
+
+ } else {
+ /*
+ * Make an empty dummy delta array.
+ * It's annoying, but temporary. (Until it expires.)
+ * Otherwise, it'd be a pain to have to check NULL
+ * delta_group.deltas all the time.
+ */
+ error = deltas_create(&deltas);
+ if (error) {
+ rwlock_unlock(&lock);
+ goto revert_base;
+ }
+
+ deltas_node.serial = state.current_serial;
+ deltas_node.deltas = deltas;
+ error = deltas_db_add(&state.deltas, &deltas_node);
+ if (error) {
+ rwlock_unlock(&lock);
+ goto revert_deltas;
+ }
}
*changed = true;
return 0;
revert_deltas:
- deltas_destroy(deltas);
+ deltas_put(deltas);
revert_base:
roa_table_destroy(new_base);
return error;
}
-/*
- * 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.
- *
- * This function can only fail due to critical r/w lock bugs.
- */
-int
-deltas_db_status(uint32_t *serial, enum delta_status *result)
-{
- struct delta *delta;
- int error;
-
- error = rwlock_read_lock(&lock);
- if (error)
- return error;
-
- if (state.base == NULL) {
- *result = DS_NO_DATA_AVAILABLE;
- goto rlock_succeed;
- }
-
- /* No serial to match, and there's data at DB */
- if (serial == NULL) {
- *result = DS_DIFF_AVAILABLE;
- goto rlock_succeed;
- }
-
- /* Is the last version? */
- if (*serial == (state.current_serial - 1)) {
- *result = DS_NO_DIFF;
- goto rlock_succeed;
- }
-
- /* The first serial isn't at deltas */
- if (*serial == START_SERIAL) {
- *result = DS_DIFF_AVAILABLE;
- goto rlock_succeed;
- }
-
- /* Get the delta corresponding to the serial */
- ARRAYLIST_FOREACH(&state.deltas, delta)
- if (delta->serial == *serial) {
- *result = DS_DIFF_AVAILABLE;
- goto rlock_succeed;
- }
-
- /* No match yet, release lock */
- rwlock_unlock(&lock);
-
- /* Reached end, diff can't be determined */
- *result = DS_DIFF_UNDETERMINED;
- return 0;
-
-rlock_succeed:
- rwlock_unlock(&lock);
- return 0;
-}
-
+/* TODO (whatever) @serial is a dumb hack. */
int
-vrps_foreach_base_roa(vrp_foreach_cb cb, void *arg)
+vrps_foreach_base_roa(vrp_foreach_cb cb, void *arg, serial_t *serial)
{
int error;
if (error)
return error;
- if (state.base != NULL)
+ if (state.base != NULL) {
error = roa_table_foreach_roa(state.base, cb, arg);
+ *serial = state.current_serial - 1;
+ } else {
+ error = -EAGAIN;
+ }
rwlock_unlock(&lock);
return error;
}
+/**
+ * Adds to @result the deltas whose serial > @from.
+ *
+ * The result code is one of the following:
+ *
+ * 0: No errors.
+ * -EAGAIN: No data available; database still under construction.
+ * -ESRCH: @from was not found.
+ * Anything else: Generic fail.
+ *
+ * As usual, only 0 guarantees valid out parameters. (@to and @result.)
+ * (But note that @result is supposed to be already initialized, so caller will
+ * have to clean it up regardless of error.)
+ */
int
-vrps_foreach_delta_roa(uint32_t from, uint32_t to, vrp_foreach_cb cb, void *arg)
+vrps_get_deltas_from(serial_t from, serial_t *to, struct deltas_db *result)
{
- struct delta *d;
+ struct delta_group *group;
bool from_found;
int error;
if (error)
return error;
- ARRAYLIST_FOREACH(&state.deltas, d) {
+ if (state.base == NULL) {
+ rwlock_unlock(&lock);
+ return -EAGAIN;
+ }
+
+ ARRAYLIST_FOREACH(&state.deltas, group) {
if (!from_found) {
- if (d->serial > from)
+ if (group->serial == from) {
from_found = true;
- else
- continue;
+ *to = group->serial;
+ }
+ continue;
}
- if (d->serial > to)
- break;
- error = deltas_foreach(d->deltas, cb, arg);
- if (error)
- break;
+
+ error = deltas_db_add(result, group);
+ if (error) {
+ rwlock_unlock(&lock);
+ return error;
+ }
+
+ deltas_get(group->deltas);
+ *to = group->serial;
}
rwlock_unlock(&lock);
-
- return error;
+ return from_found ? 0 : -ESRCH;
}
int
-get_last_serial_number(uint32_t *result)
+get_last_serial_number(serial_t *result)
{
int error;
if (error)
return error;
- *result = state.current_serial - 1;
+ if (state.base != NULL)
+ *result = state.current_serial - 1;
+ else
+ error = -EAGAIN;
rwlock_unlock(&lock);
- return 0;
+ return error;
}
uint16_t
#define SRC_VRPS_H_
#include <stdbool.h>
-#include "rtr/db/vrp.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,
+#include "data_structure/array_list.h"
+#include "rtr/db/delta.h"
+
+/*
+ * Deltas that share a serial.
+ */
+struct delta_group {
+ serial_t serial;
+ struct deltas *deltas;
};
+void deltagroup_cleanup(struct delta_group *);
+
+DEFINE_ARRAY_LIST_STRUCT(deltas_db, struct delta_group);
+DECLARE_ARRAY_LIST_FUNCTIONS(deltas_db, struct delta_group)
+
int vrps_init(void);
void vrps_destroy(void);
int vrps_update(bool *);
-int deltas_db_status(uint32_t *, enum delta_status *);
-int vrps_foreach_base_roa(vrp_foreach_cb, void *);
-int vrps_foreach_delta_roa(uint32_t, uint32_t, vrp_foreach_cb, void *);
+/*
+ * The following three functions return -EAGAIN when vrps_update() has never
+ * been called, or while it's still building the database.
+ * Handle gracefully.
+ */
+int vrps_foreach_base_roa(vrp_foreach_cb, void *, serial_t *);
+int vrps_get_deltas_from(serial_t, serial_t *, struct deltas_db *);
+int get_last_serial_number(serial_t *);
-int get_last_serial_number(uint32_t *);
uint16_t get_current_session_id(uint8_t);
#endif /* SRC_VRPS_H_ */
#include "pdu_sender.h"
#include "log.h"
+/*
+ * TODO (urgent) According to the function below, NO_DATA_AVAILABLE is not
+ * fatal. However, some callers of this function are terminating the connection
+ * regardless of that.
+ */
int
err_pdu_send(int fd, uint8_t version, uint16_t code, void *err_pdu_header,
char const *message)
#define RTR_V0 0
#define RTR_V1 1
-#define PDU_TYPE_SERIAL_NOTIFY 0
-#define PDU_TYPE_CACHE_RESPONSE 3
-#define PDU_TYPE_IPV4_PREFIX 4
-#define PDU_TYPE_IPV6_PREFIX 6
-#define PDU_TYPE_END_OF_DATA 7
-#define PDU_TYPE_CACHE_RESET 8
-#define PDU_TYPE_ROUTER_KEY 9
-#define PDU_TYPE_ERROR_REPORT 10
+enum pdu_type {
+ PDU_TYPE_SERIAL_NOTIFY = 0,
+ PDU_TYPE_SERIAL_QUERY = 1,
+ PDU_TYPE_RESET_QUERY = 2,
+ PDU_TYPE_CACHE_RESPONSE = 3,
+ PDU_TYPE_IPV4_PREFIX = 4,
+ PDU_TYPE_IPV6_PREFIX = 6,
+ PDU_TYPE_END_OF_DATA = 7,
+ PDU_TYPE_CACHE_RESET = 8,
+ PDU_TYPE_ROUTER_KEY = 9,
+ PDU_TYPE_ERROR_REPORT = 10,
+};
struct pdu_header {
uint8_t protocol_version;
struct pdu_header *pdu_header = pdu;
pr_warn("Unexpected %s PDU received", pdu_name);
err_pdu_send(fd, pdu_header->protocol_version, ERR_PDU_UNSUP_PDU_TYPE,
- pdu_header, "Unexpected PDU received");
+ pdu_header, "PDU is unexpected or out of order.");
return -EINVAL;
}
return warn_unexpected_pdu(fd, pdu, "Serial Notify");
}
-static int
-send_commmon_exchange(struct sender_common *common,
- int (*pdu_sender)(struct sender_common *))
+int
+handle_serial_query_pdu(int fd, void *pdu)
{
+ struct serial_query_pdu *received = pdu;
+ struct sender_common common;
+ struct deltas_db deltas;
+ serial_t final_serial;
int error;
+ init_sender_common(&common, fd, received->header.protocol_version);
+ /*
+ * RFC 6810 and 8210:
+ * "If [...] either the router or the cache finds that the value of the
+ * Session ID is not the same as the other's, the party which detects
+ * the mismatch MUST immediately terminate the session with an Error
+ * Report PDU with code 0 ("Corrupt Data")"
+ */
+ if (received->header.m.session_id != common.session_id)
+ return err_pdu_send(fd, common.version, ERR_PDU_CORRUPT_DATA,
+ &received->header, "Session ID doesn't match.");
+
/*
- * TODO (urgent) On certain errors, shouldn't we send error PDUs or
+ * TODO (now) On certain errors, shouldn't we send error PDUs or
* something?
*/
- /* Send Cache response PDU */
- error = send_cache_response_pdu(common);
+ /*
+ * For the record, there are two reasons why we want to work on a
+ * (shallow) copy of the deltas (as opposed to eg. a foreach):
+ * 1. We need to remove deltas that cancel each other.
+ * (Which can't be done directly on the DB.)
+ * 2. It's probably best not to hold the VRPS read lock while writing
+ * PDUs, to minimize writer stagnation.
+ */
+
+ deltas_db_init(&deltas);
+ error = vrps_get_deltas_from(received->serial_number, &final_serial,
+ &deltas);
+ if (error == -EAGAIN) {
+ error = err_pdu_send(fd, common.version,
+ ERR_PDU_NO_DATA_AVAILABLE, NULL, NULL);
+ goto end;
+ }
+ if (error == -ESRCH) {
+ /* https://tools.ietf.org/html/rfc6810#section-6.3 */
+ error = send_cache_reset_pdu(&common);
+ goto end;
+ }
if (error)
- return error;
+ goto end;
- /* Send Payload PDUs */
- error = pdu_sender(common);
+ /*
+ * https://tools.ietf.org/html/rfc6810#section-6.2
+ * (Except the end of data PDU.)
+ */
+
+ error = send_cache_response_pdu(&common);
if (error)
- return error;
+ goto end;
+ error = send_pdus_delta(&deltas, &common);
+ if (error)
+ goto end; /* TODO (now) maybe send something? */
+ error = send_end_of_data_pdu(&common, final_serial);
- /* Send End of data PDU */
- return send_end_of_data_pdu(common);
+end:
+ deltas_db_cleanup(&deltas, deltagroup_cleanup);
+ return error;
}
-/*
- * TODO (urgent) 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 base_roa_args {
+ bool started;
struct sender_common common;
+ serial_t last_serial;
+};
+
+static int
+send_base_roa(struct vrp const *vrp, void *arg)
+{
+ struct base_roa_args *args = arg;
int error;
- enum delta_status updates;
- uint32_t current_serial;
- uint16_t session_id;
- uint8_t version;
- /*
- * RFC 6810 and 8210:
- * "If [...] either the router or the cache finds that the value of the
- * Session ID is not the same as the other's, the party which detects
- * the mismatch MUST immediately terminate the session with an Error
- * Report PDU with code 0 ("Corrupt Data")"
- */
- version = received->header.protocol_version;
- session_id = get_current_session_id(version);
- if (received->header.m.session_id != session_id)
- return err_pdu_send(fd, version, ERR_PDU_CORRUPT_DATA,
- &received->header, NULL);
- if (get_last_serial_number(¤t_serial) != 0)
- goto critical;
-
- init_sender_common(&common, fd, version, &session_id,
- &received->serial_number, ¤t_serial);
-
- if (deltas_db_status(common.start_serial, &updates) != 0)
- goto critical;
-
- switch (updates) {
- 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 DS_DIFF_UNDETERMINED:
- /* https://tools.ietf.org/html/rfc8210#section-8.3 */
- return send_cache_reset_pdu(&common);
- case DS_DIFF_AVAILABLE:
- /* https://tools.ietf.org/html/rfc8210#section-8.2 */
- return send_commmon_exchange(&common, send_pdus_delta);
- case DS_NO_DIFF:
- /* Typical exchange with no Payloads */
- error = send_cache_response_pdu(&common);
+ if (!args->started) {
+ error = send_cache_response_pdu(&args->common);
if (error)
return error;
- return send_end_of_data_pdu(&common);
+ args->started = true;
}
- pr_warn("Reached 'unreachable' code");
- return -EINVAL;
-
-critical:
- return err_pdu_send(fd, version, ERR_PDU_INTERNAL_ERROR,
- &received->header, NULL);
+ /* TODO (now) maybe send something on error? */
+ return send_prefix_pdu(&args->common, vrp, FLAG_ANNOUNCEMENT);
}
int
handle_reset_query_pdu(int fd, void *pdu)
{
struct reset_query_pdu *received = pdu;
- struct sender_common common;
- uint32_t current_serial;
- uint16_t session_id;
- uint8_t version;
- enum delta_status updates;
-
- version = received->header.protocol_version;
- session_id = get_current_session_id(version);
- if (get_last_serial_number(¤t_serial) != 0)
- goto critical;
-
- init_sender_common(&common, fd, version, &session_id, NULL,
- ¤t_serial);
-
- if (deltas_db_status(NULL, &updates) != 0)
- goto critical;
- switch (updates) {
- 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 DS_DIFF_AVAILABLE:
- /* https://tools.ietf.org/html/rfc8210#section-8.1 */
- return send_commmon_exchange(&common, send_pdus_base);
- case DS_DIFF_UNDETERMINED:
- case DS_NO_DIFF:
- break;
- }
+ struct base_roa_args args;
+ serial_t current_serial;
+ int error;
- pr_warn("Reached 'unreachable' code");
- return -EINVAL;
+ args.started = false;
+ init_sender_common(&args.common, fd, received->header.protocol_version);
+
+ /*
+ * It's probably best not to work on a copy, because the tree is large.
+ * Unfortunately, this means we'll have to encourage writer stagnation,
+ * but most clients are supposed to request far more serial queries than
+ * reset queries.
+ */
+
+ error = vrps_foreach_base_roa(send_base_roa, &args, ¤t_serial);
+ if (error == -EAGAIN)
+ return err_pdu_send(fd, args.common.version,
+ ERR_PDU_NO_DATA_AVAILABLE, NULL, NULL);
+ if (error)
+ return error;
-critical:
- return err_pdu_send(fd, version, ERR_PDU_INTERNAL_ERROR,
- &received->header, NULL);
+ return send_end_of_data_pdu(&args.common, current_serial);
}
int
struct vrp_node {
- struct vrp vrp;
+ struct delta delta;
SLIST_ENTRY(vrp_node) next;
};
SLIST_HEAD(vrp_slist, vrp_node);
void
-init_sender_common(struct sender_common *common, int fd, uint8_t version,
- uint16_t *session_id, uint32_t *start_serial, uint32_t *end_serial)
+init_sender_common(struct sender_common *common, int fd, uint8_t version)
{
common->fd = fd;
common->version = version;
- common->session_id = session_id == NULL ? 0 : session_id;
- common->start_serial = start_serial;
- common->end_serial = end_serial;
+ common->session_id = get_current_session_id(version);
}
/*
* Set all the header values, EXCEPT length field.
}
int
-send_serial_notify_pdu(struct sender_common *common)
+send_serial_notify_pdu(struct sender_common *common, serial_t start_serial)
{
struct serial_notify_pdu pdu;
unsigned char data[BUFFER_SIZE];
size_t len;
set_header_values(&pdu.header, common->version, PDU_TYPE_SERIAL_NOTIFY,
- *common->session_id);
+ common->session_id);
- pdu.serial_number = *common->start_serial;
+ pdu.serial_number = start_serial;
pdu.header.length = length_serial_notify_pdu(&pdu);
len = serialize_serial_notify_pdu(&pdu, data);
size_t len;
/* This PDU has only the header */
- set_header_values(&pdu.header, common->version,
- PDU_TYPE_CACHE_RESPONSE, *common->session_id);
+ set_header_values(&pdu.header, common->version, PDU_TYPE_CACHE_RESPONSE,
+ common->session_id);
pdu.header.length = HEADER_LENGTH;
len = serialize_cache_response_pdu(&pdu, data);
}
static int
-send_ipv4_prefix_pdu(struct sender_common *common, struct vrp *vrp)
+send_ipv4_prefix_pdu(struct sender_common *common, struct vrp const *vrp,
+ uint8_t flags)
{
struct ipv4_prefix_pdu pdu;
unsigned char data[BUFFER_SIZE];
set_header_values(&pdu.header, common->version, PDU_TYPE_IPV4_PREFIX,
0);
- pdu.flags = vrp->flags;
+ pdu.flags = flags;
pdu.prefix_length = vrp->prefix_length;
pdu.max_length = vrp->max_prefix_length;
pdu.zero = 0;
}
static int
-send_ipv6_prefix_pdu(struct sender_common *common, struct vrp *vrp)
+send_ipv6_prefix_pdu(struct sender_common *common, struct vrp const *vrp,
+ uint8_t flags)
{
struct ipv6_prefix_pdu pdu;
unsigned char data[BUFFER_SIZE];
set_header_values(&pdu.header, common->version, PDU_TYPE_IPV6_PREFIX,
0);
- pdu.flags = vrp->flags;
+ pdu.flags = flags;
pdu.prefix_length = vrp->prefix_length;
pdu.max_length = vrp->max_prefix_length;
pdu.zero = 0;
}
int
-send_prefix_pdu(struct vrp *vrp, void *arg)
+send_prefix_pdu(struct sender_common *common, struct vrp const *vrp,
+ uint8_t flags)
{
switch (vrp->addr_fam) {
case AF_INET:
- return send_ipv4_prefix_pdu(arg, vrp);
+ return send_ipv4_prefix_pdu(common, vrp, flags);
case AF_INET6:
- return send_ipv6_prefix_pdu(arg, vrp);
+ return send_ipv6_prefix_pdu(common, vrp, flags);
}
return -EINVAL;
}
-int
-send_pdus_base(struct sender_common *common)
-{
- return vrps_foreach_base_roa(send_prefix_pdu, common);
-}
-
static bool
-vrp_equals(struct vrp *left, struct vrp *right)
+vrp_equals(struct vrp const *left, struct vrp const *right)
{
return left->asn == right->asn
&& left->addr_fam == right->addr_fam
right->prefix.v6.s6_addr32)));
}
+static int
+vrp_simply_send(struct delta const *delta, void *arg)
+{
+ return send_prefix_pdu(arg, &delta->vrp, delta->flags);
+}
+
/**
- * Remove the announcements/withdrawals that override each other
+ * Remove the announcements/withdrawals that override each other.
+ *
+ * (Note: We're assuming the array is already duplicateless enough thanks to the
+ * hash table.)
*/
static int
-vrp_ovrd_remove(struct vrp *vrp, void *arg)
+vrp_ovrd_remove(struct delta const *delta, void *arg)
{
struct vrp_node *ptr;
struct vrp_slist *filtered_vrps = arg;
SLIST_FOREACH(ptr, filtered_vrps, next)
- if (vrp_equals(vrp, &ptr->vrp) &&
- vrp->flags != ptr->vrp.flags) {
+ if (vrp_equals(&delta->vrp, &ptr->delta.vrp) &&
+ delta->flags != ptr->delta.flags) {
SLIST_REMOVE(filtered_vrps, ptr, vrp_node, next);
free(ptr);
return 0;
if (ptr == NULL)
return pr_enomem();
- ptr->vrp = *vrp;
+ ptr->delta = *delta;
SLIST_INSERT_HEAD(filtered_vrps, ptr, next);
return 0;
}
int
-send_pdus_delta(struct sender_common *common)
+send_pdus_delta(struct deltas_db *deltas, struct sender_common *common)
{
struct vrp_slist filtered_vrps;
+ struct delta_group *group;
struct vrp_node *ptr;
int error;
+ /*
+ * Short circuit: Entries that share serial are already guaranteed to
+ * not contradict each other, so no filtering required.
+ */
+ if (deltas->len == 1) {
+ group = &deltas->array[0];
+ return deltas_foreach(group->serial, group->deltas,
+ vrp_simply_send, common);
+ }
+
+ /*
+ * Filter: Remove entries that cancel each other.
+ * (We'll have to build a separate list because the database nodes
+ * are immutable.)
+ */
SLIST_INIT(&filtered_vrps);
- error = vrps_foreach_delta_roa(*common->start_serial,
- *common->end_serial, vrp_ovrd_remove, &filtered_vrps);
- if (error)
- goto release_list;
+ ARRAYLIST_FOREACH(deltas, group) {
+ error = deltas_foreach(group->serial, group->deltas,
+ vrp_ovrd_remove, &filtered_vrps);
+ if (error)
+ goto release_list;
+ }
- /** Now send the filtered deltas */
+ /* Now send the filtered deltas */
SLIST_FOREACH(ptr, &filtered_vrps, next) {
- error = send_prefix_pdu(&ptr->vrp, common);
+ error = send_prefix_pdu(common, &ptr->delta.vrp,
+ ptr->delta.flags);
if (error)
break;
}
}
int
-send_end_of_data_pdu(struct sender_common *common)
+send_end_of_data_pdu(struct sender_common *common, serial_t end_serial)
{
struct end_of_data_pdu pdu;
unsigned char data[BUFFER_SIZE];
int error;
set_header_values(&pdu.header, common->version, PDU_TYPE_END_OF_DATA,
- *common->session_id);
- pdu.serial_number = *common->end_serial;
+ common->session_id);
+ pdu.serial_number = end_serial;
if (common->version == RTR_V1) {
pdu.refresh_interval = config_get_refresh_interval();
pdu.retry_interval = config_get_retry_interval();
unsigned char data[BUFFER_SIZE];
size_t len;
- set_header_values(&pdu.header, version, PDU_TYPE_ERROR_REPORT,
- code);
+ set_header_values(&pdu.header, version, PDU_TYPE_ERROR_REPORT, code);
pdu.error_pdu_length = 0;
pdu.erroneous_pdu = (void *)err_pdu_header;
#define SRC_RTR_PDU_SENDER_H_
#include "pdu.h"
+#include "rtr/db/vrps.h"
struct sender_common {
int fd;
uint8_t version;
- uint16_t *session_id;
- uint32_t *start_serial;
- uint32_t *end_serial;
+ uint16_t session_id;
};
-void init_sender_common(struct sender_common *, int, uint8_t, uint16_t *,
- uint32_t *, uint32_t *);
+void init_sender_common(struct sender_common *, int, uint8_t);
-int send_serial_notify_pdu(struct sender_common *);
+int send_serial_notify_pdu(struct sender_common *, serial_t);
int send_cache_reset_pdu(struct sender_common *);
int send_cache_response_pdu(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_prefix_pdu(struct sender_common *, struct vrp const *, uint8_t);
+int send_pdus_delta(struct deltas_db *, struct sender_common *);
+int send_end_of_data_pdu(struct sender_common *, serial_t);
int send_error_report_pdu(int, uint8_t, uint16_t, struct pdu_header *,
char const *);
/* Protocol Version Negotiation */
if (rtr_version != RTR_VERSION_SUPPORTED) {
err_pdu_send(param->client_fd, RTR_VERSION_SUPPORTED,
- ERR_PDU_UNSUP_PROTO_VERSION,
- (struct pdu_header *) pdu, NULL);
+ ERR_PDU_UNSUP_PROTO_VERSION, pdu, NULL);
return end_client(param->client_fd, meta, pdu);
}
/* RTR Version ready, now update client */
if (err) {
if (err == -ERTR_VERSION_MISMATCH) {
err_pdu_send(param->client_fd, rtr_version,
- (rtr_version == RTR_V0
- ? ERR_PDU_UNSUP_PROTO_VERSION
- : ERR_PDU_UNEXPECTED_PROTO_VERSION),
- (struct pdu_header *) pdu, NULL);
+ (rtr_version == RTR_V0)
+ ? ERR_PDU_UNSUP_PROTO_VERSION
+ : ERR_PDU_UNEXPECTED_PROTO_VERSION,
+ pdu, NULL);
}
return end_client(param->client_fd, meta, pdu);
}
check_PROGRAMS = address.test
check_PROGRAMS += clients.test
check_PROGRAMS += line_file.test
+check_PROGRAMS += pdu_handler.test
check_PROGRAMS += rsync.test
check_PROGRAMS += roa_table.test
#check_PROGRAMS += tal.test
check_PROGRAMS += vcard.test
+check_PROGRAMS += vrps.test
#check_PROGRAMS += rtr/pdu.test
#check_PROGRAMS += rtr/primitive_reader.test
TESTS = ${check_PROGRAMS}
line_file_test_SOURCES += line_file_test.c
line_file_test_LDADD = ${MY_LDADD}
+pdu_handler_test_SOURCES = ${BASIC_MODULES}
+pdu_handler_test_SOURCES += ../src/common.c
+pdu_handler_test_SOURCES += ../src/rtr/pdu_handler.c
+pdu_handler_test_SOURCES += ../src/rtr/err_pdu.c
+pdu_handler_test_SOURCES += ../src/rtr/db/delta.c
+pdu_handler_test_SOURCES += ../src/rtr/db/roa_table.c
+pdu_handler_test_SOURCES += ../src/rtr/db/vrps.c
+pdu_handler_test_SOURCES += rtr/db/impersonator.c
+pdu_handler_test_SOURCES += rtr/pdu_handler_test.c
+pdu_handler_test_LDADD = ${MY_LDADD}
+
roa_table_test_SOURCES = ${BASIC_MODULES}
roa_table_test_SOURCES += ../src/rtr/db/delta.c
roa_table_test_SOURCES += ../src/rtr/db/roa_table.c
vcard_test_SOURCES += vcard_test.c
vcard_test_LDADD = ${MY_LDADD}
+vrps_test_SOURCES = ${BASIC_MODULES}
+vrps_test_SOURCES += ../src/common.c
+vrps_test_SOURCES += ../src/rtr/db/delta.c
+vrps_test_SOURCES += ../src/rtr/db/roa_table.c
+vrps_test_SOURCES += ../src/rtr/db/vrps.c
+vrps_test_SOURCES += rtr/db/impersonator.c
+vrps_test_SOURCES += rtr/db/vrps_test.c
+vrps_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
--- /dev/null
+#include "object/tal.h"
+
+#include <check.h>
+
+static int iteration = 0;
+
+static void
+add_v4(struct validation_handler *handler, uint32_t as)
+{
+ struct ipv4_prefix prefix;
+ prefix.addr.s_addr = htonl(0xC0000200);
+ prefix.len = 24;
+ ck_assert_int_eq(0, handler->handle_roa_v4(as, &prefix, 32,
+ handler->arg));
+}
+
+static void
+add_v6(struct validation_handler *handler, uint32_t as)
+{
+ struct ipv6_prefix prefix;
+ prefix.addr.s6_addr32[0] = htonl(0x20010DB8);
+ prefix.addr.s6_addr32[1] = 0;
+ prefix.addr.s6_addr32[2] = 0;
+ prefix.addr.s6_addr32[3] = 0;
+ prefix.len = 96;
+ ck_assert_int_eq(0, handler->handle_roa_v6(as, &prefix, 120,
+ handler->arg));
+}
+
+int
+perform_standalone_validation(struct validation_handler *handler)
+{
+ ck_assert_int_eq(0, handler->reset(handler->arg));
+
+ switch (iteration) {
+ case 0:
+ add_v4(handler, 0);
+ add_v6(handler, 0);
+ break;
+ case 1:
+ add_v4(handler, 0);
+ add_v6(handler, 0);
+ add_v4(handler, 1);
+ add_v6(handler, 1);
+ break;
+ case 2:
+ case 3:
+ add_v4(handler, 1);
+ add_v6(handler, 1);
+ break;
+ default:
+ ck_abort_msg("perform_standalone_validation() was called too many times (%d).",
+ iteration);
+ }
+
+ iteration++;
+ return 0;
+}
static unsigned int total_found;
static bool
-vrp_equals_v4(struct vrp *vrp, uint8_t as, uint32_t addr, uint8_t prefix_len,
- uint8_t max_prefix_len)
+vrp_equals_v4(struct vrp const *vrp, uint8_t as, uint32_t addr,
+ uint8_t prefix_len, uint8_t max_prefix_len)
{
return (AF_INET == vrp->addr_fam)
&& (as == vrp->asn)
}
static bool
-vrp_equals_v6(struct vrp *vrp, uint8_t as, uint32_t addr, uint8_t prefix_len,
- uint8_t max_prefix_len)
+vrp_equals_v6(struct vrp const *vrp, uint8_t as, uint32_t addr,
+ uint8_t prefix_len, uint8_t max_prefix_len)
{
return (AF_INET6 == vrp->addr_fam)
&& (as == vrp->asn)
}
static int
-foreach_cb(struct vrp *vrp, void *arg)
+foreach_cb(struct vrp const *vrp, void *arg)
{
char const *str;
for (i = 0; i < TOTAL_ROAS; i++)
ck_assert_int_eq(true, roas_found[i]);
- roa_table_put(table);
+ roa_table_destroy(table);
}
END_TEST
--- /dev/null
+#include <check.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "common.h"
+#include "thread_var.h"
+#include "validation_handler.h"
+#include "rtr/db/vrps.h"
+
+/* -- Expected database descriptors -- */
+
+/*
+ * BASE
+ * 0: IPv4, ASN 0
+ * 1: IPv4, ASN 1
+ * 2: IPv6, ASN 0
+ * 3: IPv6, ASN 1
+ */
+static const bool iteration0_base[] = { 1, 0, 1, 0, };
+static const bool iteration1_base[] = { 1, 1, 1, 1, };
+static const bool iteration2_base[] = { 0, 1, 0, 1, };
+
+/*
+ * DELTA
+ * 0: Withdrawal, IPv4, ASN 0 4: Announcement, IPv4, ASN 0
+ * 1: Withdrawal, IPv4, ASN 1 5: Announcement, IPv4, ASN 1
+ * 2: Withdrawal, IPv6, ASN 0 6: Announcement, IPv6, ASN 0
+ * 3: Withdrawal, IPv6, ASN 1 7: Announcement, IPv6, ASN 1
+ */
+
+static const bool deltas_0to0[] = { 0, 0, 0, 0, 0, 0, 0, 0, };
+
+static const bool deltas_0to1[] = { 0, 0, 0, 0, 0, 1, 0, 1, };
+static const bool deltas_1to1[] = { 0, 0, 0, 0, 0, 0, 0, 0, };
+
+static const bool deltas_0to2[] = { 1, 0, 1, 0, 0, 1, 0, 1, };
+static const bool deltas_1to2[] = { 1, 0, 1, 0, 0, 0, 0, 0, };
+static const bool deltas_2to2[] = { 0, 0, 0, 0, 0, 0, 0, 0, };
+
+/* Impersonator functions */
+
+serial_t
+clients_get_min_serial(void)
+{
+ return 0;
+}
+
+/* Test functions */
+
+static int
+vrp_fail(struct vrp const *vrp, void *arg)
+{
+ char const *addr;
+
+ switch (vrp->addr_fam) {
+ case AF_INET:
+ addr = v4addr2str(&vrp->prefix.v4);
+ break;
+ case AF_INET6:
+ addr = v6addr2str(&vrp->prefix.v6);
+ break;
+ default:
+ addr = "unknown";
+ }
+
+ ck_abort_msg("Expected no callbacks, got VRP %u/%s/%u/%u.",
+ vrp->asn, addr, vrp->prefix_length, vrp->max_prefix_length);
+}
+
+static array_index
+get_vrp_index(struct vrp const *vrp)
+{
+ array_index family_bit;
+
+ switch (vrp->addr_fam) {
+ case AF_INET:
+ ck_assert_uint_eq(htonl(0xC0000200), vrp->prefix.v4.s_addr);
+ ck_assert_uint_eq(24, vrp->prefix_length);
+ ck_assert_uint_eq(32, vrp->max_prefix_length);
+ family_bit = 0;
+ break;
+
+ case AF_INET6:
+ ck_assert_uint_eq(htonl(0x20010DB8), vrp->prefix.v6.s6_addr32[0]);
+ ck_assert_uint_eq(0, vrp->prefix.v6.s6_addr32[1]);
+ ck_assert_uint_eq(0, vrp->prefix.v6.s6_addr32[2]);
+ ck_assert_uint_eq(0, vrp->prefix.v6.s6_addr32[3]);
+ ck_assert_uint_eq(96, vrp->prefix_length);
+ ck_assert_uint_eq(120, vrp->max_prefix_length);
+ family_bit = 1;
+ break;
+
+ default:
+ ck_abort_msg("VRP has unknown protocol: %u", vrp->addr_fam);
+ }
+
+ ck_assert_msg(vrp->asn <= 1, "Unexpected AS number: %u", vrp->asn);
+
+ return (family_bit << 1) | (vrp->asn << 0);
+}
+
+static array_index
+get_delta_index(struct delta const *delta)
+{
+ array_index result;
+
+ result = get_vrp_index(&delta->vrp);
+ ck_assert_msg(delta->flags <= 1, "Unexpected flags: %u", delta->flags);
+
+ return (delta->flags << 2) | result;
+}
+
+static int
+vrp_check(struct vrp const *vrp, void *arg)
+{
+ bool *array = arg;
+ array_index index;
+
+ index = get_vrp_index(vrp);
+ ck_assert_uint_eq(false, array[index]);
+ array[index] = true;
+
+ return 0;
+}
+
+static int
+delta_check(struct delta const *delta, void *arg)
+{
+ bool *array = arg;
+ array_index index;
+
+ index = get_delta_index(delta);
+ ck_assert_uint_eq(false, array[index]);
+ array[index] = true;
+
+ return 0;
+
+}
+
+static void
+check_serial(serial_t expected_serial)
+{
+ serial_t actual_serial;
+ ck_assert_int_eq(0, get_last_serial_number(&actual_serial));
+ ck_assert_uint_eq(expected_serial, actual_serial);
+}
+
+static void
+check_base(serial_t expected_serial, bool const *expected_base)
+{
+ serial_t actual_serial;
+ bool actual_base[4];
+ array_index i;
+
+ memset(actual_base, 0, sizeof(actual_base));
+ ck_assert_int_eq(0, vrps_foreach_base_roa(vrp_check, actual_base,
+ &actual_serial));
+ ck_assert_uint_eq(expected_serial, actual_serial);
+ for (i = 0; i < ARRAY_LEN(actual_base); i++)
+ ck_assert_uint_eq(expected_base[i], actual_base[i]);
+}
+
+static void
+check_deltas(serial_t from, serial_t to, bool const *expected_deltas)
+{
+ serial_t actual_serial;
+ bool actual_deltas[8];
+ struct deltas_db deltas;
+ struct delta_group *group;
+ array_index i;
+
+ deltas_db_init(&deltas);
+ ck_assert_int_eq(0, vrps_get_deltas_from(from, &actual_serial,
+ &deltas));
+ ck_assert_uint_eq(to, actual_serial);
+
+ memset(actual_deltas, 0, sizeof(actual_deltas));
+ ARRAYLIST_FOREACH(&deltas, group)
+ ck_assert_int_eq(0, deltas_foreach(group->serial, group->deltas,
+ delta_check, actual_deltas));
+ for (i = 0; i < ARRAY_LEN(actual_deltas); i++)
+ ck_assert_uint_eq(expected_deltas[i], actual_deltas[i]);
+}
+
+START_TEST(test_basic)
+{
+ struct deltas_db deltas;
+ serial_t serial;
+ bool changed;
+ bool iterated_entries[8];
+
+ deltas_db_init(&deltas);
+
+ ck_assert_int_eq(0, vrps_init());
+
+ /* First validation not yet performed: Tell routers to wait */
+ ck_assert_int_eq(-EAGAIN, get_last_serial_number(&serial));
+ ck_assert_int_eq(-EAGAIN, vrps_foreach_base_roa(vrp_fail,
+ iterated_entries, &serial));
+ ck_assert_int_eq(-EAGAIN, vrps_get_deltas_from(0, &serial, &deltas));
+
+ /* First validation: One tree, no deltas */
+ ck_assert_int_eq(0, vrps_update(&changed));
+ check_serial(0);
+ check_base(0, iteration0_base);
+ check_deltas(0, 0, deltas_0to0);
+
+ /* Second validation: One tree, added deltas */
+ ck_assert_int_eq(0, vrps_update(&changed));
+ check_serial(1);
+ check_base(1, iteration1_base);
+ check_deltas(0, 1, deltas_0to1);
+ check_deltas(1, 1, deltas_1to1);
+
+ /* Third validation: One tree, removed deltas */
+ ck_assert_int_eq(0, vrps_update(&changed));
+ check_serial(2);
+ check_base(2, iteration2_base);
+ check_deltas(0, 2, deltas_0to2);
+ check_deltas(1, 2, deltas_1to2);
+ check_deltas(2, 2, deltas_2to2);
+
+ vrps_destroy();
+}
+END_TEST
+
+Suite *pdu_suite(void)
+{
+ Suite *suite;
+ TCase *core;
+
+ core = tcase_create("Core");
+ tcase_add_test(core, test_basic);
+
+ suite = suite_create("VRP Database");
+ suite_add_tcase(suite, core);
+ return suite;
+}
+
+int main(void)
+{
+ Suite *suite;
+ SRunner *runner;
+ int tests_failed;
+
+ suite = pdu_suite();
+
+ runner = srunner_create(suite);
+ srunner_run_all(runner, CK_NORMAL);
+ tests_failed = srunner_ntests_failed(runner);
+ srunner_free(runner);
+
+ return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
--- /dev/null
+#include <check.h>
+#include <stdlib.h>
+#include <sys/queue.h>
+
+#include "validation_handler.h"
+#include "rtr/pdu.h"
+#include "rtr/pdu_handler.h"
+#include "rtr/pdu_sender.h"
+#include "rtr/db/vrps.h"
+
+/* TODO (now) test the removal of deltas that cancel each other */
+/* TODO (now) test delta forgetting */
+
+/* Helper functions */
+
+struct expected_pdu {
+ uint8_t pdu_type;
+ STAILQ_ENTRY(expected_pdu) list_hook;
+};
+
+static STAILQ_HEAD(, expected_pdu) expected_pdus = STAILQ_HEAD_INITIALIZER(expected_pdus);
+
+static void
+expected_pdu_add(uint8_t pdu_type)
+{
+ struct expected_pdu *pdu;
+
+ pdu = malloc(sizeof(struct expected_pdu));
+ ck_assert_ptr_ne(NULL, pdu);
+
+ pdu->pdu_type = pdu_type;
+ STAILQ_INSERT_TAIL(&expected_pdus, pdu, list_hook);
+}
+
+static uint8_t
+pop_expected_pdu(void)
+{
+ struct expected_pdu *pdu;
+ uint8_t result;
+
+ pdu = STAILQ_FIRST(&expected_pdus);
+ ck_assert_ptr_ne(NULL, pdu);
+ result = pdu->pdu_type;
+ STAILQ_REMOVE(&expected_pdus, pdu, expected_pdu, list_hook);
+ free(pdu);
+
+ return result;
+}
+
+static bool
+has_expected_pdus(void)
+{
+ return !STAILQ_EMPTY(&expected_pdus);
+}
+
+static void
+init_db_full(void)
+{
+ bool changed;
+ ck_assert_int_eq(0, vrps_init());
+ ck_assert_int_eq(0, vrps_update(&changed));
+ ck_assert_uint_eq(true, changed);
+ ck_assert_int_eq(0, vrps_update(&changed));
+ ck_assert_uint_eq(true, changed);
+ ck_assert_int_eq(0, vrps_update(&changed));
+ ck_assert_uint_eq(true, changed);
+}
+
+static void
+init_reset_query(struct reset_query_pdu *query)
+{
+ query->header.protocol_version = RTR_V0;
+ query->header.pdu_type = PDU_TYPE_RESET_QUERY;
+ query->header.m.reserved = 0;
+ query->header.length = 8;
+}
+
+static void
+init_serial_query(struct serial_query_pdu *query, uint32_t serial)
+{
+ query->header.protocol_version = RTR_V0;
+ query->header.pdu_type = PDU_TYPE_SERIAL_QUERY;
+ query->header.m.session_id = get_current_session_id(RTR_V0);
+ query->header.length = 12;
+ query->serial_number = serial;
+}
+
+/* Impersonator functions */
+
+serial_t
+clients_get_min_serial(void)
+{
+ return 0;
+}
+
+void
+init_sender_common(struct sender_common *common, int fd, uint8_t version)
+{
+ common->fd = fd;
+ common->version = version;
+ common->session_id = get_current_session_id(version);
+}
+
+int
+send_cache_reset_pdu(struct sender_common *common)
+{
+ pr_info(" Server sent Cache Reset.");
+ ck_assert_int_eq(pop_expected_pdu(), PDU_TYPE_CACHE_RESET);
+ return 0;
+}
+
+int
+send_cache_response_pdu(struct sender_common *common)
+{
+ pr_info(" Server sent Cache Response.");
+ ck_assert_int_eq(pop_expected_pdu(), PDU_TYPE_CACHE_RESPONSE);
+ return 0;
+}
+
+int
+send_prefix_pdu(struct sender_common *common, struct vrp const *vrp,
+ uint8_t flags)
+{
+ /*
+ * We don't care about order.
+ * If the server is expected to return `M` IPv4 PDUs and `N` IPv6 PDUs,
+ * we'll just check `M + N` contiguous Prefix PDUs.
+ */
+ uint8_t pdu_type = pop_expected_pdu();
+ pr_info(" Server sent Prefix PDU.");
+ ck_assert_msg(pdu_type == PDU_TYPE_IPV4_PREFIX
+ || pdu_type == PDU_TYPE_IPV6_PREFIX,
+ "Server's PDU type is %d, not one of the IP Prefixes.", pdu_type);
+ return 0;
+}
+
+static int
+handle_delta(struct delta const *delta, void *arg)
+{
+ ck_assert_int_eq(0, send_prefix_pdu(arg, &delta->vrp, delta->flags));
+ return 0;
+}
+
+int
+send_pdus_delta(struct deltas_db *deltas, struct sender_common *common)
+{
+ struct delta_group *group;
+
+ ARRAYLIST_FOREACH(deltas, group)
+ ck_assert_int_eq(0, deltas_foreach(group->serial, group->deltas,
+ handle_delta, common));
+
+ return 0;
+}
+
+int
+send_end_of_data_pdu(struct sender_common *common, serial_t end_serial)
+{
+ pr_info(" Server sent End of Data.");
+ ck_assert_int_eq(pop_expected_pdu(), PDU_TYPE_END_OF_DATA);
+ return 0;
+}
+
+int
+send_error_report_pdu(int fd, uint8_t version, uint16_t code,
+struct pdu_header *err_pdu_header, char const *message)
+{
+ pr_info(" Server sent Error Report %u: '%s'", code, message);
+ ck_assert_int_eq(pop_expected_pdu(), PDU_TYPE_ERROR_REPORT);
+ return 0;
+}
+
+/* Tests */
+
+/* https://tools.ietf.org/html/rfc6810#section-6.1 */
+START_TEST(test_start_or_restart)
+{
+ struct reset_query_pdu client_pdu;
+
+ pr_info("-- Start or Restart --");
+
+ /* Prepare DB */
+ init_db_full();
+
+ /* Init client request */
+ init_reset_query(&client_pdu);
+
+ /* Define expected server response */
+ expected_pdu_add(PDU_TYPE_CACHE_RESPONSE);
+ expected_pdu_add(PDU_TYPE_IPV4_PREFIX);
+ expected_pdu_add(PDU_TYPE_IPV6_PREFIX);
+ expected_pdu_add(PDU_TYPE_END_OF_DATA);
+
+ /* Run and validate */
+ ck_assert_int_eq(0, handle_reset_query_pdu(0, &client_pdu));
+ ck_assert_uint_eq(false, has_expected_pdus());
+
+ /* Clean up */
+ vrps_destroy();
+}
+END_TEST
+
+/* https://tools.ietf.org/html/rfc6810#section-6.2 */
+START_TEST(test_typical_exchange)
+{
+ struct serial_query_pdu client_pdu;
+
+ pr_info("-- Typical Exchange --");
+
+ /* Prepare DB */
+ init_db_full();
+
+ /* From serial 0: Init client request */
+ init_serial_query(&client_pdu, 0);
+
+ /* From serial 0: Define expected server response */
+ expected_pdu_add(PDU_TYPE_CACHE_RESPONSE);
+ expected_pdu_add(PDU_TYPE_IPV4_PREFIX);
+ expected_pdu_add(PDU_TYPE_IPV6_PREFIX);
+ expected_pdu_add(PDU_TYPE_IPV4_PREFIX);
+ expected_pdu_add(PDU_TYPE_IPV6_PREFIX);
+ expected_pdu_add(PDU_TYPE_END_OF_DATA);
+
+ /* From serial 0: Run and validate */
+ ck_assert_int_eq(0, handle_serial_query_pdu(0, &client_pdu));
+ ck_assert_uint_eq(false, has_expected_pdus());
+
+ /* From serial 1: Init client request */
+ init_serial_query(&client_pdu, 1);
+
+ /* From serial 1: Define expected server response */
+ expected_pdu_add(PDU_TYPE_CACHE_RESPONSE);
+ expected_pdu_add(PDU_TYPE_IPV4_PREFIX);
+ expected_pdu_add(PDU_TYPE_IPV6_PREFIX);
+ expected_pdu_add(PDU_TYPE_END_OF_DATA);
+
+ /* From serial 1: Run and validate */
+ ck_assert_int_eq(0, handle_serial_query_pdu(0, &client_pdu));
+ ck_assert_uint_eq(false, has_expected_pdus());
+
+ /* From serial 2: Init client request */
+ init_serial_query(&client_pdu, 2);
+
+ /* From serial 2: Define expected server response */
+ expected_pdu_add(PDU_TYPE_CACHE_RESPONSE);
+ expected_pdu_add(PDU_TYPE_END_OF_DATA);
+
+ /* From serial 2: Run and validate */
+ ck_assert_int_eq(0, handle_serial_query_pdu(0, &client_pdu));
+ ck_assert_uint_eq(false, has_expected_pdus());
+
+ /* Clean up */
+ vrps_destroy();
+}
+END_TEST
+
+/* https://tools.ietf.org/html/rfc6810#section-6.3 */
+START_TEST(test_no_incremental_update_available)
+{
+ struct serial_query_pdu serial_query;
+
+ pr_info("-- No Incremental Update Available --");
+
+ /* Prepare DB */
+ init_db_full();
+
+ /* Init client request */
+ init_serial_query(&serial_query, 10000);
+
+ /* Define expected server response */
+ expected_pdu_add(PDU_TYPE_CACHE_RESET);
+
+ /* Run and validate */
+ ck_assert_int_eq(0, handle_serial_query_pdu(0, &serial_query));
+ ck_assert_uint_eq(false, has_expected_pdus());
+
+ /* The Reset Query is already tested in start_or_restart. */
+
+ /* Clean up */
+ vrps_destroy();
+}
+END_TEST
+
+/* https://tools.ietf.org/html/rfc6810#section-6.4 */
+START_TEST(test_cache_has_no_data_available)
+{
+ struct serial_query_pdu serial_query;
+ struct reset_query_pdu reset_query;
+
+ pr_info("-- Cache Has No Data Available --");
+
+ /* Prepare DB */
+ ck_assert_int_eq(0, vrps_init());
+
+ /* Serial Query: Init client request */
+ init_serial_query(&serial_query, 0);
+
+ /* Serial Query: Define expected server response */
+ expected_pdu_add(PDU_TYPE_ERROR_REPORT);
+
+ /* Serial Query: Run and validate */
+ ck_assert_int_eq(0, handle_serial_query_pdu(0, &serial_query));
+ ck_assert_uint_eq(false, has_expected_pdus());
+
+ /* Reset Query: Init client request */
+ init_reset_query(&reset_query);
+
+ /* Reset Query: Define expected server response */
+ expected_pdu_add(PDU_TYPE_ERROR_REPORT);
+
+ /* Reset Query: Run and validate */
+ ck_assert_int_eq(0, handle_reset_query_pdu(0, &reset_query));
+ ck_assert_uint_eq(false, has_expected_pdus());
+
+ /* Clean up */
+ vrps_destroy();
+}
+END_TEST
+
+Suite *pdu_suite(void)
+{
+ Suite *suite;
+ TCase *core;
+
+ core = tcase_create("RFC6810-Defined Protocol Sequences");
+ tcase_add_test(core, test_start_or_restart);
+ tcase_add_test(core, test_typical_exchange);
+ tcase_add_test(core, test_no_incremental_update_available);
+ tcase_add_test(core, test_cache_has_no_data_available);
+
+ /* TODO (now) add an unhappy path TCase. */
+
+ suite = suite_create("PDU Handler");
+ suite_add_tcase(suite, core);
+ return suite;
+}
+
+int main(void)
+{
+ Suite *suite;
+ SRunner *runner;
+ int tests_failed;
+
+ suite = pdu_suite();
+
+ runner = srunner_create(suite);
+ srunner_run_all(runner, CK_NORMAL);
+ tests_failed = srunner_ntests_failed(runner);
+ srunner_free(runner);
+
+ return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}